@@ -2606,10 +2594,10 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.prefName}
getOptionValue={(x) => x.prefId}
isSearchable={false}
+ onChange={onChangePrefCode}
value={prefCodeList.filter(function (option) {
return option.prefId === prefValue
})}
- isDisabled={true}
/>
)}
diff --git a/src/hooks/common/useCanvasMenu.js b/src/hooks/common/useCanvasMenu.js
index f90881d3..1aaaee23 100644
--- a/src/hooks/common/useCanvasMenu.js
+++ b/src/hooks/common/useCanvasMenu.js
@@ -7,8 +7,6 @@ import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => {
const [selectedMenu, setSelectedMenu] = useRecoilState(selectedMenuState)
- const canvas = useRecoilValue(canvasState)
- const { drawDirectionArrow } = usePolygon()
return {
selectedMenu,
diff --git a/src/hooks/common/useCanvasPopupStatusController.js b/src/hooks/common/useCanvasPopupStatusController.js
index a31bd0ed..4a6ed6dd 100644
--- a/src/hooks/common/useCanvasPopupStatusController.js
+++ b/src/hooks/common/useCanvasPopupStatusController.js
@@ -1,6 +1,6 @@
'use client'
-import { useRecoilState, useRecoilValue } from 'recoil'
+import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import useSWRMutation from 'swr/mutation'
import { useAxios } from '../useAxios'
import { unescapeString } from '@/util/common-utils'
@@ -10,6 +10,8 @@ import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useCircuitTrestle } from '../useCirCuitTrestle'
import { useEffect } from 'react'
+import { addedRoofsState } from '@/store/settingAtom'
+import { roofsState } from '@/store/roofAtom'
/**
* 캔버스 팝업 상태 관리
@@ -19,13 +21,14 @@ import { useEffect } from 'react'
export function useCanvasPopupStatusController(param = 1) {
const popupType = parseInt(param)
- const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
- const [moduleSelectionDataStore, setModuleSelectionDataStore] = useRecoilState(moduleSelectionDataState)
- const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
+ const setCompasDeg = useSetRecoilState(compasDegAtom)
+ const setModuleSelectionDataStore = useSetRecoilState(moduleSelectionDataState)
+ const setSelectedModules = useSetRecoilState(selectedModuleState)
const { get, promiseGet, getFetcher, postFetcher } = useAxios()
const canvas = useRecoilValue(canvasState)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
-
+ const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
+ const [roofs, setRoofs] = useRecoilState(roofsState)
/**
* 팝업 상태 조회
* @param {number} popupTypeParam
@@ -53,19 +56,27 @@ export function useCanvasPopupStatusController(param = 1) {
const handleModuleSelectionTotal = async () => {
for (let i = 1; i < 3; i++) {
const result = await getModuleSelection(i)
- console.log('🚀 ~ handleModuleSelectionTotal ~ result:', result)
if (!result.objectNo) return
if (i === 1) {
- setCompasDeg(result.popupStatus)
+ if (result.popupStatus && unescapeString(result.popupStatus)) {
+ const data = JSON.parse(unescapeString(result.popupStatus))
+
+ if (data?.compasDeg) setCompasDeg(data.compasDeg)
+ if (data?.module) setSelectedModules(data.module)
+ setModuleSelectionDataStore(data)
+ }
} else if (i === 2) {
const data = JSON.parse(unescapeString(result.popupStatus))
- setModuleSelectionDataStore(data)
const roofSurfaceList = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
roofSurfaceList.forEach((surface) => {
surface.modules = modules.filter((module) => module.surfaceId === surface.id)
})
- if (data.module) setSelectedModules(data.module)
+ if (data.roofConstruction) {
+ setRoofs(data.roofConstruction)
+ // setManagementState({ ...managementState, roofs: data.roofConstruction.map((roof) => roof.construction.managementState) })
+ }
+ // if (data?.module) setManagementState(data.common.managementState)
}
}
}
@@ -80,7 +91,8 @@ export function useCanvasPopupStatusController(param = 1) {
objectNo: currentCanvasPlan.objectNo,
planNo: parseInt(currentCanvasPlan.planNo),
popupType: popupType.toString(),
- popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
+ // popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
+ popupStatus: JSON.stringify(arg).replace(/"/g, '\"'),
}
postFetcher(`/api/v1/canvas-popup-status`, params)
},
diff --git a/src/hooks/common/useMenu.js b/src/hooks/common/useMenu.js
index 7fdd6a31..f8bc2a31 100644
--- a/src/hooks/common/useMenu.js
+++ b/src/hooks/common/useMenu.js
@@ -16,12 +16,13 @@ import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
-import { useRecoilValue } from 'recoil'
+import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { MENU } from '@/common/common'
import { useTrestle } from '@/hooks/module/useTrestle'
import { usePolygon } from '@/hooks/usePolygon'
import { useOrientation } from '@/hooks/module/useOrientation'
+import { corridorDimensionSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
/**
* 메뉴 처리 훅
@@ -36,8 +37,11 @@ export default function useMenu() {
const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch({})
const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle()
const { nextStep } = useOrientation()
+ const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
const handleMenu = (type) => {
if (type === 'outline') {
+ // 지붕 덮개 메뉴의 경우는 복도치수로 적용한다.
+ setCorridorDimension(0)
switch (currentMenu) {
case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE:
addPopup(popupId, 1, )
@@ -67,6 +71,8 @@ export default function useMenu() {
}
if (type === 'surface') {
+ // 배치면 메뉴의 경우는 실치수로 적용한다.
+ setCorridorDimension(1)
switch (currentMenu) {
// case MENU.BATCH_CANVAS.SLOPE_SETTING:
// addPopup(popupId, 1, )
@@ -87,6 +93,8 @@ export default function useMenu() {
}
if (type === 'module') {
+ // 모듈,회로 구성 메뉴의 경우는 실치수로 적용한다.
+ setCorridorDimension(1)
switch (currentMenu) {
case MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING:
trestleClear()
diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js
index d509a1e0..a0765766 100644
--- a/src/hooks/floorPlan/estimate/useEstimateController.js
+++ b/src/hooks/floorPlan/estimate/useEstimateController.js
@@ -130,13 +130,14 @@ export const useEstimateController = (planNo, flag) => {
addFlg: true,
paDispOrder: null,
dispCableFlg: '0',
+ itemTpCd: '',
},
],
})
}
useEffect(() => {
- setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
+ setEstimateData({ ...estimateContextState, userId: session.userId })
}, [estimateContextState])
// 첨부파일 다운로드
@@ -321,6 +322,7 @@ export const useEstimateController = (planNo, flag) => {
}
})
if (delCnt === estimateData.itemList.length) {
+ itemFlg = false
setIsGlobalLoading(false)
return swalFire({ text: getMessage('estimate.detail.save.requiredItem'), type: 'alert', icon: 'warning' })
}
@@ -452,8 +454,6 @@ export const useEstimateController = (planNo, flag) => {
}
const params = {
- saleStoreId: session.storeId,
- sapSalesStoreCd: session.custCd,
objectNo: estimateData.objectNo,
planNo: sendPlanNo,
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
@@ -462,24 +462,30 @@ export const useEstimateController = (planNo, flag) => {
}
setIsGlobalLoading(true)
- await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
- setIsGlobalLoading(false)
- if (res.status === 201) {
- if (isObjectNotEmpty(res.data)) {
- let newObjectNo = res.data.objectNo
- swalFire({
- text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'),
- type: 'alert',
- confirmFn: () => {
- setEstimateCopyPopupOpen(false) //팝업닫고
- router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
- },
- })
- }
- } else {
+ await promisePost({ url: '/api/estimate/save-estimate-copy', data: params })
+ .then((res) => {
setIsGlobalLoading(false)
- }
- })
+ if (res.status === 201) {
+ if (isObjectNotEmpty(res.data)) {
+ let newObjectNo = res.data.objectNo
+ swalFire({
+ text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'),
+ type: 'alert',
+ confirmFn: () => {
+ setEstimateCopyPopupOpen(false) //팝업닫고
+ router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
+ },
+ })
+ }
+ } else {
+ setIsGlobalLoading(false)
+ }
+ })
+ .catch((e) => {
+ console.error('캔버스 복사 중 오류 발생')
+ swalFire({ text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessageError'), type: 'alert', icon: 'error' })
+ setIsGlobalLoading(false)
+ })
}
const checkLength = (value) => {
diff --git a/src/hooks/module/useModule.js b/src/hooks/module/useModule.js
index 51cffd65..4397ebb1 100644
--- a/src/hooks/module/useModule.js
+++ b/src/hooks/module/useModule.js
@@ -54,7 +54,7 @@ export function useModule() {
})
return
}
- canvas.discardActiveObject() //선택해제
+ // canvas.discardActiveObject() //선택해제
const isSetupModules = getOtherModules(selectedObj)
const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴
@@ -991,14 +991,14 @@ export function useModule() {
const getRowModules = (target) => {
return canvas
.getObjects()
- .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top)
+ .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.top - target.top) < 1)
.sort((a, b) => a.left - b.left)
}
const getColumnModules = (target) => {
return canvas
.getObjects()
- .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left)
+ .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.left - target.left) < 1)
.sort((a, b) => a.top - b.top)
}
diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js
index 5db1963e..1871e34a 100644
--- a/src/hooks/module/useModuleBasicSetting.js
+++ b/src/hooks/module/useModuleBasicSetting.js
@@ -1,11 +1,23 @@
import { useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
-import { canvasSettingState, canvasState, checkedModuleState, currentObjectState, isManualModuleSetupState } from '@/store/canvasAtom'
+
+import {
+ canvasSettingState,
+ canvasState,
+ checkedModuleState,
+ currentObjectState,
+ isManualModuleLayoutSetupState,
+ isManualModuleSetupState,
+ moduleSetupOptionState,
+ toggleManualSetupModeState,
+ moduleRowColArrayState,
+} from '@/store/canvasAtom'
+
import { rectToPolygon, polygonToTurfPolygon, calculateVisibleModuleHeight, getDegreeByChon, toFixedWithoutRounding } from '@/util/canvas-util'
-import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
+import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
-import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
+import { moduleSetupSurfaceState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE, LINE_TYPE } from '@/common/common'
import * as turf from '@turf/turf'
@@ -15,13 +27,13 @@ import { QLine } from '@/components/fabric/QLine'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
-import { moduleStatisticsState } from '@/store/circuitTrestleAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useMasterController } from '@/hooks/common/useMasterController'
import { v4 as uuidv4 } from 'uuid'
import { isObjectNotEmpty } from '@/util/common-utils'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
import { useMode } from '@/hooks/useMode'
+import { usePolygon } from '@/hooks/usePolygon'
export function useModuleBasicSetting(tabNum) {
const canvas = useRecoilValue(canvasState)
@@ -30,15 +42,12 @@ export function useModuleBasicSetting(tabNum) {
const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
const { addCanvasMouseEventListener, initEvent, removeMouseEvent, addTargetMouseEventListener } = useEvent()
const { swalFire } = useSwal()
-
const compasDeg = useRecoilValue(compasDegAtom)
const { setSurfaceShapePattern } = useRoofFn()
const checkedModule = useRecoilValue(checkedModuleState)
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
- const setModuleStatistics = useSetRecoilState(moduleStatisticsState)
-
+ const [isManualModuleLayoutSetup, setIsManualModuleLayoutSetup] = useRecoilState(isManualModuleLayoutSetupState)
const canvasSetting = useRecoilValue(canvasSettingState)
-
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const [trestleDetailParams, setTrestleDetailParams] = useState([])
const [trestleDetailList, setTrestleDetailList] = useState([])
@@ -46,29 +55,32 @@ export function useModuleBasicSetting(tabNum) {
const { getTrestleDetailList } = useMasterController()
const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false)
- const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
+ const setCurrentObject = useSetRecoilState(currentObjectState)
const { setModuleStatisticsData } = useCircuitTrestle()
const { createRoofPolygon, createMarginPolygon, createPaddingPolygon } = useMode()
+ const { drawDirectionArrow } = usePolygon()
+ const moduleSetupOption = useRecoilValue(moduleSetupOptionState)
+ const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
+ const [moduleRowColArray, setModuleRowColArray] = useRecoilState(moduleRowColArrayState)
+
useEffect(() => {
- // console.log('basicSetting', basicSetting)
-
- if (canvas) {
- //드래그 여부
- // canvas.selection = true
- // canvas.selectionFullyContained = true
- }
-
return () => {
//수동 설치시 초기화
removeMouseEvent('mouse:up')
removeMouseEvent('mouse:move')
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
+ canvas?.getObjects().forEach((obj) => {
+ if (obj.name === POLYGON_TYPE.ROOF) {
+ obj.set({
+ stroke: 'black',
+ strokeWidth: 3,
+ })
+ }
+ })
}
}, [])
- // const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext)
-
//모듈 선택에서 선택된 값들 넘어옴
const makeModuleInitArea = () => {
if (isObjectNotEmpty(moduleSelectionData) && tabNum === 3) {
@@ -77,23 +89,10 @@ export function useModuleBasicSetting(tabNum) {
const roofConstructions = moduleSelectionData.roofConstructions
if (roofConstructions && roofConstructions.length > 0) {
- const listParams = roofConstructions.map((item) => {
- return {
- ...common,
- roofMatlCd: item.trestle.roofMatlCd,
- trestleMkrCd: item.trestle.trestleMkrCd,
- constMthdCd: item.trestle.constMthdCd,
- roofBaseCd: item.trestle.roofBaseCd,
- constTp: item.construction.constTp,
- mixMatlNo: selectedModules.mixMatlNo,
- roofPitch: item.addRoof.hajebichi ? item.addRoof.hajebichi : 0,
- inclCd: String(item.addRoof.pitch),
- roofIndex: item.addRoof.index,
- workingWidth: item.addRoof.lenBase,
- raftBaseCd: item.trestle.raftBaseCd,
- }
- })
- setTrestleDetailParams(listParams)
+ //roofIndex 넣기
+ const roofConstructionArray = roofConstructions.map((detail) => ({ ...detail.trestleDetail, roofIndex: detail.roofIndex }))
+
+ setTrestleDetailList(roofConstructionArray)
//북면 설치 가능 판매점
if (moduleSelectionData.common.saleStoreNorthFlg === '1') {
@@ -104,7 +103,7 @@ export function useModuleBasicSetting(tabNum) {
//육지붕 일경우에는 바로 배치면 설치LL
canvas
.getObjects()
- .filter((roof) => roof.name === 'roof')
+ .filter((roof) => roof.name === POLYGON_TYPE.ROOF)
.forEach((roof) => {
makeModuleInstArea(roof, null)
})
@@ -112,62 +111,74 @@ export function useModuleBasicSetting(tabNum) {
}
}
- //가대 상세 데이터 조회
- const getTrestleDetailListData = async () => {
- const trestleDetailList = await getTrestleDetailList(trestleDetailParams)
- if (trestleDetailList.length > 0) {
- setTrestleDetailList(trestleDetailList)
- }
- }
-
- //가대 상세 데이터 파라메터 담기면 실행
- useEffect(() => {
- if (trestleDetailParams.length > 0) {
- getTrestleDetailListData(trestleDetailParams)
- }
- }, [trestleDetailParams])
-
//가대 상세 데이터 들어오면 실행
useEffect(() => {
if (trestleDetailList.length > 0) {
- console.log('trestleDetailList', trestleDetailList)
+ let rowColArray = []
//지붕을 가져옴
canvas
.getObjects()
- .filter((roof) => roof.name === 'roof')
+ .filter((roof) => roof.name === POLYGON_TYPE.ROOF)
.forEach((roof) => {
if (!roof.roofMaterial) return
const roofIndex = roof.roofMaterial.index //지붕의 지붕재의 순번
+
trestleDetailList.forEach((detail) => {
- if (detail.data !== null) {
- if (Number(detail.data.roofIndex) === roofIndex) {
+ if (isObjectNotEmpty(detail)) {
+ if (Number(detail.roofIndex) === roofIndex) {
//roof에 상세 데이터 추가
- roof.set({ trestleDetail: detail.data })
+ roof.set({
+ trestleDetail: detail,
+ stroke: roofOutlineColor(roofIndex),
+ strokeWidth: 7,
+ })
+
+ const offsetObjects = moduleSelectionData.roofConstructions.find((item) => item.addRoof.index === roofIndex)
+
roof.lines.forEach((line) => {
- line.attributes = {
- ...line.attributes,
- offset: getOffset(detail.data, line.attributes.type),
- }
+ line.attributes = { ...line.attributes, offset: getOffset(offsetObjects.addRoof, line.attributes.type) }
})
//배치면 설치 영역
- makeModuleInstArea(roof, detail.data)
- //surface에 상세 데이터 추가
+ makeModuleInstArea(roof, detail)
}
}
})
})
+
+ trestleDetailList.forEach((detail) => {
+ const moduleRowArray = []
+ if (isObjectNotEmpty(detail) && detail.module.length > 0) {
+ detail.module.forEach((module) => {
+ moduleRowArray.push({ moduleMaxRows: module.moduleMaxRows, mixModuleMaxRows: module.mixModuleMaxRows })
+ })
+ }
+ rowColArray.push(moduleRowArray)
+ })
+ setModuleRowColArray(rowColArray)
}
}, [trestleDetailList])
+ const roofOutlineColor = (roofIndex) => {
+ if (roofIndex === 1) {
+ return '#FFC000'
+ } else if (roofIndex === 2) {
+ return '#7030A0'
+ } else if (roofIndex === 3) {
+ return '#385723'
+ } else {
+ return '#FFFF00'
+ }
+ }
+
const getOffset = (data, type) => {
switch (type) {
case LINE_TYPE.WALLLINE.EAVES:
- return data.eaveIntvl / 10
+ return data.eavesMargin / 10
case LINE_TYPE.WALLLINE.GABLE:
- return data.kerabaIntvl / 10
+ return data.kerabaMargin / 10
case LINE_TYPE.SUBLINE.RIDGE:
case LINE_TYPE.WALLLINE.SHED:
- return data.ridgeIntvl / 10
+ return data.ridgeMargin / 10
default:
return 200 / 10
}
@@ -187,13 +198,7 @@ export function useModuleBasicSetting(tabNum) {
//도머등 오브젝트 객체가 있으면 아웃라인 낸다
const batchObjects = canvas
?.getObjects()
- .filter(
- (obj) =>
- obj.name === BATCH_TYPE.OPENING ||
- obj.name === BATCH_TYPE.SHADOW ||
- obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
- obj.name === BATCH_TYPE.PENTAGON_DORMER,
- ) //도머s 객체
+ .filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) //그림자 제외 나머지 오브젝트들
//도머도 외곽을 따야한다
const batchObjectOptions = {
@@ -240,7 +245,6 @@ export function useModuleBasicSetting(tabNum) {
setSurfaceShapePattern(roof, roofDisplay.column, true, roof.roofMaterial) //패턴 변경
// let offsetPoints = createPaddingPolygon(createRoofPolygon(roof.points), roof.lines).vertices //안쪽 offset
let offsetPoints = null
- console.log(roof, roof.getCurrentPoints())
const polygon = createRoofPolygon(roof.getCurrentPoints())
const originPolygon = new QPolygon(roof.getCurrentPoints(), { fontSize: 0 })
@@ -250,8 +254,10 @@ export function useModuleBasicSetting(tabNum) {
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
if (canvasSetting.roofSizeSet == '3') {
+ const margin = moduleSelectionData.common.margin ? moduleSelectionData.common.margin : 200
+
//육지붕일때는 그냥 하드코딩
- offsetPoints = offsetPolygon(roof.points, -30) //육지붕일때
+ offsetPoints = offsetPolygon(roof.points, -Number(margin) / 10) //육지붕일때
} else {
//육지붕이 아닐때
if (allPointsOutside) {
@@ -353,34 +359,24 @@ export function useModuleBasicSetting(tabNum) {
}
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
- setupSurface.set({
- ...setupSurface,
- strokeWidth: 3,
- strokeDashArray: [0],
- fill: 'rgba(255,255,255,0.1)',
- })
+ setupSurface.set({ ...setupSurface, strokeWidth: 3, strokeDashArray: [0], fill: 'rgba(255,255,255,0.1)' })
canvas.discardActiveObject() // 객체의 활성 상태 해제
//중복으로 들어가는걸 방지하기 위한 코드
canvas?.renderAll()
selectedModuleInstSurfaceArray.push(setupSurface)
- setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] })
+ setCurrentObject({ name: POLYGON_TYPE.MODULE_SETUP_SURFACE, arrayData: [...selectedModuleInstSurfaceArray] })
} else {
//선택후 재선택하면 선택안됨으로 변경
- setupSurface.set({
- ...setupSurface,
- fill: 'rgba(255,255,255,0.1)',
- strokeDashArray: [10, 4],
- strokeWidth: 1,
- })
+ setupSurface.set({ ...setupSurface, fill: 'rgba(255,255,255,0.1)', strokeDashArray: [10, 4], strokeWidth: 1 })
canvas.discardActiveObject() // 객체의 활성 상태 해제
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
const removeIndex = setupSurface.parentId
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
- setCurrentObject({ name: 'moduleSetupSurface', arrayData: [...selectedModuleInstSurfaceArray] })
+ setCurrentObject({ name: POLYGON_TYPE.MODULE_SETUP_SURFACE, arrayData: [...selectedModuleInstSurfaceArray] })
}
canvas?.renderAll()
@@ -412,42 +408,46 @@ export function useModuleBasicSetting(tabNum) {
}
useEffect(() => {
- if (canvasSetting.roofSizeSet !== '3') {
- if (isObjectNotEmpty(moduleSelectionData) && moduleSelectionData.common.saleStoreNorthFlg === '1') {
+ if (canvasSetting.roofSizeSet != '3') {
+ if (isObjectNotEmpty(moduleSelectionData) && moduleSelectionData?.common?.saleStoreNorthFlg === '1') {
setSaleStoreNorthFlg(true)
}
}
- }, [isManualModuleSetup])
+ }, [isManualModuleSetup, isManualModuleLayoutSetup])
/**
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
* 확인 후 셀을 이동시킴
*/
- const manualModuleSetup = (placementRef) => {
- console.log('isManualModuleSetup', isManualModuleSetup)
-
+ const manualModuleSetup = () => {
+ //레이아웃 수동설치 토글
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
+ const isChidori = moduleSetupOption.isChidori
if (isManualModuleSetup) {
+ if (isManualModuleLayoutSetup) {
+ setIsManualModuleLayoutSetup(false)
+ // setManualSetupMode(`manualLayoutSetup_false`)
+ }
+
if (checkedModule.length === 0) {
swalFire({ text: getMessage('module.place.select.module') })
- setIsManualModuleSetup(!isManualModuleSetup)
+ setIsManualModuleSetup(false)
+ setManualSetupMode(`manualSetup_false`)
return
}
if (checkedModule.length > 1) {
swalFire({ text: getMessage('module.place.select.one.module') })
- setIsManualModuleSetup(!isManualModuleSetup)
+ setIsManualModuleSetup(false)
+ setManualSetupMode(`manualSetup_false`)
return
}
const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체
//수동모드 모듈 설치면 선택 잠금
moduleSetupSurfaces.forEach((obj) => {
- obj.set({
- selectable: false,
- evented: false,
- })
+ obj.set({ selectable: false, evented: false })
})
//모듈 기본 옵션
@@ -548,7 +548,9 @@ export function useModuleBasicSetting(tabNum) {
/**
* 스냅기능
*/
- let snapDistance = flowDirection === 'south' || flowDirection === 'north' ? 70 : 40
+
+ let snapDistance = flowDirection === 'south' || flowDirection === 'north' ? 50 : 50
+ let sideSnapDistance = 15
let trestleSnapDistance = 15
let intvHor =
@@ -588,51 +590,98 @@ export function useModuleBasicSetting(tabNum) {
const holdCellCenterX = holdCellLeft + toFixedWithoutRounding(cell.width / 2, 2)
const holdCellCenterY = holdCellTop + toFixedWithoutRounding(cell.height / 2, 2)
+ //흐름방향따라 달라야 한다.
+ if (flowDirection === 'south' || flowDirection === 'north') {
+ if (Math.abs(smallCenterX - holdCellCenterX) < snapDistance) {
+ //움직이는 모듈 가운데 -> 설치 모듈 가운데
+ tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
+ }
+
+ if (isChidori) {
+ //움직이는 모듈왼쪽 -> 가운데
+ if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) {
+ tempModule.left = holdCellCenterX + intvHor / 2
+ }
+ // 오른쪽 -> 가운데
+ if (Math.abs(smallRight - holdCellCenterX) < snapDistance) {
+ tempModule.left = holdCellCenterX - width - intvHor / 2
+ }
+ }
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+ } else {
+ //흐름방향 west, east
+
+ //가운데 가운데
+ if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - toFixedWithoutRounding(width / 2, 2)
+ }
+
+ if (isChidori) {
+ //위쪽 -> 가운데
+ if (Math.abs(smallTop - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY + intvHor / 2
+ }
+ // 밑 -> 가운데
+ if (Math.abs(smallBottom - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - height - intvHor / 2
+ }
+ }
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+
+ if (Math.abs(smallLeft - holdCellLeft) < snapDistance) {
+ tempModule.left = holdCellLeft
+ }
+ }
+
//가운데 -> 가운대
- if (Math.abs(smallCenterX - holdCellCenterX) < snapDistance) {
- tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
- }
-
- //왼쪽 -> 가운데
- if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) {
- // console.log('holdCellCenterX', holdCellCenterX)
- // console.log('smallLeft', smallLeft)
-
- // console.log('모듈 센터에 스냅')
- tempModule.left = holdCellCenterX + intvHor / 2
-
- // console.log('tempModule.left', tempModule.left)
- }
- // 오른쪽 -> 가운데
- if (Math.abs(smallRight - holdCellCenterX) < snapDistance) {
- tempModule.left = holdCellCenterX - width - intvHor / 2
- }
-
- //설치된 셀에 좌측에 스냅
- if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
- // console.log('모듈 좌측 스냅')
- tempModule.left = holdCellLeft - width - intvHor
- }
-
- //설치된 셀에 우측에 스냅
- if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
- // console.log('모듈 우측 스냅')
- tempModule.left = holdCellRight + intvHor
- }
- //설치된 셀에 위쪽에 스냅
- if (Math.abs(smallBottom - holdCellTop) < 10) {
- tempModule.top = holdCellTop - height - intvVer
- }
-
- //설치된 셀에 밑쪽에 스냅
- if (Math.abs(smallTop - holdCellBottom) < 10) {
- tempModule.top = holdCellBottom + intvVer
- }
-
- //설치된 셀에 윗쪽에 스냅
- if (Math.abs(smallTop - holdCellTop) < 10) {
- tempModule.top = holdCellTop
- }
//세로 가운데 -> 가운데
// if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
@@ -641,14 +690,10 @@ export function useModuleBasicSetting(tabNum) {
// //위쪽 -> 가운데
// if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
// tempModule.top = holdCellCenterY
- // }
- // //아랫쪽 -> 가운데
- // if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
- // tempModule.top = holdCellCenterY - height
- // }
})
}
+ //여기서부턴 배치면 설치 외곽선 라인
// 위쪽 변에 스냅
if (Math.abs(smallTop - trestleTop) < trestleSnapDistance) {
tempModule.top = trestleTop + 1
@@ -673,22 +718,19 @@ export function useModuleBasicSetting(tabNum) {
// 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)) < trestleSnapDistance) {
- tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width / 2
- }
-
+ // if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < trestleSnapDistance) {
+ // tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width / 2
+ // }
// 모듈오른쪽이 세로중앙선에 붙게 스냅
// if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < trestleSnapDistance) {
// tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
// }
} else {
// 모듈이 가로중앙선에 스냅
- if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < trestleSnapDistance) {
- tempModule.top = bigCenterY - tempModule.height / 2
- }
-
+ // if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < trestleSnapDistance) {
+ // tempModule.top = bigCenterY - tempModule.height / 2
+ // }
// if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < trestleSnapDistance) {
// tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
// }
@@ -716,6 +758,18 @@ export function useModuleBasicSetting(tabNum) {
addCanvasMouseEventListener('mouse:up', (e) => {
let isIntersection = true
+
+ // 혼합 설치 불가능한 모듈인지 확인 Y는 Y끼리 N은 N끼리만 설치 가능
+ if (trestlePolygon.modules.length > 0) {
+ //이미 설치된 모듈중에 한개만 가져옴
+ const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
+ //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
+ if (checkedModule[0].mixAsgYn !== mixAsgYn) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+ }
+
if (!inside) return
if (tempModule) {
const rectPoints = [
@@ -778,6 +832,13 @@ export function useModuleBasicSetting(tabNum) {
canvas?.add(manualModule)
manualDrawModules.push(manualModule)
setModuleStatisticsData()
+
+ //그림자는 무조건 가장 앞으로
+ const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
+ if (shadowObj) {
+ shadowObj.bringToFront()
+ }
+
// getModuleStatistics()
} else {
swalFire({ text: getMessage('module.place.overlab') })
@@ -788,6 +849,871 @@ export function useModuleBasicSetting(tabNum) {
}
})
}
+ } else {
+ if (moduleSetupSurfaces) {
+ //수동모드 해제시 모듈 설치면 선택 잠금
+ moduleSetupSurfaces.forEach((obj) => {
+ obj.set({ selectable: true, evented: true })
+ })
+ }
+
+ removeMouseEvent('mouse:up')
+ removeMouseEvent('mouse:move')
+ canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
+ }
+ }
+
+ const manualModuleLayoutSetup = (layoutSetupRef) => {
+ const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
+
+ const isChidori = moduleSetupOption.isChidori //치도리 여부
+ const setupLocation = moduleSetupOption.setupLocation //모듈 설치 위치 처마, 용마루
+
+ if (isManualModuleLayoutSetup) {
+ if (isManualModuleSetup) {
+ setIsManualModuleSetup(false)
+ // setManualSetupMode(`manualSetup_false`)
+ }
+
+ if (checkedModule.length === 0) {
+ swalFire({ text: getMessage('module.place.select.module') })
+ setIsManualModuleLayoutSetup(false)
+ setManualSetupMode(`manualLayoutSetup_false`)
+ return
+ }
+
+ //숫자 0이 하나라도 있으면 설치 불가
+ const hasZeroLength = checkedModule.some((item) =>
+ layoutSetupRef.some(
+ (layoutItem) => layoutItem.checked && item.itemId === layoutItem.moduleId && (layoutItem.row === 0 || layoutItem.col === 0),
+ ),
+ )
+
+ if (hasZeroLength) {
+ swalFire({ text: getMessage('module.layout.setup.has.zero.value') })
+ setIsManualModuleLayoutSetup(false)
+ setManualSetupMode(`manualLayoutSetup_false`)
+ return
+ }
+
+ //혼합 가능 모듈과 혼합 불가능 모듈을 선택했을때 카운트를 해서 확인
+ const mixAsgY = checkedModule.filter((obj) => obj.mixAsgYn === 'Y')
+ const mixAsgN = checkedModule.filter((obj) => obj.mixAsgYn === 'N')
+
+ //Y인 모듈과 N인 모듈이 둘다 존재하면 설치 불가
+ if (mixAsgY.length > 0 && mixAsgN.length > 0) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+
+ const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체
+ //수동모드 모듈 설치면 선택 잠금
+ moduleSetupSurfaces.forEach((obj) => {
+ obj.set({
+ selectable: false,
+ evented: false,
+ })
+ })
+
+ //모듈 기본 옵션
+ const moduleOptions = {
+ fill: checkedModule[0].color,
+ stroke: 'black',
+ strokeWidth: 0.3,
+ selectable: true, // 선택 가능하게 설정
+ lockMovementX: true, // X 축 이동 잠금
+ lockMovementY: true, // Y 축 이동 잠금
+ lockRotation: true, // 회전 잠금
+ lockScalingX: true, // X 축 크기 조정 잠금
+ lockScalingY: true, // Y 축 크기 조정 잠금
+ name: POLYGON_TYPE.MODULE,
+ }
+
+ const objectsIncludeSurface = (turfModuleSetupSurface) => {
+ let containsBatchObjects = []
+ containsBatchObjects = batchObjects.filter((batchObject) => {
+ let convertBatchObject = polygonToTurfPolygon(batchObject)
+ // 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
+ return (
+ turf.booleanContains(turfModuleSetupSurface, convertBatchObject) ||
+ turf.booleanWithin(convertBatchObject, turfModuleSetupSurface) ||
+ turf.booleanOverlap(turfModuleSetupSurface, convertBatchObject)
+ )
+ })
+
+ return containsBatchObjects
+ }
+
+ if (moduleSetupSurfaces.length !== 0) {
+ // const col = layoutSetupRef.reduce((acc, cur) => acc + cur.col, 0)
+ // const row = layoutSetupRef.reduce((acc, cur) => acc + cur.row, 0)
+
+ let tempModule
+ let manualDrawModules = []
+ let inside = false
+ let turfPolygon
+ let flowDirection
+ let trestlePolygon
+ let width, height, intvHor, intvVer
+ let containsBatchObjects
+
+ 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]
+ manualDrawModules = moduleSetupSurfaces[i].modules // 앞에서 자동으로 했을때 추가됨
+ flowDirection = moduleSetupSurfaces[i].direction //도형의 방향
+
+ containsBatchObjects = objectsIncludeSurface(turfPolygon) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직
+
+ const moduleWidth = Number(checkedModule[0].longAxis) / 10
+ const moduleHeight = Number(checkedModule[0].shortAxis) / 10
+ let tmpWidth = flowDirection === 'south' || flowDirection === 'north' ? moduleWidth : moduleHeight
+ let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth
+
+ width =
+ canvasSetting.roofSizeSet == '1'
+ ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection).width
+ : tmpWidth
+ height =
+ canvasSetting.roofSizeSet == '1'
+ ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection).height
+ : tmpHeight
+
+ intvHor =
+ flowDirection === 'south' || flowDirection === 'north'
+ ? moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10
+ : moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10
+ intvVer =
+ flowDirection === 'south' || flowDirection === 'north'
+ ? moduleSetupSurfaces[i].trestleDetail.moduleIntvlVer / 10
+ : moduleSetupSurfaces[i].trestleDetail.moduleIntvlHor / 10
+
+ //큰버전용 계산
+ // let calcHalfWidth = (Number((width * col).toFixed(1)) + Number((intvHor * (col - 1)).toFixed(1))) / 2
+ // let calcHalfHeight = (Number((height * row).toFixed(1)) + Number((intvVer * (row - 1)).toFixed(1))) / 2
+
+ //작은버전용
+ const points = [
+ {
+ x: toFixedWithoutRounding(mousePoint.x, 2) + toFixedWithoutRounding(width / 2, 2),
+ y: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2),
+ },
+ {
+ x: toFixedWithoutRounding(mousePoint.x, 2) + toFixedWithoutRounding(width / 2, 2),
+ y: toFixedWithoutRounding(mousePoint.y, 2) + toFixedWithoutRounding(height / 2, 2),
+ },
+ {
+ x: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2),
+ y: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2),
+ },
+ {
+ x: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2),
+ y: toFixedWithoutRounding(mousePoint.y, 2) + toFixedWithoutRounding(height / 2, 2),
+ },
+ ]
+
+ //아래래
+ // let points = [
+ // {
+ // x: Number(mousePoint.x.toFixed(1)) - calcHalfWidth,
+ // y: Number(mousePoint.y.toFixed(1)) - calcHalfHeight,
+ // },
+ // {
+ // x: Number(mousePoint.x.toFixed(1)) - calcHalfWidth,
+ // y: Number(mousePoint.y.toFixed(1)) + calcHalfHeight,
+ // },
+ // {
+ // x: Number(mousePoint.x.toFixed(1)) + calcHalfWidth,
+ // y: Number(mousePoint.y.toFixed(1)) + calcHalfHeight,
+ // },
+ // {
+ // x: Number(mousePoint.x.toFixed(1)) + calcHalfWidth,
+ // y: Number(mousePoint.y.toFixed(1)) - calcHalfHeight,
+ // },
+ // ]
+
+ 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: 'rgba(224, 221, 222, 0.7)',
+ stroke: 'black',
+ strokeWidth: 1,
+ strokeDashArray: [10, 5],
+ width: toFixedWithoutRounding(width, 2), //작은버전
+ height: toFixedWithoutRounding(height, 2), //작은버전
+ left: toFixedWithoutRounding(mousePoint.x, 2) - toFixedWithoutRounding(width / 2, 2), //작은버전
+ top: toFixedWithoutRounding(mousePoint.y, 2) - toFixedWithoutRounding(height / 2, 2), //작은버전
+ // width: Number((width * col).toFixed(1)) + Number((intvHor * (col - 1)).toFixed(1)), //큰버전
+ // height: Number((height * row).toFixed(1)) + Number((intvVer * (row - 1)).toFixed(1)), //큰버전
+ // left: Number(mousePoint.x.toFixed(1)) - calcHalfWidth.toFixed(1), //큰버전
+ // top: Number(mousePoint.y.toFixed(1)) - calcHalfHeight.toFixed(1), //큰버전
+ selectable: false,
+ lockMovementX: true,
+ lockMovementY: true,
+ lockRotation: true,
+ lockScalingX: true,
+ lockScalingY: true,
+ name: 'tempModule',
+ parentId: moduleSetupSurfaces[i].parentId,
+ })
+
+ //북면이고 북면설치상점이 아니면 그냥 return
+ if (trestlePolygon.isNorth && !saleStoreNorthFlg) {
+ return
+ } else {
+ canvas?.add(tempModule) //움직여가면서 추가됨
+ }
+
+ /**
+ * 스냅기능
+ */
+ let snapDistance = flowDirection === 'south' || flowDirection === 'north' ? 70 : 40
+ let sideSnapDistance = 15
+ let trestleSnapDistance = 15
+
+ const trestleLeft = toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
+ const trestleTop = toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
+ const trestleRight =
+ toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
+ const trestleBottom =
+ toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
+ const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
+
+ // 이동하는 모듈의 경계 좌표
+ const smallLeft = toFixedWithoutRounding(tempModule.left, 2)
+ const smallTop = toFixedWithoutRounding(tempModule.top, 2)
+ const smallRight = smallLeft + toFixedWithoutRounding(tempModule.width, 2)
+ const smallBottom = smallTop + toFixedWithoutRounding(tempModule.height, 2)
+ const smallCenterX = smallLeft + toFixedWithoutRounding(tempModule.width / 2, 2)
+ const smallCenterY = smallTop + toFixedWithoutRounding(tempModule.height / 2, 2)
+
+ /**
+ * 미리 깔아놓은 셀이 있을때 셀에 흡착됨
+ */
+ if (manualDrawModules) {
+ manualDrawModules.forEach((cell) => {
+ const holdCellLeft = toFixedWithoutRounding(cell.left, 2)
+ const holdCellTop = toFixedWithoutRounding(cell.top, 2)
+ const holdCellRight = holdCellLeft + toFixedWithoutRounding(cell.width, 2)
+ const holdCellBottom = holdCellTop + toFixedWithoutRounding(cell.height, 2)
+ const holdCellCenterX = holdCellLeft + toFixedWithoutRounding(cell.width / 2, 2)
+ const holdCellCenterY = holdCellTop + toFixedWithoutRounding(cell.height / 2, 2)
+
+ //흐름방향따라 달라야 한다.
+ if (flowDirection === 'south' || flowDirection === 'north') {
+ if (Math.abs(smallCenterX - holdCellCenterX) < snapDistance) {
+ //움직이는 모듈 가운데 -> 설치 모듈 가운데
+ tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
+ }
+
+ if (isChidori) {
+ //움직이는 모듈왼쪽 -> 가운데
+ if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) {
+ tempModule.left = holdCellCenterX + intvHor / 2
+ }
+ // 오른쪽 -> 가운데
+ if (Math.abs(smallRight - holdCellCenterX) < snapDistance) {
+ tempModule.left = holdCellCenterX - width - intvHor / 2
+ }
+ }
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+ } else {
+ //흐름방향 west, east
+
+ //가운데 가운데
+ if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - toFixedWithoutRounding(width / 2, 2)
+ }
+
+ if (isChidori) {
+ //위쪽 -> 가운데
+ if (Math.abs(smallTop - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY + intvHor / 2
+ }
+ // 밑 -> 가운데
+ if (Math.abs(smallBottom - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - height - intvHor / 2
+ }
+ }
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+
+ if (Math.abs(smallLeft - holdCellLeft) < snapDistance) {
+ tempModule.left = holdCellLeft
+ }
+ }
+ })
+ }
+
+ // 위쪽 변에 스냅
+ if (Math.abs(smallTop - trestleTop) < trestleSnapDistance) {
+ tempModule.top = trestleTop + 1
+ }
+
+ // 아래쪽 변에 스냅
+ if (Math.abs(smallBottom - trestleBottom) < trestleSnapDistance) {
+ tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height - 1
+ }
+
+ // 왼쪽변에 스냅
+ if (Math.abs(smallLeft - trestleLeft) < trestleSnapDistance) {
+ tempModule.left = trestleLeft + 1
+ }
+ //오른쪽 변에 스냅
+ if (Math.abs(smallRight - trestleRight) < trestleSnapDistance) {
+ tempModule.left = trestleRight - tempModule.width - 1
+ }
+
+ if (flowDirection === 'south' || flowDirection === 'north') {
+ // 모듈이 가운데가 세로중앙선에 붙게 스냅
+ if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < trestleSnapDistance) {
+ tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width / 2
+ }
+ } else {
+ // 모듈이 가로중앙선에 스냅
+ if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < trestleSnapDistance) {
+ tempModule.top = bigCenterY - tempModule.height / 2
+ }
+ }
+
+ tempModule.setCoords()
+ canvas?.renderAll()
+ inside = true
+ break
+ } else {
+ inside = false
+ }
+ }
+
+ if (!inside) {
+ // tempModule.set({ fill: 'red' })
+ canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule'))
+ canvas?.renderAll()
+ }
+ })
+
+ addCanvasMouseEventListener('mouse:up', (e) => {
+ if (trestlePolygon.modules.length > 0) {
+ //이미 설치된 모듈중에 한개만 가져옴
+ const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
+ //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
+ if (checkedModule[0].mixAsgYn !== mixAsgYn) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+ }
+
+ if (!inside) return
+
+ //입력받은 값의 합
+ //지붕재+공법에 따라 최대 모듈 갯수가 달라지므로 클릭시점에 validate 체크해야함
+ const isMultipleModules = checkedModule.length > 1 //모듈이 여러개면
+ const maxCol = trestlePolygon.trestleDetail.moduleMaxCols //최대 열수 -> 얘는 멀티랑 관계없음
+ const maxRow = isMultipleModules
+ ? trestlePolygon.trestleDetail.moduleMaxRows
+ : trestlePolygon.trestleDetail.module.find((item) => item.moduleTpCd === checkedModule[0].moduleTpCd).moduleMaxRows //멀티모듈이면 밖에 maxRows로 판단 아니면 module->itemmList를 가지고 판단
+
+ //단수 합단수
+ const sumRowCount = isMultipleModules
+ ? layoutSetupRef.filter((item) => item.checked).reduce((acc, cur) => acc + cur.row, 0)
+ : layoutSetupRef.find((item) => item.moduleId === checkedModule[0].itemId).row //멀티모듈이면 전체 합, 체크된 한개의 열
+
+ //
+ const sumColCount = layoutSetupRef.filter((item) => item.col).some((item) => item.col > maxCol)
+
+ if (sumRowCount > maxRow || sumColCount) {
+ swalFire({ text: getMessage('module.layout.setup.max.count', [maxRow, maxCol]), icon: 'warning' })
+ return
+ }
+
+ // 혼합일때 모듈 개별의 row를 체크함
+ const isPassedObject =
+ isMultipleModules &&
+ layoutSetupRef.find((item, index) => item.checked && item.row > trestlePolygon.trestleDetail.module[index].mixModuleMaxRows)
+
+ if (isPassedObject) {
+ swalFire({
+ text: getMessage('module.layout.setup.max.count.multiple', [
+ layoutSetupRef.indexOf(isPassedObject) + 1,
+ trestlePolygon.trestleDetail.module[layoutSetupRef.indexOf(isPassedObject)].mixModuleMaxRows,
+ maxCol,
+ ]),
+ icon: 'warning',
+ })
+ return
+ }
+
+ if (tempModule) {
+ let startX, startY
+ let installedLastHeightCoord = 0 //마지막으로 설치된 모듈의 좌표
+ let installedHeightModuleCount = 0
+
+ checkedModule.forEach((module, index) => {
+ //모듈 사이즈
+ const moduleWidth = toFixedWithoutRounding(module.longAxis / 10, 1)
+ const moduleHeight = toFixedWithoutRounding(module.shortAxis / 10, 1)
+
+ //흐름 방향에 따른 모듈 사이즈
+ let tmpWidth = flowDirection === 'south' || flowDirection === 'north' ? moduleWidth : moduleHeight
+ let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth
+
+ //복시도, 실치수에 따른 모듈 높이 조정
+ width =
+ canvasSetting.roofSizeSet == '1'
+ ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(trestlePolygon.roofMaterial.pitch), flowDirection).width
+ : tmpWidth
+ height =
+ canvasSetting.roofSizeSet == '1'
+ ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(trestlePolygon.roofMaterial.pitch), flowDirection).height
+ : tmpHeight
+
+ //그려진 가이드 라인의 포인트를 기준으로 시작점을 만든다
+ //남쪽에 처마방향이나 북쪽에 용마루면
+ //tempModule에 좌,하 좌표를 기준으로 우측으로 위로 그림
+ if ((flowDirection === 'south' && setupLocation === 'eaves') || (flowDirection === 'north' && setupLocation === 'ridge')) {
+ //남
+ startX = toFixedWithoutRounding(tempModule.left, 1)
+ startY = toFixedWithoutRounding(tempModule.top + tempModule.height, 1)
+
+ moduleOptions.fill = module.color
+
+ let col = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].col
+ let row = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].row
+ let tempCol = col
+
+ for (let i = 0; i < row; i++) {
+ let tempY = startY - i * height
+ let isInstalled = false
+
+ if (index > 0) {
+ //두번째 모듈일때 마지막 설치 지점
+ tempY = index > 0 && i === 0 ? installedLastHeightCoord - intvVer : installedLastHeightCoord
+ }
+
+ let tempHeightMargin = i === 0 ? 0 : i * intvVer
+
+ //치도리 설치시 컬럼 조정
+ if (isChidori) {
+ //치도리면 짝수열은 안으로 넣기
+ tempCol = installedHeightModuleCount % 2 === 0 ? col : col - 1
+ }
+
+ for (let j = 0; j < tempCol; j++) {
+ let chidoriMargin = 0
+ let tempX = startX + j * width
+ let tempWidthMargin = j === 0 ? 0 : j * intvHor
+
+ if (isChidori) {
+ chidoriMargin = installedHeightModuleCount % 2 === 0 ? 0 : width / 2 + intvHor
+ }
+
+ let rectPoints = [
+ { x: tempX + tempWidthMargin + chidoriMargin, y: tempY - height - tempHeightMargin },
+ { x: tempX + tempWidthMargin + chidoriMargin, y: tempY - tempHeightMargin },
+ { x: tempX + width + tempWidthMargin + chidoriMargin, y: tempY - tempHeightMargin },
+ { x: tempX + width + tempWidthMargin + chidoriMargin, y: tempY - height - tempHeightMargin },
+ ]
+
+ tempModule.set({ points: rectPoints })
+ const tempTurfModule = polygonToTurfPolygon(tempModule)
+ tempModule.setCoords() //좌표 재정렬
+
+ if (turf.booleanContains(turfPolygon, tempTurfModule) || turf.booleanWithin(tempTurfModule, turfPolygon)) {
+ //마우스 클릭시 set으로 해당 위치에 셀을 넣음
+ const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
+ if (!isOverlap) {
+ canvas?.remove(tempModule)
+
+ //안겹치면 넣는다
+ // tempModule.setCoords()
+ moduleOptions.surfaceId = trestlePolygon.id
+
+ let manualModule = new QPolygon(tempModule.points, {
+ ...moduleOptions,
+ moduleInfo: module,
+ // left: toFixedWithoutRounding(tempX + tempWidthMargin, 1),
+ // top: toFixedWithoutRounding(tempY - height - tempHeightMargin, 1),
+ width: toFixedWithoutRounding(width, 1),
+ height: toFixedWithoutRounding(height, 1),
+ })
+
+ let isDisjoint = checkModuleDisjointObjects(tempTurfModule, containsBatchObjects)
+
+ //오브젝트와 겹치지 않으면 넣는다
+ if (isDisjoint) {
+ canvas?.add(manualModule)
+ canvas?.renderAll()
+ manualDrawModules.push(manualModule)
+ setModuleStatisticsData()
+ installedLastHeightCoord = tempY - height - tempHeightMargin
+
+ if (j === 0) {
+ isInstalled = true
+ }
+ } else {
+ //디버깅용
+ // manualModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
+ // canvas?.add(manualModule)
+ // canvas.renderAll()
+ }
+ } else {
+ swalFire({ text: getMessage('module.place.overlab') })
+ return
+ }
+ }
+ }
+
+ if (isInstalled) {
+ ++installedHeightModuleCount
+ }
+ }
+ //북쪽에 처마거나 남쪽에 용마루일경우
+ //tempModule에 우,상 좌표를 기준으로 좌측으로 아래로 그림
+ } else if ((flowDirection === 'north' && setupLocation === 'eaves') || (flowDirection === 'south' && setupLocation === 'ridge')) {
+ //북북
+ startX = toFixedWithoutRounding(tempModule.left + tempModule.width, 1)
+ startY = toFixedWithoutRounding(tempModule.top, 1)
+
+ moduleOptions.fill = module.color
+
+ const col = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].col
+ const row = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].row
+ let tempCol = col //치도리시 하나 빼려고 임시로
+
+ for (let i = 0; i < row; i++) {
+ let tempY = startY + i * height
+ let isInstalled = false
+
+ if (index > 0) {
+ //두번째 모듈일때 마지막 설치 지점
+ tempY = index > 0 && i === 0 ? installedLastHeightCoord + intvVer : installedLastHeightCoord
+ }
+
+ let tempHeightMargin = i === 0 ? 0 : i * intvVer
+
+ if (isChidori) {
+ tempCol = installedHeightModuleCount % 2 === 0 ? col : col - 1
+ }
+
+ for (let j = 0; j < tempCol; j++) {
+ let chidoriMargin = 0
+ let tempX = startX - j * width
+ let tempWidthMargin = j === 0 ? 0 : j * intvHor
+
+ if (isChidori) {
+ chidoriMargin = installedHeightModuleCount % 2 === 0 ? 0 : width / 2 + intvHor
+ }
+
+ let rectPoints = [
+ { x: tempX - tempWidthMargin - chidoriMargin, y: tempY + height + tempHeightMargin },
+ { x: tempX - tempWidthMargin - chidoriMargin, y: tempY + tempHeightMargin },
+ { x: tempX - width - tempWidthMargin - chidoriMargin, y: tempY + tempHeightMargin },
+ { x: tempX - width - tempWidthMargin - chidoriMargin, y: tempY + height + tempHeightMargin },
+ ]
+
+ tempModule.set({ points: rectPoints })
+ const tempTurfModule = polygonToTurfPolygon(tempModule)
+ tempModule.setCoords() //좌표 재정렬
+
+ if (turf.booleanContains(turfPolygon, tempTurfModule) || turf.booleanWithin(tempTurfModule, turfPolygon)) {
+ //마우스 클릭시 set으로 해당 위치에 셀을 넣음
+ const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
+ if (!isOverlap) {
+ canvas?.remove(tempModule)
+
+ //안겹치면 넣는다
+ // tempModule.setCoords()
+ moduleOptions.surfaceId = trestlePolygon.id
+
+ let manualModule = new QPolygon(tempModule.points, {
+ ...moduleOptions,
+ moduleInfo: module,
+ width: toFixedWithoutRounding(width, 1),
+ height: toFixedWithoutRounding(height, 1),
+ })
+
+ let isDisjoint = checkModuleDisjointObjects(tempTurfModule, containsBatchObjects)
+
+ //오브젝트와 겹치지 않으면 넣는다
+ if (isDisjoint) {
+ canvas?.add(manualModule)
+ canvas?.renderAll()
+ manualDrawModules.push(manualModule)
+ setModuleStatisticsData()
+ installedLastHeightCoord = tempY + height + tempHeightMargin
+
+ if (j === 0) {
+ isInstalled = true
+ }
+ } else {
+ //디버깅용
+ // manualModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
+ // canvas?.add(manualModule)
+ // canvas.renderAll()
+ }
+ } else {
+ swalFire({ text: getMessage('module.place.overlab') })
+ return
+ }
+ }
+ }
+ if (isInstalled) {
+ ++installedHeightModuleCount
+ }
+ }
+ //서쪽에 처마 또는 동쪽의 용마루일경우
+ //tempModule에 좌,상 좌표를 기준으로 아래로 좌측으로 그림
+ } else if ((flowDirection === 'west' && setupLocation === 'eaves') || (flowDirection === 'east' && setupLocation === 'ridge')) {
+ startX = toFixedWithoutRounding(tempModule.left, 1)
+ startY = toFixedWithoutRounding(tempModule.top, 1)
+ moduleOptions.fill = module.color
+
+ const col = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].col
+ const row = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].row
+ let tempCol = col //치도리시 하나 빼려고 임시로
+
+ for (let i = 0; i < row; i++) {
+ //옆으로 올라가는거라 height가 아니고 width로 변경
+ let tempX = startX + i * width
+ let tempWidthMargin = i === 0 ? 0 : i * intvHor
+ let isInstalled = false
+
+ if (index > 0) {
+ //두번째 모듈일때 마지막 설치 지점 //명칭은 그대로 쓴다
+ tempX = index > 0 && i === 0 ? installedLastHeightCoord + intvHor : installedLastHeightCoord
+ }
+
+ if (isChidori) {
+ tempCol = installedHeightModuleCount % 2 === 0 ? col : col - 1
+ }
+
+ for (let j = 0; j < tempCol; j++) {
+ let tempY = startY + j * height
+ let tempHeightMargin = j === 0 ? 0 : j * intvVer
+
+ let chidoriMargin = 0
+
+ if (isChidori) {
+ chidoriMargin = installedHeightModuleCount % 2 === 0 ? 0 : height / 2 + intvVer
+ }
+
+ let rectPoints = [
+ { x: tempX + tempWidthMargin, y: tempY + tempHeightMargin + chidoriMargin },
+ { x: tempX + tempWidthMargin, y: tempY + height + tempHeightMargin + chidoriMargin },
+ { x: tempX + width + tempWidthMargin, y: tempY + height + tempHeightMargin + chidoriMargin },
+ { x: tempX + width + tempWidthMargin, y: tempY + tempHeightMargin + chidoriMargin },
+ ]
+
+ tempModule.set({ points: rectPoints })
+ const tempTurfModule = polygonToTurfPolygon(tempModule)
+ tempModule.setCoords() //좌표 재정렬
+
+ if (turf.booleanContains(turfPolygon, tempTurfModule) || turf.booleanWithin(tempTurfModule, turfPolygon)) {
+ //마우스 클릭시 set으로 해당 위치에 셀을 넣음
+ const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
+ if (!isOverlap) {
+ canvas?.remove(tempModule)
+
+ //안겹치면 넣는다
+ // tempModule.setCoords()
+ moduleOptions.surfaceId = trestlePolygon.id
+
+ let manualModule = new QPolygon(tempModule.points, {
+ ...moduleOptions,
+ moduleInfo: module,
+ width: toFixedWithoutRounding(width, 1),
+ height: toFixedWithoutRounding(height, 1),
+ })
+
+ let isDisjoint = checkModuleDisjointObjects(tempTurfModule, containsBatchObjects)
+
+ //오브젝트와 겹치지 않으면 넣는다
+ if (isDisjoint) {
+ canvas?.add(manualModule)
+ canvas?.renderAll()
+ manualDrawModules.push(manualModule)
+ setModuleStatisticsData()
+ installedLastHeightCoord = tempX + width + tempWidthMargin
+
+ if (j === 0) {
+ isInstalled = true
+ }
+ } else {
+ //디버깅용
+ // manualModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
+ // canvas?.add(manualModule)
+ // canvas.renderAll()
+ }
+ } else {
+ swalFire({ text: getMessage('module.place.overlab') })
+ return
+ }
+ }
+ }
+ if (isInstalled) {
+ ++installedHeightModuleCount
+ }
+ }
+ //동쪽의 처마 또는 서쪽의 용마루일경우
+ //tempModule에 우,하 좌표를 기준으로 위로 우측으로 그림
+ } else if ((flowDirection === 'east' && setupLocation === 'eaves') || (flowDirection === 'west' && setupLocation === 'ridge')) {
+ startX = toFixedWithoutRounding(tempModule.left + width, 1)
+ startY = toFixedWithoutRounding(tempModule.top + height, 1)
+ moduleOptions.fill = module.color
+
+ const col = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].col
+ const row = layoutSetupRef.filter((item) => item.moduleId === module.itemId)[0].row
+ let tempCol = col //치도리시 하나 빼려고 임시로
+
+ for (let i = 0; i < row; i++) {
+ //옆으로 올라가는거라 height가 아니고 width로 변경
+ let tempX = startX - i * width
+ let tempWidthMargin = i === 0 ? 0 : i * intvHor
+ let isInstalled = false
+
+ if (index > 0) {
+ //두번째 모듈일때 마지막 설치 지점 //명칭은 그대로 쓴다
+ tempX = index > 0 && i === 0 ? installedLastHeightCoord - intvHor : installedLastHeightCoord
+ }
+
+ if (isChidori) {
+ tempCol = installedHeightModuleCount % 2 === 0 ? col : col - 1
+ }
+
+ for (let j = 0; j < tempCol; j++) {
+ let chidoriMargin = 0
+ let tempY = startY - j * height
+ let tempHeightMargin = j === 0 ? 0 : j * intvVer
+
+ if (isChidori) {
+ chidoriMargin = installedHeightModuleCount % 2 === 0 ? 0 : height / 2 + intvVer
+ }
+
+ let rectPoints = [
+ { x: tempX - tempWidthMargin, y: tempY - tempHeightMargin - chidoriMargin },
+ { x: tempX - tempWidthMargin, y: tempY - height - tempHeightMargin - chidoriMargin },
+ { x: tempX - width - tempWidthMargin, y: tempY - height - tempHeightMargin - chidoriMargin },
+ { x: tempX - width - tempWidthMargin, y: tempY - tempHeightMargin - chidoriMargin },
+ ]
+
+ tempModule.set({ points: rectPoints })
+ const tempTurfModule = polygonToTurfPolygon(tempModule)
+ tempModule.setCoords() //좌표 재정렬
+
+ if (turf.booleanContains(turfPolygon, tempTurfModule) || turf.booleanWithin(tempTurfModule, turfPolygon)) {
+ //마우스 클릭시 set으로 해당 위치에 셀을 넣음
+ const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
+ if (!isOverlap) {
+ canvas?.remove(tempModule)
+
+ //안겹치면 넣는다
+ // tempModule.setCoords()
+ moduleOptions.surfaceId = trestlePolygon.id
+
+ let manualModule = new QPolygon(tempModule.points, {
+ ...moduleOptions,
+ moduleInfo: module,
+ width: toFixedWithoutRounding(width, 1),
+ height: toFixedWithoutRounding(height, 1),
+ })
+
+ let isDisjoint = checkModuleDisjointObjects(tempTurfModule, containsBatchObjects)
+
+ //오브젝트와 겹치지 않으면 넣는다
+ if (isDisjoint) {
+ canvas?.add(manualModule)
+ canvas?.renderAll()
+ manualDrawModules.push(manualModule)
+ setModuleStatisticsData()
+ installedLastHeightCoord = tempX - width - tempWidthMargin
+
+ if (j === 0) {
+ isInstalled = true
+ }
+ } else {
+ //디버깅용
+ // manualModule.set({ fill: 'transparent', stroke: 'red', strokeWidth: 1 })
+ // canvas?.add(manualModule)
+ // canvas.renderAll()
+ }
+ } else {
+ swalFire({ text: getMessage('module.place.overlab') })
+ return
+ }
+ }
+ }
+ if (isInstalled) {
+ ++installedHeightModuleCount
+ }
+ }
+ }
+ })
+ //그림자가 있다면 무조건 그림자를 가장 앞으로 올림
+ const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
+ if (shadowObj) {
+ shadowObj.bringToFront()
+ }
+ }
+ })
+ }
} else {
if (moduleSetupSurfaces) {
//수동모드 해제시 모듈 설치면 선택 잠금
@@ -814,9 +1740,19 @@ export function useModuleBasicSetting(tabNum) {
return
}
- const isChidori = placementRef.isChidori.current === 'true' ? true : false
- const setupLocation = placementRef.setupLocation.current
- const isMaxSetup = placementRef.isMaxSetup.current === 'true' ? true : false
+ //혼합 가능 모듈과 혼합 불가능 모듈을 선택했을때 카운트를 해서 확인
+ const mixAsgY = checkedModule.filter((obj) => obj.mixAsgYn === 'Y')
+ const mixAsgN = checkedModule.filter((obj) => obj.mixAsgYn === 'N')
+
+ //Y인 모듈과 N인 모듈이 둘다 존재하면 설치 불가
+ if (mixAsgY.length > 0 && mixAsgN.length > 0) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+
+ const isChidori = moduleSetupOption.isChidori
+ const setupLocation = moduleSetupOption.setupLocation
+ const isMaxSetup = false
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
@@ -832,24 +1768,11 @@ export function useModuleBasicSetting(tabNum) {
//어짜피 자동으로 누르면 선택안된데도 다 날아간다
canvas.getObjects().forEach((obj) => {
- if (obj.name === 'module') {
+ if (obj.name === POLYGON_TYPE.MODULE) {
canvas.remove(obj)
}
})
- // if (moduleIsSetup.length > 0) {
- // swalFire({ text: 'alert 아이콘 테스트입니다.', icon: 'error' })
- // }
-
- // moduleSetupSurfaces.forEach((obj) => {
- // if (obj.modules) {
- // obj.modules.forEach((module) => {
- // canvas?.remove(module)
- // })
- // obj.modules = []
- // }
- // })
-
notSelectedTrestlePolygons.forEach((obj) => {
if (obj.modules) {
obj.modules.forEach((module) => {
@@ -868,11 +1791,9 @@ export function useModuleBasicSetting(tabNum) {
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
- name: 'module',
+ name: POLYGON_TYPE.MODULE,
}
- let leftMargin, bottomMargin, square, chidoriLength
-
//선택된 지붕안에 오브젝트(도머, 개구등)이 있는지 확인하는 로직 포함되면 배열 반환
const objectsIncludeSurface = (turfModuleSetupSurface) => {
let containsBatchObjects = []
@@ -889,36 +1810,6 @@ export function useModuleBasicSetting(tabNum) {
return containsBatchObjects
}
- // /**
- // * 도머나 개구가 모듈에 걸치는지 확인하는 로직
- // * @param {*} squarePolygon
- // * @param {*} containsBatchObjects
- // * @returns
- // */
- // const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => {
- // let isDisjoint = false
- //
- // if (containsBatchObjects.length > 0) {
- // let convertBatchObject
- // //도머가 있으면 적용되는 로직
- // isDisjoint = containsBatchObjects.every((batchObject) => {
- // if (batchObject.type === 'group') {
- // convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
- // } else {
- // convertBatchObject = polygonToTurfPolygon(batchObject)
- // }
- // /**
- // * 도머가 여러개일수있으므로 겹치는게 있다면...
- // * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환
- // */
- // return turf.booleanDisjoint(squarePolygon, convertBatchObject)
- // })
- // } else {
- // isDisjoint = true
- // }
- // return isDisjoint
- // }
-
/**
* 배치면 안에 있는지 확인
* @param {*} squarePolygon
@@ -953,7 +1844,7 @@ export function useModuleBasicSetting(tabNum) {
checkedModule.forEach((module, moduleIndex) => {
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
- let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
+ // let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
//모듈의 넓이 높이를 가져옴 (복시도 촌수 적용)
//1번 깔았던 모듈 기준으로 잡야아함
@@ -983,7 +1874,7 @@ export function useModuleBasicSetting(tabNum) {
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
if (flowLines.left.type === 'curve' && flowLines.right.type === 'curve') {
- startPointX = isChidori ? flowLines.left.x1 + 1 : flowLines.left.x1 + (calcAreaWidth - totalModuleWidthCount * width) / 2
+ startPointX = flowLines.left.x1 + (calcAreaWidth - totalModuleWidthCount * width) / 2
}
let heightMargin = 0
@@ -1012,7 +1903,7 @@ export function useModuleBasicSetting(tabNum) {
let moduleX = startPointX + width * j + 1 //5정도 마진을 준다
widthMargin = j === 0 ? 0 : intvHor * j // 가로 마진값
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
- if (isChidori && !isMaxSetup) {
+ if (isChidori) {
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : width / 2 - intvHor
}
@@ -1534,37 +2425,8 @@ export function useModuleBasicSetting(tabNum) {
if (moduleSetupSurface.direction === 'north') {
downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
}
- } else if (setupLocation === 'center') {
- //중가면
- if (moduleSetupSurface.direction === 'south') {
- downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
- }
- if (moduleSetupSurface.direction === 'west') {
- leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
- }
- if (moduleSetupSurface.direction === 'east') {
- rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
- }
- if (moduleSetupSurface.direction === 'north') {
- topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, true, intvHor, intvVer)
- }
}
- // const setupedModules = moduleSetupArray.filter((module, index) => {
- // let disjointFromTrestle = checkModuleDisjointSurface(module.turfPoints, turfModuleSetupSurface)
- // let isDisjoint = checkModuleDisjointObjects(module.turfPoints, containsBatchObjects)
-
- // if (!(disjointFromTrestle && isDisjoint)) {
- // canvas?.remove(module)
- // // module.set({ fill: 'rgba(255,190,41, 0.4)', stroke: 'black', strokeWidth: 1 })
- // return false
- // } else {
- // return module
- // }
- // })
-
- // canvas?.renderAll()
-
//나간애들 제외하고 설치된 애들로 겹친애들 삭제 하기
moduleSetupArray.forEach((module, index) => {
if (isMaxSetup && index > 0) {
@@ -1590,6 +2452,12 @@ export function useModuleBasicSetting(tabNum) {
// moduleSetupArray: setupedModules,
// })
// setModuleIsSetup(moduleArray)
+
+ //그림자는 무조건 가장 앞으로
+ const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
+ if (shadowObj) {
+ shadowObj.bringToFront()
+ }
})
// calculateForApi()
}
@@ -1622,8 +2490,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: -Infinity, y1: -Infinity, index: -1 },
)
+
flowArray.push(bottomFlow)
const topFlow = surface.lines.reduce(
@@ -1633,8 +2502,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: Infinity, y1: Infinity, index: -1 },
)
+
flowArray.push(topFlow)
let rtnObjArray = []
@@ -1684,14 +2554,7 @@ export function useModuleBasicSetting(tabNum) {
const pointY2 = top
//디버깅
- const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
- stroke: 'red',
- strokeWidth: 1,
- selectable: true,
- })
-
- // console.log(`index ${index} : finalLine`, pointX1, pointY1, pointX2, pointY2)
-
+ const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { stroke: 'red', strokeWidth: 1, selectable: true })
// canvas?.add(finalLine)
// canvas?.renderAll()
@@ -1742,8 +2605,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: Infinity, y1: Infinity, index: -1 },
)
+
flowArray.push(leftFlow)
const rightFlow = surface.lines.reduce(
@@ -1753,8 +2617,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: -Infinity, y1: -Infinity, index: -1 },
)
+
flowArray.push(rightFlow)
let rtnObjArray = []
@@ -1810,11 +2675,7 @@ export function useModuleBasicSetting(tabNum) {
const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y)
//디버깅용
- const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
- stroke: 'red',
- strokeWidth: 1,
- selectable: true,
- })
+ const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { stroke: 'red', strokeWidth: 1, selectable: true })
// canvas?.add(finalLine)
// canvas?.renderAll()
@@ -1896,12 +2757,7 @@ export function useModuleBasicSetting(tabNum) {
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
- const obj = {
- left: leftFlow,
- right: rightFlow,
- top: topFlow,
- bottom: bottomFlow,
- }
+ const obj = { left: leftFlow, right: rightFlow, top: topFlow, bottom: bottomFlow }
return obj
}
@@ -1934,7 +2790,7 @@ export function useModuleBasicSetting(tabNum) {
) //도머s 객체
let moduleOptions = {
- fill: '#BFFD9F',
+ fill: checkedModule[0].color,
stroke: 'black',
strokeWidth: 0.3,
selectable: true, // 선택 가능하게 설정
@@ -1967,14 +2823,15 @@ export function useModuleBasicSetting(tabNum) {
const angle = calculateAngle(points1, points2)
//변별로 선택으로 되어있을때 모듈면을 회전시키기
- const targetdSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0]
- targetdSurface.angle = -angle
+ const targetSurface = moduleSetupSurfaces.filter((surface) => surface.surfaceId === obj.surfaceId)[0]
+ targetSurface.angle = -angle
//변별로 선택되어있는 지붕도 회전시키기
- const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetdSurface.parentId)[0]
+ const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === targetSurface.parentId)[0]
targetRoof.angle = -angle
targetRoof.fire('modified')
- targetdSurface.fire('modified')
+ targetSurface.fire('modified')
+ // drawDirectionArrow(targetRoof)
}
canvas.remove(obj)
})
@@ -1983,6 +2840,10 @@ export function useModuleBasicSetting(tabNum) {
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0]
if (targetRoof) targetRoof.angle = -compasDeg
surface.angle = -compasDeg
+
+ targetRoof.fire('modified')
+ surface.fire('modified')
+ // drawDirectionArrow(targetRoof)
})
}
canvas.renderAll()
@@ -2046,131 +2907,158 @@ export function useModuleBasicSetting(tabNum) {
/**
* 스냅기능
*/
- let snapDistance = 10
- let cellSnapDistance = 50
+ let snapDistance = flowDirection === 'south' || flowDirection === 'north' ? 70 : 40
+ let sideSnapDistance = 15
+ let trestleSnapDistance = 15
- let intvHor = flowDirection === 'south' || flowDirection === 'north' ? 1 : 3
- let intvVer = flowDirection === 'south' || flowDirection === 'north' ? 3 : 1
+ let intvVer = flowDirection === 'south' || flowDirection === 'north' ? 10 : 30
+ let intvHor = flowDirection === 'south' || flowDirection === 'north' ? 30 : 10
- 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 trestleLeft = toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
+ const trestleTop = toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) - toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
+ const trestleRight =
+ toFixedWithoutRounding(moduleSetupSurfaces[i].left, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].width / 2, 2)
+ const trestleBottom =
+ toFixedWithoutRounding(moduleSetupSurfaces[i].top, 2) + toFixedWithoutRounding(moduleSetupSurfaces[i].height / 2, 2)
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
+ // 이동하는 모듈의 경계 좌표
+ const smallLeft = toFixedWithoutRounding(tempModule.left, 2)
+ const smallTop = toFixedWithoutRounding(tempModule.top, 2)
+ const smallRight = smallLeft + toFixedWithoutRounding(tempModule.width, 2)
+ const smallBottom = smallTop + toFixedWithoutRounding(tempModule.height, 2)
+ const smallCenterX = smallLeft + toFixedWithoutRounding(tempModule.width / 2, 2)
+ const smallCenterY = smallTop + toFixedWithoutRounding(tempModule.height / 2, 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
+ const holdCellLeft = toFixedWithoutRounding(cell.left, 2)
+ const holdCellTop = toFixedWithoutRounding(cell.top, 2)
+ const holdCellRight = holdCellLeft + toFixedWithoutRounding(cell.width, 2)
+ const holdCellBottom = holdCellTop + toFixedWithoutRounding(cell.height, 2)
+ const holdCellCenterX = holdCellLeft + toFixedWithoutRounding(cell.width / 2, 2)
+ const holdCellCenterY = holdCellTop + toFixedWithoutRounding(cell.height / 2, 2)
- //설치된 셀에 좌측에 스냅
- if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
- tempModule.left = holdCellLeft - width - intvHor
- }
+ //흐름방향따라 달라야 한다.
+ if (flowDirection === 'south' || flowDirection === 'north') {
+ if (Math.abs(smallCenterX - holdCellCenterX) < snapDistance) {
+ //움직이는 모듈 가운데 -> 설치 모듈 가운데
+ tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
+ }
- //설치된 셀에 우측에 스냅
- if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
- tempModule.left = holdCellRight + intvHor
- }
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
- //설치된 셀에 위쪽에 스냅
- if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
- tempModule.top = holdCellTop - height - intvVer
- }
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
- //설치된 셀에 밑쪽에 스냅
- if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
- tempModule.top = holdCellBottom + intvVer
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+ } else {
+ //흐름방향 west, east
+
+ //가운데 가운데
+ if (Math.abs(smallCenterY - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - toFixedWithoutRounding(width / 2, 2)
+ }
+
+ //위쪽 -> 가운데
+ if (Math.abs(smallTop - holdCellCenterY) < snapDistance) {
+ // console.log('holdCellCenterX', holdCellCenterX)
+ // console.log('smallLeft', smallLeft)
+
+ // console.log('모듈 센터에 스냅')
+ tempModule.top = holdCellCenterY + intvHor / 2
+
+ // console.log('tempModule.left', tempModule.left)
+ }
+ // 밑 -> 가운데
+ if (Math.abs(smallBottom - holdCellCenterY) < snapDistance) {
+ tempModule.top = holdCellCenterY - height - intvHor / 2
+ }
+
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ // console.log('모듈 좌측 스냅')
+ tempModule.left = holdCellLeft - width - intvHor
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ // console.log('모듈 우측 스냅')
+ tempModule.left = holdCellRight + intvHor
+ }
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop - height - intvVer
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < sideSnapDistance) {
+ tempModule.top = holdCellBottom + intvVer
+ }
+
+ //설치된 셀에 윗쪽에 스냅
+ if (Math.abs(smallTop - holdCellTop) < sideSnapDistance) {
+ tempModule.top = holdCellTop
+ }
+
+ if (Math.abs(smallLeft - holdCellLeft) < snapDistance) {
+ tempModule.left = holdCellLeft
+ }
}
- //가운데 -> 가운데
- 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 - trestleTop) < trestleSnapDistance) {
+ tempModule.top = trestleTop + 1
}
// 아래쪽 변에 스냅
- 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(smallBottom - trestleBottom) < trestleSnapDistance) {
+ tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height - 1
}
// 왼쪽변에 스냅
- if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
- tempModule.left = trestleLeft
+ if (Math.abs(smallLeft - trestleLeft) < trestleSnapDistance) {
+ tempModule.left = trestleLeft + 1
}
//오른쪽 변에 스냅
- if (Math.abs(smallRight - trestleRight) < snapDistance) {
- tempModule.left = trestleRight - tempModule.width * tempModule.scaleX
+ if (Math.abs(smallRight - trestleRight) < trestleSnapDistance) {
+ tempModule.left = trestleRight - tempModule.width - 1
}
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
+ if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < trestleSnapDistance) {
+ tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width / 2
}
} else {
// 모듈이 가로중앙선에 스냅
- if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) {
+ if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < trestleSnapDistance) {
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()
@@ -2191,15 +3079,23 @@ export function useModuleBasicSetting(tabNum) {
addCanvasMouseEventListener('mouse:up', (e) => {
let isIntersection = true
+
+ if (trestlePolygon.modules.length > 0) {
+ //이미 설치된 모듈중에 한개만 가져옴
+ const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
+ //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
+ if (checkedModule[0].mixAsgYn !== mixAsgYn) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+ }
+
if (!inside) return
if (tempModule) {
const rectPoints = [
{ x: tempModule.left, y: tempModule.top },
{ x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top },
- {
- x: tempModule.left + tempModule.width * tempModule.scaleX,
- y: tempModule.top + tempModule.height * tempModule.scaleY,
- },
+ { x: tempModule.left + tempModule.width * tempModule.scaleX, y: tempModule.top + tempModule.height * tempModule.scaleY },
{ x: tempModule.left, y: tempModule.top + tempModule.height * tempModule.scaleY },
]
@@ -2254,10 +3150,7 @@ export function useModuleBasicSetting(tabNum) {
if (moduleSetupSurfaces) {
//수동모드 해제시 모듈 설치면 선택 잠금
moduleSetupSurfaces.forEach((obj) => {
- obj.set({
- selectable: true,
- evented: true,
- })
+ obj.set({ selectable: true, evented: true })
})
}
@@ -2270,6 +3163,16 @@ export function useModuleBasicSetting(tabNum) {
const autoFlatroofModuleSetup = (placementFlatRef) => {
initEvent() //마우스 이벤트 초기화
+ //혼합 가능 모듈과 혼합 불가능 모듈을 선택했을때 카운트를 해서 확인
+ const mixAsgY = checkedModule.filter((obj) => obj.mixAsgYn === 'Y')
+ const mixAsgN = checkedModule.filter((obj) => obj.mixAsgYn === 'N')
+
+ //Y인 모듈과 N인 모듈이 둘다 존재하면 설치 불가
+ if (mixAsgY.length > 0 && mixAsgN.length > 0) {
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error') })
+ return
+ }
+
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
?.getObjects()
@@ -2335,6 +3238,7 @@ export function useModuleBasicSetting(tabNum) {
targetRoof.setCoords()
targetSurface.setCoords()
moduleSetupSurfaces.push(targetSurface)
+ // drawDirectionArrow(targetSurface)
}
canvas.remove(obj)
})
@@ -2343,6 +3247,10 @@ export function useModuleBasicSetting(tabNum) {
const targetRoof = canvas.getObjects().filter((roof) => roof.name === POLYGON_TYPE.ROOF && roof.id === surface.parentId)[0]
if (targetRoof) targetRoof.angle = -compasDeg
surface.angle = -compasDeg
+
+ targetRoof.fire('modified')
+ surface.fire('modified')
+ // drawDirectionArrow(surface)
})
}
canvas.renderAll()
@@ -2425,8 +3333,6 @@ export function useModuleBasicSetting(tabNum) {
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
}
- let moduleGroup = []
-
const flatRoofDownFlowSetupModule = (
surfaceMaxLines,
maxLengthLine,
@@ -2698,11 +3604,13 @@ export function useModuleBasicSetting(tabNum) {
selectedModules,
makeModuleInstArea,
manualModuleSetup,
+ manualModuleLayoutSetup,
autoModuleSetup,
restoreModuleInstArea,
manualFlatroofModuleSetup,
autoFlatroofModuleSetup,
checkModuleDisjointObjects,
makeModuleInitArea,
+ roofOutlineColor,
}
}
diff --git a/src/hooks/module/useModulePlace.js b/src/hooks/module/useModulePlace.js
deleted file mode 100644
index 38e628a0..00000000
--- a/src/hooks/module/useModulePlace.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { useEffect, useState } from 'react'
-import { useRecoilValue, useSetRecoilState } from 'recoil'
-import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
-import { useMasterController } from '@/hooks/common/useMasterController'
-import { canvasSettingState, canvasState, currentCanvasPlanState, moduleSetupSurfaceState } from '@/store/canvasAtom'
-import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
-import { useRoofFn } from '@/hooks/common/useRoofFn'
-import { roofDisplaySelector } from '@/store/settingAtom'
-import offsetPolygon from '@/util/qpolygon-utils'
-import { v4 as uuidv4 } from 'uuid'
-import { QPolygon } from '@/components/fabric/QPolygon'
-import { useEvent } from '@/hooks/useEvent'
-import { useSwal } from '@/hooks/useSwal'
-import { useMessage } from '@/hooks/useMessage'
-
-export function useModulePlace() {
- const canvas = useRecoilValue(canvasState)
- const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
- const [trestleDetailParams, setTrestleDetailParams] = useState([])
- const [trestleDetailList, setTrestleDetailList] = useState([])
- const selectedModules = useRecoilValue(selectedModuleState)
- const { getTrestleDetailList } = useMasterController()
- const canvasSetting = useRecoilValue(canvasSettingState)
- const { setSurfaceShapePattern } = useRoofFn()
- const roofDisplay = useRecoilValue(roofDisplaySelector)
- const { addTargetMouseEventListener } = useEvent()
- const setModuleSetupSurface = useSetRecoilState(moduleSetupSurfaceState)
- const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false)
- const { swalFire } = useSwal()
- const { getMessage } = useMessage()
-
- return {
- selectedModules,
- }
-}
diff --git a/src/hooks/module/useModuleSelection.js b/src/hooks/module/useModuleSelection.js
index 8fa274a4..c78ffff1 100644
--- a/src/hooks/module/useModuleSelection.js
+++ b/src/hooks/module/useModuleSelection.js
@@ -13,22 +13,19 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
export function useModuleSelection(props) {
const canvas = useRecoilValue(canvasState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
-
const [roughnessCodes, setRoughnessCodes] = useState([]) //면조도 목록
const [windSpeedCodes, setWindSpeedCodes] = useState([]) //기준풍속 목록
const [moduleList, setModuleList] = useState([{}]) //모듈 목록
-
const [selectedSurfaceType, setSelectedSurfaceType] = useState({}) //선택된 면조도
- const [installHeight, setInstallHeight] = useState() //설치 높이
- const [standardWindSpeed, setStandardWindSpeed] = useState({}) //기준풍속
- const [verticalSnowCover, setVerticalSnowCover] = useState() //수직적설량
-
+ const [installHeight, setInstallHeight] = useState(managementState?.installHeight) //설치 높이
+ const [standardWindSpeed, setStandardWindSpeed] = useState() //기준풍속
+ const [verticalSnowCover, setVerticalSnowCover] = useState(managementState?.verticalSnowCover) //수직적설량
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) //선택된 모듈
+ const [margin, setMargin] = useState(100)
const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
const { getModuleTypeItemList } = useMasterController()
const { findCommonCode } = useCommonCode()
const resetStatisticsData = useResetRecoilState(moduleStatisticsState)
-
const { restoreModuleInstArea } = useModuleBasicSetting()
const bindInitData = () => {
@@ -95,7 +92,7 @@ export function useModuleSelection(props) {
getModuleData(roofsIds)
//모듈설치면 초기화
- restoreModuleInstArea()
+ // restoreModuleInstArea()
resetStatisticsData()
}, [])
@@ -184,14 +181,6 @@ export function useModuleSelection(props) {
})
}
- useEffect(() => {
- // console.log('installHeight', installHeight)
- }, [installHeight])
-
- useEffect(() => {
- // console.log('verticalSnowCover', verticalSnowCover)
- }, [verticalSnowCover])
-
//TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨
// useEffect(() => {
@@ -226,11 +215,17 @@ export function useModuleSelection(props) {
roughnessCodes,
windSpeedCodes,
managementState,
+ setManagementState,
moduleList,
+ setSelectedModules,
selectedSurfaceType,
+ setSelectedSurfaceType,
installHeight,
+ setInstallHeight,
standardWindSpeed,
+ setStandardWindSpeed,
verticalSnowCover,
+ setVerticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
diff --git a/src/hooks/module/useModuleTrestle.js b/src/hooks/module/useModuleTrestle.js
new file mode 100644
index 00000000..3d14d600
--- /dev/null
+++ b/src/hooks/module/useModuleTrestle.js
@@ -0,0 +1,288 @@
+import { use, useContext, useEffect, useReducer, useState } from 'react'
+import { useCommonCode } from '../common/useCommonCode'
+import { useMasterController } from '../common/useMasterController'
+import { selectedModuleState } from '@/store/selectedModuleOptions'
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { GlobalDataContext } from '@/app/GlobalDataProvider'
+import { popSpinnerState } from '@/store/popupAtom'
+
+const RAFT_BASE_CODE = '203800'
+
+const trestleReducer = (state, action) => {
+ switch (action.type) {
+ case 'SET_RAFT_BASE':
+ case 'SET_TRESTLE_MAKER':
+ case 'SET_CONST_MTHD':
+ case 'SET_ROOF_BASE':
+ case 'SET_CONSTRUCTION':
+ case 'SET_TRESTLE_DETAIL':
+ return {
+ ...action.roof,
+ }
+ case 'SET_INITIALIZE':
+ return {
+ moduleTpCd: action.roof.module?.itemTp ?? '',
+ roofMatlCd: action.roof?.roofMatlCd ?? '',
+ hajebichi: action.roof?.hajebichi ?? 0,
+ raftBaseCd: action.roof?.raftBaseCd ?? null,
+ trestleMkrCd: action.roof.trestle?.trestleMkrCd ?? null,
+ constMthdCd: action.roof.trestle?.constMthdCd ?? null,
+ constTp: action.roof.construction?.constTp ?? null,
+ roofBaseCd: action.roof.trestle?.roofBaseCd ?? null,
+ workingWidth: action.roof.workingWidth ?? 0,
+ lengthBase: action.roof?.length ?? 0,
+ illuminationTp: action.roof.common?.illuminationTp ?? null,
+ instHt: action.roof.common?.instHt ?? null,
+ stdWindSpeed: action.roof.common?.stdWindSpeed ?? null,
+ stdSnowLd: action.roof.common?.stdSnowLd ?? null,
+ inclCd: action.roof?.pitch ?? null,
+ roofPitch: action.roof?.roofPchBase ?? 0,
+ eavesMargin: action.roof?.eavesMargin ?? null,
+ ridgeMargin: action.roof?.ridgeMargin ?? null,
+ kerabaMargin: action.roof?.kerabaMargin ?? null,
+ }
+ default:
+ return state
+ }
+}
+
+export function useModuleTrestle(props) {
+ const { selectedRoof } = props
+ const { findCommonCode } = useCommonCode()
+ const [raftBaseList, setRaftBaseList] = useState([])
+ const [trestleList, setTrestleList] = useState([])
+ const [constMthdList, setConstMthdList] = useState([])
+ const [roofBaseList, setRoofBaseList] = useState([])
+ const [constructionList, setConstructionList] = useState([])
+ const { getTrestleList, getConstructionList, getTrestleDetailList } = useMasterController()
+
+ const [lengthBase, setLengthBase] = useState(0)
+ const [hajebichi, setHajebichi] = useState(0)
+ const [cvrYn, setCvrYn] = useState('N')
+ const [cvrChecked, setCvrChecked] = useState(false)
+ const [snowGdPossYn, setSnowGdPossYn] = useState('N')
+ const [snowGdChecked, setSnowGdChecked] = useState(false)
+ const [eavesMargin, setEavesMargin] = useState(0)
+ const [ridgeMargin, setRidgeMargin] = useState(0)
+ const [kerabaMargin, setKerabaMargin] = useState(0)
+ const [trestleState, dispatch] = useReducer(trestleReducer, null)
+ const [trestleDetail, setTrestleDetail] = useState(null)
+ const [popSpinnerStore, setPopSpinnerStore] = useRecoilState(popSpinnerState)
+
+ useEffect(() => {
+ const raftCodeList = findCommonCode(RAFT_BASE_CODE)
+
+ setRaftBaseList(raftCodeList)
+ setTrestleList([])
+ setConstMthdList([])
+ setRoofBaseList([])
+ setConstructionList([])
+ // setEavesMargin(selectedRoof?.addRoof?.eavesMargin ?? 0)
+ // setRidgeMargin(selectedRoof?.addRoof?.ridgeMargin ?? 0)
+ // setKerabaMargin(selectedRoof?.addRoof?.kerabaMargin ?? 0)
+
+ setHajebichi(selectedRoof?.hajebichi ?? 0)
+ setEavesMargin(selectedRoof?.eavesMargin ?? 0)
+ setRidgeMargin(selectedRoof?.ridgeMargin ?? 0)
+ setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
+ setLengthBase(Math.round(selectedRoof?.length ?? 0))
+ setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N')
+ setCvrChecked(selectedRoof?.construction?.cvrChecked ?? false)
+ setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N')
+ setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false)
+ setTrestleDetail(selectedRoof?.trestleDetail)
+ }, [selectedRoof])
+
+ useEffect(() => {
+ if (trestleState) {
+ handleSetTrestleList()
+ if (!trestleState?.trestleMkrCd) {
+ setConstMthdList([])
+ setRoofBaseList([])
+ setConstructionList([])
+ setTrestleDetail(null)
+ return
+ }
+
+ handleSetConstMthdList()
+ if (!trestleState?.constMthdCd) {
+ setRoofBaseList([])
+ setConstructionList([])
+ setTrestleDetail(null)
+ return
+ }
+
+ handleSetRoofBaseList()
+ if (!trestleState?.roofBaseCd) {
+ setConstructionList([])
+ setTrestleDetail(null)
+ return
+ }
+
+ handleSetConstructionList()
+ if (!trestleState?.constTp) {
+ setTrestleDetail(null)
+ return
+ }
+
+ if (!trestleState?.eavesMargin) {
+ handleSetTrestleDetailData()
+ }
+ }
+ }, [trestleState])
+
+ const handleSetTrestleList = () => {
+ setPopSpinnerStore(true)
+ getTrestleList({
+ moduleTpCd: trestleState?.moduleTpCd ?? '',
+ roofMatlCd: trestleState?.roofMatlCd ?? '',
+ raftBaseCd: trestleState?.raftBaseCd ?? '',
+ })
+ .then((res) => {
+ if (res?.data) setTrestleList(res.data)
+ setPopSpinnerStore(false)
+ })
+ .catch((e) => {
+ setPopSpinnerStore(false)
+ })
+ }
+
+ const handleSetConstMthdList = () => {
+ setPopSpinnerStore(true)
+ getTrestleList({
+ moduleTpCd: trestleState?.moduleTpCd ?? '',
+ roofMatlCd: trestleState?.roofMatlCd ?? '',
+ raftBaseCd: trestleState?.raftBaseCd ?? '',
+ trestleMkrCd: trestleState?.trestleMkrCd ?? '',
+ })
+ .then((res) => {
+ if (res?.data) setConstMthdList(res.data)
+ setPopSpinnerStore(false)
+ })
+ .catch((e) => {
+ setPopSpinnerStore(false)
+ })
+ }
+
+ const handleSetRoofBaseList = () => {
+ setPopSpinnerStore(true)
+ getTrestleList({
+ moduleTpCd: trestleState?.moduleTpCd ?? '',
+ roofMatlCd: trestleState?.roofMatlCd ?? '',
+ raftBaseCd: trestleState?.raftBaseCd ?? '',
+ trestleMkrCd: trestleState?.trestleMkrCd ?? '',
+ constMthdCd: trestleState?.constMthdCd ?? '',
+ })
+ .then((res) => {
+ if (res?.data) setRoofBaseList(res.data)
+ setPopSpinnerStore(false)
+ })
+ .catch((e) => {
+ setPopSpinnerStore(false)
+ })
+ }
+
+ const handleSetConstructionList = () => {
+ setPopSpinnerStore(true)
+ getConstructionList({
+ moduleTpCd: trestleState?.moduleTpCd ?? '',
+ roofMatlCd: trestleState?.roofMatlCd ?? '',
+ trestleMkrCd: trestleState?.trestleMkrCd ?? '',
+ constMthdCd: trestleState?.constMthdCd ?? '',
+ roofBaseCd: trestleState?.roofBaseCd ?? '',
+ illuminationTp: trestleState.illuminationTp ?? '',
+ instHt: trestleState.instHt ?? '',
+ stdWindSpeed: trestleState.stdWindSpeed ?? '',
+ stdSnowLd: trestleState.stdSnowLd ?? '',
+ inclCd: trestleState.inclCd ?? '',
+ raftBaseCd: trestleState.raftBaseCd ?? '',
+ roofPitch: Math.round(trestleState.roofPitch) ?? '',
+ })
+ .then((res) => {
+ if (res?.data) setConstructionList(res.data)
+ setPopSpinnerStore(false)
+ })
+ .catch((e) => {
+ setPopSpinnerStore(false)
+ })
+ }
+
+ const handleSetTrestleDetailData = () => {
+ setPopSpinnerStore(true)
+ getTrestleDetailList([
+ {
+ moduleTpCd: trestleState.moduleTpCd ?? '',
+ roofMatlCd: trestleState.roofMatlCd ?? '',
+ trestleMkrCd: trestleState.trestleMkrCd ?? '',
+ constMthdCd: trestleState.constMthdCd ?? '',
+ roofBaseCd: trestleState.roofBaseCd ?? '',
+ illuminationTp: trestleState.illuminationTp ?? '',
+ instHt: trestleState.instHt ?? '',
+ stdWindSpeed: trestleState.stdWindSpeed ?? '',
+ stdSnowLd: trestleState.stdSnowLd ?? '',
+ inclCd: trestleState.inclCd ?? '',
+ constTp: trestleState.constTp ?? '',
+ mixMatlNo: trestleState.mixMatlNo ?? '',
+ roofPitch: trestleState.roofPitch ?? '',
+ workingWidth: trestleState.workingWidth ?? '',
+ },
+ ])
+ .then((res) => {
+ if (res.length > 0) {
+ if (!res[0].data) return
+ setEavesMargin(res[0].data.eaveIntvl)
+ setRidgeMargin(res[0].data.ridgeIntvl)
+ setKerabaMargin(res[0].data.kerabaIntvl)
+ setTrestleDetail(res[0].data)
+
+ // dispatch({
+ // type: 'SET_TRESTLE_DETAIL',
+ // roof: {
+ // ...trestleState,
+ // eavesMargin: res[0].data.eaveIntvl,
+ // ridgeMargin: res[0].data.ridgeIntvl,
+ // kerabaMargin: res[0].data.kerabaIntvl,
+ // },
+ // })
+ }
+ setPopSpinnerStore(false)
+ })
+ .catch((e) => {
+ setPopSpinnerStore(false)
+ })
+ }
+
+ return {
+ trestleState,
+ trestleDetail,
+ dispatch,
+ raftBaseList,
+ trestleList,
+ constMthdList,
+ roofBaseList,
+ constructionList,
+ handleSetTrestleList,
+ handleSetConstMthdList,
+ handleSetRoofBaseList,
+ handleSetConstructionList,
+ handleSetTrestleDetailData,
+ lengthBase,
+ setLengthBase,
+ hajebichi,
+ setHajebichi,
+ cvrYn,
+ cvrChecked,
+ snowGdPossYn,
+ snowGdChecked,
+ eavesMargin,
+ ridgeMargin,
+ kerabaMargin,
+ setEavesMargin,
+ setRidgeMargin,
+ setKerabaMargin,
+ setCvrYn,
+ setCvrChecked,
+ setSnowGdPossYn,
+ setSnowGdChecked,
+ }
+}
diff --git a/src/hooks/module/useOrientation.js b/src/hooks/module/useOrientation.js
index 649d8f79..cf15390e 100644
--- a/src/hooks/module/useOrientation.js
+++ b/src/hooks/module/useOrientation.js
@@ -22,8 +22,8 @@ export function useOrientation() {
})
}, [])*/
- const nextStep = () => {
- if (isNaN(compasDeg)) {
+ const nextStep = (compas = compasDeg) => {
+ if (isNaN(compas)) {
setCompasDeg(0)
}
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
@@ -36,7 +36,7 @@ export function useOrientation() {
roofs.forEach((roof) => {
roof.set({
- moduleCompass: isNaN(compasDeg) ? 0 : compasDeg,
+ moduleCompass: isNaN(compas) ? 0 : compas,
})
drawDirectionArrow(roof)
})
diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js
index 48ad5913..7048e64e 100644
--- a/src/hooks/module/useTrestle.js
+++ b/src/hooks/module/useTrestle.js
@@ -10,6 +10,7 @@ import { useSwal } from '@/hooks/useSwal'
import { useContext } from 'react'
import { QcastContext } from '@/app/QcastProvider'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
+import { useMessage } from '@/hooks/useMessage'
// 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주
const MODULE_MARGIN = 10
@@ -26,6 +27,7 @@ export const useTrestle = () => {
const { getSelectedPcsItemList } = useCircuitTrestle()
const { resetCircuits } = useCircuitTrestle()
+ const { getMessage } = useMessage()
const apply = () => {
const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit)
@@ -58,7 +60,6 @@ export const useTrestle = () => {
}
const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction
if (!construction) {
- swalFire({ text: 'construction 존재안함', icon: 'error' })
return
}
@@ -131,9 +132,9 @@ export const useTrestle = () => {
surface.isChidory = isChidory
if (plvrYn === 'N' && isChidory) {
- swalFire({ text: '치조불가공법입니다.', icon: 'error' })
+ swalFire({ text: getMessage('chidory.can.not.install'), icon: 'error' })
clear()
- throw new Error('치조불가공법입니다.')
+ throw new Error(getMessage('chidory.can.not.install'))
}
surface.set({ isChidory: isChidory })
@@ -370,11 +371,16 @@ export const useTrestle = () => {
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
)
} else {
+ //C1C2C3인 경우
+ let newLeftRowsInfo = normalizeModules(rack.value.moduleTpCd, leftRowsInfo)
+
return (
- rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp &&
- rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
- Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count &&
- Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count
+ rack.value.moduleTpCd === newLeftRowsInfo.moduleTotalTp &&
+ rack.value.moduleRows === newLeftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
+ newLeftRowsInfo.rowsInfo.every((row, index) => {
+ const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
+ return rackRowCount === row.count
+ })
)
}
})?.value.racks
@@ -387,11 +393,15 @@ export const useTrestle = () => {
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
)
} else {
+ let newRightRowsInfo = normalizeModules(rack.value.moduleTpCd, rightRowsInfo)
+
return (
- rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp &&
- rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
- Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count &&
- Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count
+ rack.value.moduleTpCd === newRightRowsInfo.moduleTotalTp &&
+ rack.value.moduleRows === newRightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
+ newRightRowsInfo.rowsInfo.every((row, index) => {
+ const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
+ return rackRowCount === row.count
+ })
)
}
})?.value.racks
@@ -404,11 +414,15 @@ export const useTrestle = () => {
rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
)
} else {
+ let newCenterRowsInfo = normalizeModules(rack.value.moduleTpCd, centerRowsInfo)
+
return (
- rack.value.moduleTpCd === centerRowsInfo.moduleTotalTp &&
- rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
- Number(rack.value.moduleTpRows1) === centerRowsInfo.rowsInfo[0].count &&
- Number(rack.value.moduleTpRows2) === centerRowsInfo.rowsInfo[1].count
+ rack.value.moduleTpCd === newCenterRowsInfo.moduleTotalTp &&
+ rack.value.moduleRows === newCenterRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
+ newCenterRowsInfo.rowsInfo.every((row, index) => {
+ const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
+ return rackRowCount === row.count
+ })
)
}
})?.value.racks
@@ -499,11 +513,15 @@ export const useTrestle = () => {
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
)
} else {
+ let newLeftRowsInfo = normalizeModules(rack.value.moduleTpCd, leftRowsInfo)
+
return (
- rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp &&
- rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
- Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count &&
- Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count
+ rack.value.moduleTpCd === newLeftRowsInfo.moduleTotalTp &&
+ rack.value.moduleRows === newLeftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
+ newLeftRowsInfo.rowsInfo.every((row, index) => {
+ const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
+ return rackRowCount === row.count
+ })
)
}
})?.value.racks
@@ -575,11 +593,16 @@ export const useTrestle = () => {
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
)
} else {
+ // 변환 C1C2만 있는경우 C3 0개로 추가해준다.
+ let newRightRowsInfo = normalizeModules(rack.value.moduleTpCd, rightRowsInfo)
+
return (
- rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp &&
- rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
- Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count &&
- Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count
+ rack.value.moduleTpCd === newRightRowsInfo.moduleTotalTp &&
+ rack.value.moduleRows === newRightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
+ newRightRowsInfo.rowsInfo.every((row, index) => {
+ const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
+ return rackRowCount === row.count
+ })
)
}
})?.value.racks
@@ -634,6 +657,31 @@ export const useTrestle = () => {
return { moduleTotalTp, rowsInfo }
}
+ function normalizeModules(rackTpCd, data) {
+ // rackTpCd를 숫자를 기준으로 자른다.
+ const allModules = rackTpCd.match(/[A-Za-z]+\d+/g) || [] // 모든 모듈 유형
+
+ // 현재 존재하는 모듈 유형을 추출
+ const existingModules = data.rowsInfo.map((row) => row.moduleTpCd)
+
+ const result = { ...data, rowsInfo: [...data.rowsInfo] }
+
+ // 없는 모듈을 추가 (count: 0)
+ allModules.forEach((module) => {
+ if (!existingModules.includes(module)) {
+ result.rowsInfo.push({ moduleTpCd: module, count: 0 })
+ }
+ })
+
+ // rowsInfo를 C1, C2, C3 순서로 정렬
+ result.rowsInfo.sort((a, b) => allModules.indexOf(a.moduleTpCd) - allModules.indexOf(b.moduleTpCd))
+
+ // moduleTotalTp를 C1C2C3로 설정
+ result.moduleTotalTp = allModules.join('')
+
+ return result
+ }
+
// itemList 조회 후 estimateParam에 저장
const getEstimateData = async () => {
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
@@ -660,13 +708,18 @@ export const useTrestle = () => {
})
// trestles 배열에서 null인 경우 제거
- const params = { trestles, pcses, modules }
+
+ const dblCblTotCnt = getTotalConnectCableCnt()
+ const params = { trestles, pcses, modules, dblCblTotCnt }
//견적서 itemList 조회
const { data, data2, result } = await getQuotationItem(params)
if (result.resultCode === 'E') {
swalFire({ text: result.resultMsg, icon: 'error' })
+ clear()
+ setViewCircuitNumberTexts(true)
+ setIsGlobalLoading(false)
return
}
@@ -999,9 +1052,7 @@ export const useTrestle = () => {
if (!rackInfos) {
const maxRows = surface.trestleDetail.moduleMaxRows
const maxCols = surface.trestleDetail.moduleMaxCols
- const msg = `選択した家で設置可能
-モジュールの最大段数は${maxRows}、最大列数は${maxCols}です。
-上限より上部に取り付けたモジュールを削除してください。`
+ const msg = `段数の上限は${maxRows}段です。 上限より上の段には設置できません`
swalFire({ title: msg, type: 'alert' })
throw new Error('rackInfos is null')
}
@@ -2130,12 +2181,13 @@ export const useTrestle = () => {
const visited = new Set()
const width = Math.floor(moduleExample.width)
const height = Math.floor(moduleExample.height)
- const horizonPadding = 0 // 가로 패딩
- const verticalPadding = 0 // 세로 패딩
+ const horizonPadding = 3 // 가로 패딩
+ const verticalPadding = 7 // 세로 패딩
function isAdjacent(p1, p2) {
const dx = Math.abs(p1.x - p2.x)
const dy = Math.abs(p1.y - p2.y)
+
return (
(Math.abs(width + horizonPadding - dx) < 2 && dy < 2) ||
(dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 ||
@@ -2167,6 +2219,128 @@ export const useTrestle = () => {
return groups
}
+ function areConnected(m1, m2, surface) {
+ /*const m1Fill = m1.fill
+ const m2Fill = m2.fill
+ m1.set({ fill: 'red' })
+ m2.set({ fill: 'blue' })
+ canvas.renderAll()*/
+
+ let sizes = []
+
+ const { width: currentWidth, height: currentHeight, moduleInfo: currentModuleInfo } = m1
+ const { width: neighborWidth, height: neighborHeight, moduleInfo: neighborModuleInfo } = m2
+
+ const { moduleTpCd: currentModuleTpCd } = currentModuleInfo
+ const { moduleTpCd: neighborModuleTpCd } = neighborModuleInfo
+ const { x: m1X, y: m1Y } = m1.getCenterPoint()
+ const { x: m2X, y: m2Y } = m2.getCenterPoint()
+ sizes.push({ width: currentWidth, height: currentHeight })
+
+ if (currentModuleTpCd !== neighborModuleTpCd) {
+ sizes.push({ width: neighborWidth, height: neighborHeight })
+ }
+
+ /*m1.set({ fill: m1Fill })
+ m2.set({ fill: m2Fill })
+ canvas.renderAll()*/
+
+ return sizes.some(({ width, height }) => {
+ let maxX
+ let maxY
+ let halfMaxX
+ let halfMaxY
+ const { direction, trestleDetail } = surface
+ const { moduleIntvlHor, moduleIntvlVer } = trestleDetail
+
+ if (direction === 'south' || direction === 'north') {
+ maxX = width + moduleIntvlHor / 10
+ maxY = height + moduleIntvlVer / 10
+ halfMaxX = moduleIntvlHor / 10
+ halfMaxY = moduleIntvlVer / 10
+
+ if (currentModuleTpCd !== neighborModuleTpCd) {
+ maxX = currentWidth / 2 + neighborWidth / 2 + moduleIntvlHor / 10
+ maxY = currentHeight / 2 + neighborHeight / 2 + moduleIntvlVer / 10
+ }
+
+ // console.log(maxX, maxY, halfMaxX, halfMaxY)
+
+ if (Math.abs(m1X - m2X) < 1) {
+ return Math.abs(Math.abs(m1Y - m2Y) - maxY) < 1
+ } else if (Math.abs(m1Y - m2Y) < 1) {
+ return Math.abs(Math.abs(m1X - m2X) - maxX) < 1
+ }
+
+ return (
+ (Math.abs(m1X - m2X) < maxX - moduleIntvlHor / 10 && Math.abs(m1Y - m2Y) < maxY - moduleIntvlVer / 10) ||
+ (Math.abs(Math.abs(m1X - m2X) - maxX / 2) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY) < halfMaxY + 1) ||
+ (Math.abs(Math.abs(m1X - m2X) - maxX) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY / 2) < halfMaxY + 1)
+ )
+ } else if (direction === 'east' || direction === 'west') {
+ maxX = height + moduleIntvlHor / 10
+ maxY = width + moduleIntvlVer / 10
+ halfMaxX = moduleIntvlVer / 10
+ halfMaxY = moduleIntvlHor / 10
+
+ if (currentModuleTpCd !== neighborModuleTpCd) {
+ maxX = currentHeight / 2 + neighborHeight / 2 + moduleIntvlVer / 10
+ maxY = currentWidth / 2 + neighborWidth / 2 + moduleIntvlHor / 10
+ }
+
+ if (Math.abs(m1X - m2X) < 1) {
+ return Math.abs(Math.abs(m1Y - m2Y) - maxX) < 1
+ } else if (Math.abs(m1Y - m2Y) < 1) {
+ return Math.abs(Math.abs(m1X - m2X) - maxY) < 1
+ }
+
+ return (
+ (Math.abs(m1X - m2X) <= maxY - moduleIntvlVer / 10 && Math.abs(m1Y - m2Y) <= maxX - moduleIntvlHor / 10) ||
+ (Math.abs(Math.abs(m1X - m2X) - maxY / 2) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX) < halfMaxX + 1) ||
+ (Math.abs(Math.abs(m1X - m2X) - maxY) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX / 2) < halfMaxX + 1)
+ )
+ }
+ })
+ }
+
+ // 25-04-02 추가
+ // 그룹화
+ function groupPoints(modules, surface) {
+ const groups = []
+ const visited = new Set()
+
+ for (const point of modules) {
+ const { x: pointX, y: pointY } = point.getCenterPoint()
+ const key = `${pointX},${pointY}`
+ if (visited.has(key)) continue
+
+ const queue = [point]
+ const group = []
+
+ while (queue.length > 0) {
+ const current = queue.shift()
+ const { x: currentX, y: currentY } = current.getCenterPoint()
+ const currentKey = `${currentX},${currentY}`
+ if (visited.has(currentKey)) continue
+
+ visited.add(currentKey)
+ group.push(current)
+
+ for (const neighbor of modules) {
+ const { x: neighborX, y: neighborY } = neighbor.getCenterPoint()
+ const neighborKey = `${neighborX},${neighborY}`
+ if (!visited.has(neighborKey) && areConnected(current, neighbor, surface)) {
+ queue.push(neighbor)
+ }
+ }
+ }
+
+ groups.push(group)
+ }
+
+ return groups
+ }
+
// 각도에 따른 길이 반환
function getTrestleLength(length, degree) {
if (roofSizeSet !== 1) {
@@ -2864,5 +3038,82 @@ export const useTrestle = () => {
return surfaces.every((surface) => surface.isComplete)
}
- return { apply, getTrestleParams, clear, setViewCircuitNumberTexts, getEstimateData, setAllModuleSurfaceIsComplete, isAllComplete }
+ const groupByType = (originArr = []) => {
+ const grouped = {}
+ const newArr = [...originArr]
+
+ // 타입별로 객체들을 분류
+ for (const item of newArr) {
+ if (!grouped[item.circuitNumber]) {
+ grouped[item.circuitNumber] = []
+ }
+ grouped[item.circuitNumber].push(item)
+ }
+
+ // 객체를 배열로 변환
+ return Object.values(grouped)
+ }
+
+ function groupByCircuitAndSurface(arr) {
+ const circuitGroups = {}
+
+ for (const item of arr) {
+ const { circuitNumber, surfaceId } = item
+ if (!circuitGroups[circuitNumber]) {
+ circuitGroups[circuitNumber] = new Map()
+ }
+
+ const surfaceMap = circuitGroups[circuitNumber]
+ const key = surfaceId
+
+ if (!surfaceMap.has(key)) {
+ surfaceMap.set(key, [])
+ }
+
+ surfaceMap.get(key).push(item)
+ }
+
+ // 결과: circuitNumber별 surface 그룹 수
+ const result = {}
+ for (const [circuit, surfaceMap] of Object.entries(circuitGroups)) {
+ result[circuit] = surfaceMap.size
+ }
+
+ return result
+ }
+
+ // 양단 케이블 구하는 공식
+ const getTotalConnectCableCnt = () => {
+ let cnt = 0
+ const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
+ const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
+ surfaces.forEach((surface) => {
+ const modules = surface.modules
+ const groups = groupByType(modules)
+ groups.forEach((group) => {
+ const result = groupPoints(group, surface)
+ cnt += result.length - 1
+ })
+ })
+
+ const groupByCircuitAndSurfaceCnt = groupByCircuitAndSurface(modules)
+
+ Object.keys(groupByCircuitAndSurfaceCnt).forEach((key) => {
+ cnt += groupByCircuitAndSurfaceCnt[key] - 1
+ })
+
+ return cnt
+ }
+
+ return {
+ apply,
+ getTrestleParams,
+ clear,
+ setViewCircuitNumberTexts,
+ getEstimateData,
+ setAllModuleSurfaceIsComplete,
+ isAllComplete,
+ groupCoordinates,
+ groupPoints,
+ }
}
diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js
index 76f42075..7bde5638 100644
--- a/src/hooks/option/useCanvasSetting.js
+++ b/src/hooks/option/useCanvasSetting.js
@@ -123,6 +123,7 @@ export function useCanvasSetting(executeEffect = true) {
const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */
const resetSelectedModules = useResetRecoilState(selectedModuleState) /* 선택된 모듈 */
+ const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [raftCodes, setRaftCodes] = useState([]) /* 서까래 정보 */
@@ -526,7 +527,10 @@ export function useCanvasSetting(executeEffect = true) {
/** 모듈 선택 데이터 초기화 */
resetModuleSelectionData()
- moduleSelectedDataTrigger({ common: {}, module: {}, roofConstructions: [] })
+ //1번 초기화
+ orientationTrigger({ compasDeg: 0, common: {}, module: {} })
+ //2번 초기화
+ moduleSelectedDataTrigger({ roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
if (!isModuleExist) {
resetSelectedModules()
diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js
index 6023bce8..37d1aa7a 100644
--- a/src/hooks/roofcover/useAuxiliaryDrawing.js
+++ b/src/hooks/roofcover/useAuxiliaryDrawing.js
@@ -84,7 +84,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
// innerLines가 있을경우 삭제
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofs.length === 0) {
- swalFire({ text: '지붕형상이 없습니다.' })
+ swalFire({ text: getMessage('roof.line.not.found') })
closePopup(id)
return
}
diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js
index ff0073ae..37b8017c 100644
--- a/src/hooks/roofcover/useEavesGableEdit.js
+++ b/src/hooks/roofcover/useEavesGableEdit.js
@@ -52,7 +52,7 @@ export function useEavesGableEdit(id) {
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) {
- swalFire({ text: '외벽선이 없습니다.' })
+ swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id)
}
}, [])
diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js
index bced656b..18a07cf8 100644
--- a/src/hooks/roofcover/useOuterLineWall.js
+++ b/src/hooks/roofcover/useOuterLineWall.js
@@ -32,6 +32,7 @@ import { outlineDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup'
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
import Big from 'big.js'
+import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting'
//외벽선 그리기
export function useOuterLineWall(id, propertiesId) {
@@ -256,7 +257,7 @@ export function useOuterLineWall(id, propertiesId) {
canvas?.renderAll()
setOuterLineFix(true)
closePopup(id)
- addPopup(propertiesId, 1,
)
+ addPopup(propertiesId, 1,
)
}
if (points.length < 3) {
diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js
index 536a2127..d9c2073c 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -1,6 +1,6 @@
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState } from '@/store/canvasAtom'
-import { useEffect, useRef, useState } from 'react'
+import { useContext, useEffect, useRef, useState } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon'
@@ -11,6 +11,8 @@ import {
roofDisplaySelector,
roofMaterialsSelector,
selectedRoofMaterialSelector,
+ settingModalFirstOptionsState,
+ corridorDimensionSelector,
} from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common'
@@ -26,6 +28,9 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { outerLinePointsState } from '@/store/outerLineAtom'
+import { QcastContext } from '@/app/QcastProvider'
+import { usePlan } from '@/hooks/usePlan'
+import { roofsState } from '@/store/roofAtom'
export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState)
@@ -49,11 +54,13 @@ export function useRoofAllocationSetting(id) {
const { get, post } = useAxios(globalLocaleState)
const { getMessage } = useMessage()
const { swalFire } = useSwal()
-
+ const { setIsGlobalLoading } = useContext(QcastContext)
const { setSurfaceShapePattern } = useRoofFn()
-
+ const { saveCanvas } = usePlan()
+ const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const resetPoints = useResetRecoilState(outerLinePointsState)
+ const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
useEffect(() => {
/** 배치면 초기설정에서 선택한 지붕재 배열 설정 */
@@ -68,30 +75,19 @@ export function useRoofAllocationSetting(id) {
roof.innerLines.forEach((line) => {
/** 실측값이 없는 경우 라인 두께 4로 설정 */
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
- line.set({
- strokeWidth: 4,
- stroke: 'black',
- selectable: true,
- })
+ line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
}
/** 현재 선택된 라인인 경우 라인 두께 2로 설정 */
if (editingLines.includes(line)) {
- line.set({
- strokeWidth: 2,
- stroke: 'black',
- selectable: true,
- })
+ line.set({ strokeWidth: 2, stroke: 'black', selectable: true })
}
})
})
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
- currentObject.set({
- strokeWidth: 4,
- stroke: '#EA10AC',
- })
+ currentObject.set({ strokeWidth: 4, stroke: '#EA10AC' })
}
}, [currentObject])
@@ -99,7 +95,7 @@ export function useRoofAllocationSetting(id) {
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofBases.length === 0) {
- swalFire({ text: '할당할 지붕이 없습니다.' })
+ swalFire({ text: getMessage('roofAllocation.not.found'), icon: 'warning' })
closePopup(id)
}
@@ -112,9 +108,7 @@ export function useRoofAllocationSetting(id) {
*/
const fetchBasicSettings = async (planNo) => {
try {
- await get({
- url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}`,
- }).then((res) => {
+ await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}` }).then((res) => {
let roofsArray = {}
if (res.length > 0) {
@@ -184,11 +178,7 @@ export function useRoofAllocationSetting(id) {
selectedRoofMaterial: selectRoofs.find((roof) => roof.selected),
})
- setBasicInfo({
- planNo: '' + res[0].planNo,
- roofSizeSet: '' + res[0].roofSizeSet,
- roofAngleSet: '' + res[0].roofAngleSet,
- })
+ setBasicInfo({ planNo: '' + res[0].planNo, roofSizeSet: '' + res[0].roofSizeSet, roofAngleSet: '' + res[0].roofAngleSet })
})
} catch (error) {
console.error('Data fetching error:', error)
@@ -200,6 +190,7 @@ export function useRoofAllocationSetting(id) {
*/
const basicSettingSave = async () => {
try {
+ setIsGlobalLoading(true)
const patternData = {
objectNo: correntObjectNo,
planNo: Number(basicSetting.planNo),
@@ -222,6 +213,8 @@ export function useRoofAllocationSetting(id) {
await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
+ setIsGlobalLoading(false)
+ saveCanvas(false)
})
//Recoil 설정
@@ -242,36 +235,48 @@ export function useRoofAllocationSetting(id) {
swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.exceed.count') })
return
}
- setCurrentRoofList([
- ...currentRoofList,
- {
- ...currentRoofMaterial,
- selected: false,
- id: currentRoofMaterial.roofMatlCd,
- name: currentRoofMaterial.roofMatlNm,
- index: currentRoofList.length,
- },
- ])
+
+ const originCurrentRoofList = currentRoofList.map((roof) => {
+ return { ...roof, selected: false }
+ })
+ originCurrentRoofList.push({
+ ...currentRoofMaterial,
+ selected: true,
+ id: currentRoofMaterial.roofMatlCd,
+ name: currentRoofMaterial.roofMatlNm,
+ index: currentRoofList.length,
+ })
+
+ setCurrentRoofList(originCurrentRoofList)
}
/**
* 지붕재 삭제
*/
const onDeleteRoofMaterial = (idx) => {
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+
+ for (let i = 0; i < roofs.length; i++) {
+ if (roofs[i].roofMaterial?.index === idx) {
+ swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.material.can.not.delete') })
+ return
+ }
+ }
+
const isSelected = currentRoofList[idx].selected
const newRoofList = JSON.parse(JSON.stringify(currentRoofList)).filter((_, index) => index !== idx)
if (isSelected) {
newRoofList[0].selected = true
}
setCurrentRoofList(newRoofList)
+ setRoofsStore(newRoofList)
+ setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
}
/**
* 선택한 지붕재로 할당
*/
const handleSave = () => {
- basicSettingSave()
-
/**
* 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
*/
@@ -280,6 +285,7 @@ export function useRoofAllocationSetting(id) {
} else {
apply()
resetPoints()
+ basicSettingSave()
}
}
@@ -287,23 +293,47 @@ export function useRoofAllocationSetting(id) {
* 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우
*/
const handleSaveContext = () => {
- basicSettingSave()
const newRoofList = currentRoofList.map((roof, idx) => {
+ if (roof.index !== idx) {
+ // 기존 저장된 지붕재의 index 수정
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === roof.index)
+ roofs.forEach((roof) => {
+ setSurfaceShapePattern(roof, roofDisplay.column, false, { ...roof, index: idx }, true)
+ })
+ }
+
return { ...roof, index: idx, raft: roof.raft ? roof.raft : roof.raftBaseCd }
})
+
setBasicSetting((prev) => {
- return {
- ...prev,
- selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
- }
+ return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
})
setRoofList(newRoofList)
+ setRoofMaterials(newRoofList)
+ setRoofsStore(newRoofList)
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true)
drawDirectionArrow(currentObject)
modifyModuleSelectionData()
closeAll()
+ basicSettingSave()
+ setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
+ }
+
+ /**
+ * 기존 세팅된 지붕에 지붕재 내용을 바뀐 내용으로 수정
+ * @param newRoofMaterials
+ */
+ const setRoofMaterials = (newRoofMaterials) => {
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ newRoofMaterials.forEach((roofMaterial) => {
+ const index = roofMaterial.index
+ const tempRoofs = roofs.filter((roof) => roof.roofMaterial?.index === index)
+ tempRoofs.forEach((roof) => {
+ setSurfaceShapePattern(roof, roofDisplay.column, false, roofMaterial)
+ })
+ })
}
/**
@@ -313,11 +343,7 @@ export function useRoofAllocationSetting(id) {
if (!checkInnerLines()) {
apply()
} else {
- swalFire({
- type: 'alert',
- icon: 'error',
- text: getMessage('실제치수를 입력해 주세요.'),
- })
+ swalFire({ type: 'alert', icon: 'error', text: getMessage('실제치수를 입력해 주세요.') })
}
}
@@ -332,11 +358,7 @@ export function useRoofAllocationSetting(id) {
if (roof.separatePolygon.length === 0) {
roof.innerLines.forEach((line) => {
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
- line.set({
- strokeWidth: 4,
- stroke: 'black',
- selectable: true,
- })
+ line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
result = true
}
})
@@ -361,6 +383,7 @@ export function useRoofAllocationSetting(id) {
splitPolygonWithLines(roofBase)
}
} catch (e) {
+ console.log(e)
return
}
@@ -383,10 +406,7 @@ export function useRoofAllocationSetting(id) {
})
setBasicSetting((prev) => {
- return {
- ...prev,
- selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
- }
+ return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
})
setRoofList(newRoofList)
@@ -394,9 +414,7 @@ export function useRoofAllocationSetting(id) {
roofs.forEach((roof) => {
if (roof.isFixed) return
- roof.set({
- isFixed: true,
- })
+ roof.set({ isFixed: true })
/** 모양 패턴 설정 */
setSurfaceShapePattern(
@@ -408,6 +426,8 @@ export function useRoofAllocationSetting(id) {
drawDirectionArrow(roof)
})
+ setRoofMaterials(newRoofList)
+ setRoofsStore(newRoofList)
/** 외곽선 삭제 */
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine')
removeTargets.forEach((obj) => {
@@ -416,9 +436,12 @@ export function useRoofAllocationSetting(id) {
setEditingLines([])
closeAll()
setSelectedMenu('surface')
+ //지붕면 완성 후 실측치 로 보이도록 수정
+ setCorridorDimension(1)
/** 모듈 선택 데이터 초기화 */
- modifyModuleSelectionData()
+ // modifyModuleSelectionData()
+ setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
}
/**
@@ -431,10 +454,7 @@ export function useRoofAllocationSetting(id) {
if (id === line.id) {
setEditingLines([...editingLines.filter((editLine) => editLine.id !== line.id), line])
line.attributes.actualSize = size
- line.set({
- strokeWidth: 2,
- stroke: 'black',
- })
+ line.set({ strokeWidth: 2, stroke: 'black' })
}
})
})
@@ -539,7 +559,7 @@ export function useRoofAllocationSetting(id) {
* 모듈 선택에서 선택한 데이터 초기화
*/
const modifyModuleSelectionData = () => {
- if (moduleSelectionData.roofConstructions.length > 0) {
+ if (moduleSelectionData.roofConstructions?.length > 0) {
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: [] })
moduleSelectedDataTrigger({ ...moduleSelectionData, roofConstructions: [] })
}
diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js
index 33d5d953..241d2fac 100644
--- a/src/hooks/roofcover/useRoofShapePassivitySetting.js
+++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js
@@ -28,7 +28,7 @@ export function useRoofShapePassivitySetting(id) {
const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { drawRoofPolygon } = useMode()
- const { addPolygonByLines } = usePolygon()
+ const { addPolygonByLines, addLengthText } = usePolygon()
const currentObject = useRecoilValue(currentObjectState)
const offsetRef = useRef(null)
const pitchRef = useRef(null)
@@ -51,7 +51,7 @@ export function useRoofShapePassivitySetting(id) {
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) {
- swalFire({ text: '외벽선이 없습니다.' })
+ swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id)
return
}
@@ -248,6 +248,7 @@ export function useRoofShapePassivitySetting(id) {
// 완료 한 경우에는 지붕까지 그려줌
addPitchTextsByOuterLines()
const roof = drawRoofPolygon(wall)
+ addLengthText(roof)
}
canvas.renderAll()
diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js
index 1167aac9..192d8cc0 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -191,7 +191,7 @@ export function useRoofShapeSetting(id) {
let direction
if (outerLines.length < 2) {
- swalFire({ text: '외벽선이 없습니다.', icon: 'error' })
+ swalFire({ text: getMessage('wall.line.not.found') })
return
}
@@ -231,6 +231,21 @@ export function useRoofShapeSetting(id) {
pitch: pitchRef.current,
onlyOffset: true,
}
+
+ switch (line.direction) {
+ case 'bottom':
+ direction = 'east'
+ break
+ case 'top':
+ direction = 'west'
+ break
+ case 'left':
+ direction = 'south'
+ break
+ case 'right':
+ direction = 'north'
+ break
+ }
}
})
}
@@ -716,6 +731,7 @@ export function useRoofShapeSetting(id) {
type: LINE_TYPE.WALLLINE.SHED,
pitch: shedPitchRef.current,
width: shedWidth / 10,
+ offset: shedWidth / 10,
}
selectedLine.attributes = { ...attributes, isFixed: true }
addPitchText(currentObject)
diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js
index 01fbec09..245a7f15 100644
--- a/src/hooks/roofcover/useWallLineOffsetSetting.js
+++ b/src/hooks/roofcover/useWallLineOffsetSetting.js
@@ -60,7 +60,7 @@ export function useWallLineOffsetSetting(id) {
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (outerLines.length === 0) {
- swalFire({ text: '외벽선이 없습니다.' })
+ swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id)
return
}
diff --git a/src/hooks/surface/usePlacementShapeDrawing.js b/src/hooks/surface/usePlacementShapeDrawing.js
index b1e92f90..db0859cf 100644
--- a/src/hooks/surface/usePlacementShapeDrawing.js
+++ b/src/hooks/surface/usePlacementShapeDrawing.js
@@ -32,6 +32,7 @@ import {
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup'
+import { useSurfaceShapeBatch } from './useSurfaceShapeBatch'
import { roofDisplaySelector } from '@/store/settingAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
@@ -50,6 +51,8 @@ export function usePlacementShapeDrawing(id) {
const { addPolygonByLines, drawDirectionArrow } = usePolygon()
const { tempGridMode } = useTempGrid()
const { setSurfaceShapePattern } = useRoofFn()
+ const { changeSurfaceLineType } = useSurfaceShapeBatch({})
+
const canvasSetting = useRecoilValue(canvasSettingState)
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
@@ -253,11 +256,14 @@ export function usePlacementShapeDrawing(id) {
setPoints([])
canvas?.renderAll()
- if (+canvasSetting?.roofSizeSet === 3) {
- closePopup(id)
- return
- }
- addPopup(id, 1,
, false)
+ // if (+canvasSetting?.roofSizeSet === 3) {
+ // closePopup(id)
+ // return
+ // }
+ // addPopup(id, 1,
, false)
+
+ changeSurfaceLineType(roof)
+ closePopup(id)
}
if (points.length < 3) {
diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js
index 1b0597b6..16f68668 100644
--- a/src/hooks/surface/useSurfaceShapeBatch.js
+++ b/src/hooks/surface/useSurfaceShapeBatch.js
@@ -3,7 +3,7 @@
import { useEffect } from 'react'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasSettingState, canvasState, currentCanvasPlanState, globalPitchState } from '@/store/canvasAtom'
-import { MENU, POLYGON_TYPE } from '@/common/common'
+import { MENU, POLYGON_TYPE, LINE_TYPE } from '@/common/common'
import { getIntersectionPoint, toFixedWithoutRounding } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon'
@@ -111,7 +111,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
name: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH_TEMP,
- flipX: xInversion !== yInversion,
+ // flipX: xInversion !== yInversion,
// angle: xInversion && yInversion ? Math.abs((rotate + 180) % 360) : Math.abs(rotate),
// angle: rotate,
originX: 'center',
@@ -120,6 +120,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
}
obj = new QPolygon(points, options)
+
let imageRotate = 0
if (xInversion && !yInversion) {
if (rotate % 180 === 0 || rotate < 0) {
@@ -148,7 +149,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} else {
imageRotate = (rotate + 360) % 360
}
- obj.set({ angle: imageRotate })
+ obj.set({ angle: imageRotate, flipX: xInversion !== yInversion })
obj.setCoords() //좌표 변경 적용
canvas?.add(obj)
@@ -158,6 +159,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false
+ const { xInversion, yInversion } = surfaceRefs
canvas?.remove(obj)
//각도 추가
@@ -178,6 +180,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
}
//회전, flip등이 먹은 기준으로 새로생성
+ // const batchSurface = addPolygon(reorderedPoints, {
const batchSurface = addPolygon(obj.getCurrentPoints(), {
fill: 'transparent',
stroke: 'red',
@@ -196,18 +199,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
pitch: globalPitch,
surfaceId: surfaceId,
direction: direction,
+ isXInversion: xInversion,
+ isYInversion: yInversion,
})
canvas.setActiveObject(batchSurface)
setSurfaceShapePattern(batchSurface, roofDisplay.column)
drawDirectionArrow(batchSurface)
- // if (setIsHidden) setIsHidden(false)
-
// closePopup(id)
initEvent()
- if (+canvasSetting?.roofSizeSet === 3) return
- const popupId = uuidv4()
- addPopup(popupId, 2, )
+ // if (+canvasSetting?.roofSizeSet === 3) return
+ // const popupId = uuidv4()
+ // addPopup(popupId, 2, )
+
+ // console.log('xInversion', xInversion) //상하반전
+ // console.log('yInversion', yInversion) //좌우반전
+
+ changeSurfaceLineType(batchSurface)
+
+ if (setIsHidden) setIsHidden(false)
})
} else {
if (setIsHidden) setIsHidden(false)
@@ -488,18 +498,18 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
}
case 10: {
points = [
- { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
- { 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, y: pointer.y + length4 / 2 },
+ { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
{
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, y: pointer.y + length4 / 2 - length5 },
]
break
}
@@ -613,27 +623,27 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
}
case 14: {
points = [
- { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 },
- { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 },
- { 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, y: pointer.y + length4 / 2 },
+ { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 },
{
x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2,
y: pointer.y + length4 / 2 - length4 + length5,
},
+ { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 },
+ { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 },
+ { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
]
break
}
case 15: {
points = [
- { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 },
{ x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
- { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) },
- { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
+ { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 },
{ x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 },
+ { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
+ { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) },
]
break
}
@@ -641,28 +651,28 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
case 16: {
points = [
{
- x: pointer.x - length1 / 2,
- y: pointer.y + length3 / 2,
+ x: pointer.x - length1 / 2 + (length1 - length2) / 2,
+ y: pointer.y + length3 / 2 - (length3 - length4) - length4,
},
{
x: pointer.x - length1 / 2 + (length1 - length2) / 2,
y: pointer.y + length3 / 2 - (length3 - length4),
},
{
- x: pointer.x - length1 / 2 + (length1 - length2) / 2,
- y: pointer.y + length3 / 2 - (length3 - length4) - length4,
+ x: pointer.x - length1 / 2,
+ y: pointer.y + length3 / 2,
},
{
- x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2,
- y: pointer.y + length3 / 2 - (length3 - length4) - length4,
+ x: pointer.x - length1 / 2 + length1,
+ y: pointer.y + length3 / 2,
},
{
x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2,
y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4,
},
{
- x: pointer.x - length1 / 2 + length1,
- y: pointer.y + length3 / 2,
+ x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2,
+ y: pointer.y + length3 / 2 - (length3 - length4) - length4,
},
]
break
@@ -673,25 +683,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이
points = [
- {
- x: pointer.x - length1 / 2 + length1,
- y: pointer.y + length3 / 2,
- },
{
x: pointer.x - length1 / 2,
y: pointer.y + length3 / 2,
},
{
- x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)),
- y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)),
+ x: pointer.x - length1 / 2 + length1,
+ y: pointer.y + length3 / 2,
+ },
+ {
+ x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)),
+ y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)),
},
{
x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2,
y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)),
},
{
- x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)),
- y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)),
+ x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)),
+ y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)),
},
]
break
@@ -1066,45 +1076,294 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
canvas?.renderAll()
}
- const updateFlippedPoints = (polygon) => {
- if (!(polygon instanceof fabric.Polygon)) {
- console.error('The object is not a Polygon.')
- return
- }
+ /**
+ * 면형상 작도시 라인 속성 넣는 로직
+ * 폴리곤으로 보면 직선방향에 따라 아래쪽인지 윗쪽인지 판단이 가능하다고 생각하여
+ * south -> 밑면은 무조건 right direction이라 가정하고 작업함 좌우반전시 반대로 그려지는 경우도 생기지만 그럴땐 흐름방향에 따라 최대값(최소값)을 찾아
+ * 해당 하는 흐름에 맞게 변경함
+ * @param { } polygon
+ */
- const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon
+ //폴리곤, 상하반전, 좌우반전
+ const changeSurfaceLineType = (polygon) => {
+ const { isXInversion, isYInversion } = 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 }
+ polygon.lines.forEach((line) => {
+ line.attributes.type = LINE_TYPE.WALLLINE.GABLE
})
- // flipX, flipY를 초기화
- polygon.flipX = false
- polygon.flipY = false
+ const directionConfig = {
+ south: { evaesDirection: 'right', ridgeDirection: 'left', coord1: 'y1', coord2: 'y2' },
+ north: { evaesDirection: 'left', ridgeDirection: 'right', coord1: 'y1', coord2: 'y2' },
+ east: { evaesDirection: 'top', ridgeDirection: 'bottom', coord1: 'x1', coord2: 'x2' },
+ west: { evaesDirection: 'bottom', ridgeDirection: 'top', coord1: 'x1', coord2: 'x2' },
+ }
- // points 업데이트
- polygon.set({ points: newPoints })
- polygon.setCoords()
+ const { evaesDirection, ridgeDirection, coord1, coord2 } = directionConfig[polygon.direction] || directionConfig.west
- return polygon
+ polygon.lines.forEach((line) => {
+ if (line[coord1] === line[coord2]) {
+ if (line.direction === evaesDirection) {
+ line.attributes.type = LINE_TYPE.WALLLINE.EAVES
+ } else if (line.direction === ridgeDirection) {
+ line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
+ }
+ }
+ })
+
+ /**
+ * 진짜 처마 라인인지 확인하는 로직 -> 특정 모양에 따라 처마가 없는 경우가 있는데 위에 로직으로는
+ * 용마루도 처마로 만들어서 재보정
+ */
+ //직선 찾는 로직
+ const maxLine = polygon.lines.filter((line) => line[coord1] === line[coord2])
+
+ if (maxLine.length > 0) {
+ const maxLineSorted = maxLine.reduce((a, b) => {
+ return (polygon.direction === 'south' || polygon.direction === 'east' ? b : a)[coord1] >
+ (polygon.direction === 'south' || polygon.direction === 'east' ? a : b)[coord1]
+ ? b
+ : a
+ })
+
+ //정렬된 폴리곤이 아니면(대각선이 존재하는 폴리곤일때)
+ if (!polygon.isSortedPoints) {
+ //좌우 반전을 했으면 반대로 정의함
+ if (isYInversion || isXInversion) {
+ polygon.lines.forEach((line) => {
+ if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
+ line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
+ } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) {
+ line.attributes.type = LINE_TYPE.WALLLINE.EAVES
+ }
+ })
+ }
+ }
+
+ if (maxLine.length === 1) {
+ const maxLineCoord = polygon.lines.reduce((a, b) => {
+ return (polygon.direction === 'south' || polygon.direction === 'east' ? b : a)[coord1] >
+ (polygon.direction === 'south' || polygon.direction === 'east' ? a : b)[coord1]
+ ? b
+ : a
+ })
+
+ const isRealEavesLine = polygon.lines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
+ if (isRealEavesLine.length > 0) {
+ isRealEavesLine.forEach((line) => {
+ if (polygon.direction === 'south' || polygon.direction === 'north') {
+ const targetCoord =
+ polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2)
+ const realLineCoord = polygon.direction === 'south' ? Math.max(line.y1, line.y2) : Math.min(line.y1, line.y2)
+
+ if (targetCoord !== realLineCoord) {
+ line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
+ }
+ } else if (polygon.direction === 'east' || polygon.direction === 'west') {
+ const targetCoord =
+ polygon.direction === 'east' ? Math.max(maxLineCoord.x1, maxLineCoord.x2) : Math.min(maxLineCoord.x1, maxLineCoord.x2)
+ const realLineCoord = polygon.direction === 'east' ? Math.max(line.x1, line.x2) : Math.min(line.x1, line.x2)
+
+ if (targetCoord !== realLineCoord) {
+ line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
+ }
+ }
+ })
+ }
+ }
+ }
+ }
+
+ function findCentroid(points) {
+ let sumX = 0,
+ sumY = 0
+ for (let i = 0; i < points.length; i++) {
+ sumX += points[i].x
+ sumY += points[i].y
+ }
+ return { x: sumX / points.length, y: sumY / points.length }
+ }
+
+ // 도형의 포인트를 왼쪽부터 반시계 방향으로 정렬하는 함수
+ /**
+ * 다각형의 점들을 시계 반대 방향으로 정렬하는 함수
+ * @param {Array} points - {x, y} 좌표 객체 배열
+ * @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
+ * @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
+ */
+ function orderPointsCounterClockwise(points, startPoint = null) {
+ if (points.length <= 3) {
+ return points // 점이 3개 이하면 이미 다각형의 모든 점이므로 그대로 반환
+ }
+
+ // 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
+ let start = startPoint
+ if (!start) {
+ start = points[0]
+ for (let i = 1; i < points.length; i++) {
+ if (points[i].x < start.x || (points[i].x === start.x && points[i].y < start.y)) {
+ start = points[i]
+ }
+ }
+ }
+
+ // 다각형의 중심점 계산
+ let centerX = 0,
+ centerY = 0
+ for (let i = 0; i < points.length; i++) {
+ centerX += points[i].x
+ centerY += points[i].y
+ }
+ centerX /= points.length
+ centerY /= points.length
+
+ // 시작점에서 시계 반대 방향으로 각도 계산
+ let angles = []
+ for (let i = 0; i < points.length; i++) {
+ // 시작점은 제외
+ if (points[i] === start) continue
+
+ // 시작점을 기준으로 각 점의 각도 계산
+ let angle = Math.atan2(points[i].y - start.y, points[i].x - start.x)
+
+ // 각도가 음수면 2π를 더해 0~2π 범위로 변환
+ if (angle < 0) angle += 2 * Math.PI
+
+ angles.push({
+ point: points[i],
+ angle: angle,
+ })
+ }
+
+ // 각도에 따라 정렬 (시계 반대 방향)
+ angles.sort((a, b) => a.angle - b.angle)
+
+ // 정렬된 배열 생성 (시작점을 첫 번째로)
+ let orderedPoints = [start]
+ for (let i = 0; i < angles.length; i++) {
+ orderedPoints.push(angles[i].point)
+ }
+
+ return orderedPoints
+ }
+
+ /**
+ * 특정 점에서 시작하여 시계 반대 방향으로 다음 점을 찾는 함수
+ * @param {Object} currentPoint - 현재 점 {x, y}
+ * @param {Array} points - 모든 점들의 배열
+ * @param {Array} visited - 방문한 점들의 인덱스 배열
+ * @param {Object} prevVector - 이전 벡터 방향 (첫 호출에서는 null)
+ * @returns {Object} 다음 점의 인덱스와 객체
+ */
+ function findNextCounterClockwisePoint(currentPoint, points, visited, prevVector = null) {
+ let minAngle = Infinity
+ let nextIndex = -1
+
+ // 이전 벡터가 없으면 (첫 점인 경우) 아래쪽을 향하는 벡터 사용
+ if (!prevVector) {
+ prevVector = { x: 0, y: -1 }
+ }
+
+ for (let i = 0; i < points.length; i++) {
+ // 이미 방문했거나 현재 점이면 건너뜀
+ if (visited.includes(i) || (points[i].x === currentPoint.x && points[i].y === currentPoint.y)) {
+ continue
+ }
+
+ // 현재 점에서 다음 후보 점으로의 벡터
+ let vector = {
+ x: points[i].x - currentPoint.x,
+ y: points[i].y - currentPoint.y,
+ }
+
+ // 벡터의 크기
+ let magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y)
+
+ // 단위 벡터로 정규화
+ vector.x /= magnitude
+ vector.y /= magnitude
+
+ // 이전 벡터와 현재 벡터 사이의 각도 계산 (내적 사용)
+ let dotProduct = prevVector.x * vector.x + prevVector.y * vector.y
+ let crossProduct = prevVector.x * vector.y - prevVector.y * vector.x
+
+ // 각도 계산 (atan2 사용)
+ let angle = Math.atan2(crossProduct, dotProduct)
+
+ // 시계 반대 방향으로 가장 작은 각도를 가진 점 찾기
+ // 각도가 음수면 2π를 더해 0~2π 범위로 변환
+ if (angle < 0) angle += 2 * Math.PI
+
+ if (angle < minAngle) {
+ minAngle = angle
+ nextIndex = i
+ }
+ }
+
+ return nextIndex !== -1 ? { index: nextIndex, point: points[nextIndex] } : null
+ }
+
+ /**
+ * 다각형의 점들을 시계 반대 방향으로 추적하는 함수
+ * @param {Array} points - {x, y} 좌표 객체 배열
+ * @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
+ * @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
+ */
+ function tracePolygonCounterClockwise(points, startPoint = null) {
+ if (points.length <= 3) {
+ return orderPointsCounterClockwise(points, startPoint)
+ }
+
+ // 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
+ let startIndex = 0
+ if (!startPoint) {
+ for (let i = 1; i < points.length; i++) {
+ if (points[i].x < points[startIndex].x || (points[i].x === points[startIndex].x && points[i].y < points[startIndex].y)) {
+ startIndex = i
+ }
+ }
+ startPoint = points[startIndex]
+ } else {
+ // 시작점이 제공된 경우 해당 점의 인덱스 찾기
+ for (let i = 0; i < points.length; i++) {
+ if (points[i].x === startPoint.x && points[i].y === startPoint.y) {
+ startIndex = i
+ break
+ }
+ }
+ }
+
+ // 결과 배열 초기화
+ let orderedPoints = [startPoint]
+ let visited = [startIndex]
+
+ let currentPoint = startPoint
+ let prevVector = null
+
+ // 모든 점을 방문할 때까지 반복
+ while (visited.length < points.length) {
+ let next = findNextCounterClockwisePoint(currentPoint, points, visited, prevVector)
+
+ if (!next) break // 더 이상 찾을 점이 없으면 종료
+
+ orderedPoints.push(next.point)
+ visited.push(next.index)
+
+ // 이전 벡터 업데이트 (현재 점에서 다음 점으로의 벡터)
+ prevVector = {
+ x: next.point.x - currentPoint.x,
+ y: next.point.y - currentPoint.y,
+ }
+
+ // 벡터 정규화
+ let magnitude = Math.sqrt(prevVector.x * prevVector.x + prevVector.y * prevVector.y)
+ prevVector.x /= magnitude
+ prevVector.y /= magnitude
+
+ currentPoint = next.point
+ }
+
+ return orderedPoints
}
return {
@@ -1115,5 +1374,6 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
changeSurfaceLinePropertyEvent,
changeSurfaceLineProperty,
changeSurfaceLinePropertyReset,
+ changeSurfaceLineType,
}
}
diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js
index ed24387e..4204dc71 100644
--- a/src/hooks/useCanvasEvent.js
+++ b/src/hooks/useCanvasEvent.js
@@ -1,9 +1,10 @@
import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
-import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom'
+import { canvasSizeState, canvasState, canvasZoomState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon'
import { fontSelector } from '@/store/fontAtom'
+import { MENU } from '@/common/common'
// 캔버스에 필요한 이벤트
export function useCanvasEvent() {
@@ -13,11 +14,16 @@ export function useCanvasEvent() {
const canvasSize = useRecoilValue(canvasSizeState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
+ const currentMenu = useRecoilValue(currentMenuState)
useEffect(() => {
canvas?.setZoom(canvasZoom / 100)
}, [canvasZoom])
+ useEffect(() => {
+ attachDefaultEventOnCanvas()
+ }, [currentMenu])
+
// 기본적인 이벤트 필요시 추가
const attachDefaultEventOnCanvas = () => {
removeEventOnCanvas()
@@ -198,7 +204,7 @@ export function useCanvasEvent() {
if (selected?.length > 0) {
selected.forEach((obj) => {
- if (obj.type === 'QPolygon' && obj.name !== 'module') {
+ if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
obj.set({ stroke: 'red' })
}
})
@@ -211,7 +217,7 @@ export function useCanvasEvent() {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
- if (obj.type === 'QPolygon') {
+ if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.name !== 'moduleSetupSurface') {
obj.set({ stroke: 'black' })
}
@@ -227,7 +233,7 @@ export function useCanvasEvent() {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
- if (obj.type === 'QPolygon') {
+ if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
obj.set({ stroke: 'black' })
}
})
@@ -235,7 +241,7 @@ export function useCanvasEvent() {
if (selected?.length > 0) {
selected.forEach((obj) => {
- if (obj.type === 'QPolygon' && obj.name !== 'module') {
+ if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
obj.set({ stroke: 'red' })
}
})
diff --git a/src/hooks/useCirCuitTrestle.js b/src/hooks/useCirCuitTrestle.js
index 027b965a..ae90be97 100644
--- a/src/hooks/useCirCuitTrestle.js
+++ b/src/hooks/useCirCuitTrestle.js
@@ -31,7 +31,7 @@ export function useCircuitTrestle(executeEffect = false) {
const { getMessage } = useMessage()
useEffect(() => {
- if (Object.keys(selectedModules).length > 0 && executeEffect) setModuleStatisticsData()
+ if (selectedModules && Object.keys(selectedModules).length > 0 && executeEffect) setModuleStatisticsData()
}, [selectedModules])
const getOptYn = () => {
diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js
index 8eb4d69e..fe6d800e 100644
--- a/src/hooks/useContextMenu.js
+++ b/src/hooks/useContextMenu.js
@@ -81,9 +81,9 @@ export function useContextMenu() {
switch (selectedMenu) {
case 'outline':
break
- default:
- setContextMenu([])
- break
+ // default:
+ // setContextMenu([])
+ // break
}
}
diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js
index d3d270f6..593fd76f 100644
--- a/src/hooks/useEvent.js
+++ b/src/hooks/useEvent.js
@@ -77,7 +77,10 @@ export function useEvent() {
setCanvasZoom(Number((zoom * 100).toFixed(0)))
// 마우스 위치 기준으로 확대/축소
- canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom)
+ canvas.zoomToPoint(new fabric.Point(opt.e.offsetX, opt.e.offsetY), zoom)
+ canvas.calcOffset()
+ canvas.setViewportTransform(canvas.viewportTransform)
+ canvas.requestRenderAll()
// 이벤트의 기본 동작 방지 (스크롤 방지)
opt.e.preventDefault()
@@ -104,6 +107,10 @@ export function useEvent() {
const horizonLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'horizontal')
const verticalLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'vertical')
+ if (!horizonLines || !verticalLines) {
+ return
+ }
+
const closestHorizontalLine = horizonLines.reduce((prev, curr) => {
const prevDistance = calculateDistance(pointer, prev)
const currDistance = calculateDistance(pointer, curr)
diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js
index 013e00c4..f812f00e 100644
--- a/src/hooks/usePlan.js
+++ b/src/hooks/usePlan.js
@@ -20,6 +20,7 @@ import { compasDegAtom } from '@/store/orientationAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useCanvasPopupStatusController } from './common/useCanvasPopupStatusController'
import { useCanvasMenu } from './common/useCanvasMenu'
+import { QcastContext } from '@/app/QcastProvider'
/**
* 플랜 처리 훅
@@ -52,6 +53,9 @@ export function usePlan(params = {}) {
const { fetchBasicSettings, basicSettingCopySave } = useCanvasSetting()
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
+ /** 전역 로딩바 컨텍스트 */
+ const { setIsGlobalLoading } = useContext(QcastContext)
+
/**
* 플랜 복사 시 모듈이 있을경우 모듈 데이터 복사하기 위한 처리
*/
@@ -414,7 +418,7 @@ export function usePlan(params = {}) {
useEffect(() => {
setSelectedPlan(currentCanvasPlan)
handleCurrentPlanUrl()
- resetCurrentObject()
+ // resetCurrentObject()
resetModuleSetupSurface()
}, [currentCanvasPlan])
@@ -445,18 +449,26 @@ export function usePlan(params = {}) {
if (currentCanvasPlan?.id) {
await saveCanvas(false)
}
- JSON.parse(currentCanvasData()).objects.length > 0
- ? swalFire({
- text: `Plan ${currentCanvasPlan.planNo} ` + getMessage('plan.message.confirm.copy'),
- type: 'confirm',
- confirmFn: async () => {
- await postObjectPlan(userId, objectNo, true, false)
- },
- denyFn: async () => {
- await postObjectPlan(userId, objectNo, false, false)
- },
- })
- : await postObjectPlan(userId, objectNo, false, false)
+ if (JSON.parse(currentCanvasData()).objects.length > 0) {
+ swalFire({
+ text: `Plan ${currentCanvasPlan.planNo} ` + getMessage('plan.message.confirm.copy'),
+ type: 'confirm',
+ confirmFn: async () => {
+ setIsGlobalLoading(true)
+ await postObjectPlan(userId, objectNo, true, false)
+ setIsGlobalLoading(false)
+ },
+ denyFn: async () => {
+ setIsGlobalLoading(true)
+ await postObjectPlan(userId, objectNo, false, false)
+ setIsGlobalLoading(false)
+ },
+ })
+ } else {
+ setIsGlobalLoading(true)
+ await postObjectPlan(userId, objectNo, false, false)
+ setIsGlobalLoading(false)
+ }
}
/**
diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js
index 54de877b..ccb8f60a 100644
--- a/src/hooks/usePolygon.js
+++ b/src/hooks/usePolygon.js
@@ -176,6 +176,10 @@ export const usePolygon = () => {
* @param showDirectionText
*/
const drawDirectionArrow = (polygon, showDirectionText = true) => {
+ if (!polygon) {
+ return
+ }
+
if (polygon.points.length < 3) {
return
}
@@ -767,7 +771,7 @@ export const usePolygon = () => {
obj.type === 'QLine' &&
obj.attributes?.type !== 'pitchSizeLine' &&
obj.attributes?.roofId === polygon.id &&
- (innerLineTypes.includes(obj.name) || !obj.name),
+ innerLineTypes.includes(obj.name),
)
innerLines = [...polygon.innerLines]
diff --git a/src/locales/ja.json b/src/locales/ja.json
index 0028a047..4dd62685 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -18,6 +18,7 @@
"plan.menu.placement.surface.initial.setting": "配置面初期設定",
"modal.placement.initial.setting.plan.drawing": "図面の作成方法",
"modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物件作成",
+ "modal.placement.initial.setting.plan.drawing.size.info": "※数字は[半角]入力のみ可能です。",
"modal.placement.initial.setting.size": "寸法入力方法",
"modal.placement.initial.setting.size.info": "寸法入力方法案内",
"modal.placement.initial.setting.size.roof": "伏図入力",
@@ -36,8 +37,8 @@
"modal.roof.shape.setting.ridge": "棟",
"modal.roof.shape.setting.patten.a": "Aパターン",
"modal.roof.shape.setting.patten.b": "Bパターン",
- "modal.roof.shape.setting.side": "別に設定",
- "plan.menu.roof.cover": "屋根作図",
+ "modal.roof.shape.setting.side": "個別に設定",
+ "plan.menu.roof.cover": "伏せ図入力",
"plan.menu.roof.cover.outline.drawing": "外壁線を描く",
"plan.menu.roof.cover.roof.shape.setting": "屋根形状の設定",
"plan.menu.roof.cover.roof.shape.passivity.setting": "屋根形状の手動設定",
@@ -72,7 +73,7 @@
"common.setting.rollback": "前に戻る",
"modal.cover.outline.remove": "外壁の取り外し",
"modal.cover.outline.select.move": "外壁選択の移動",
- "plan.menu.placement.surface": "配置面",
+ "plan.menu.placement.surface": "実測値入力",
"plan.menu.placement.surface.slope.setting": "傾斜設定",
"plan.menu.placement.surface.drawing": "配置面の描画",
"modal.placement.surface.drawing.straight.line": "直線",
@@ -88,13 +89,17 @@
"plan.menu.module.circuit.setting.default": "モジュール/架台設定",
"modal.module.basic.setting.orientation.setting": "方位設定",
"modal.module.basic.setting.orientation.setting.info": "※シミュレーション計算用方位を指定します。南の方位を設定してください。",
- "modal.module.basic.setting.orientation.setting.angle.passivity": "勾配を直接入力",
+ "modal.module.basic.setting.orientation.setting.angle.passivity": "角度変更",
"modal.module.basic.setting.module.roof.material": "屋根材",
"modal.module.basic.setting.module.trestle.maker": "架台メーカー",
"modal.module.basic.setting.module.rafter.margin": "垂木の間隔",
"modal.module.basic.setting.module.construction.method": "工法",
"modal.module.basic.setting.module.under.roof": "屋根の下",
"modal.module.basic.setting.module.setting": "モジュールの選択",
+ "modal.module.basic.setting.module.placement.area": "モジュール配置領域",
+ "modal.module.basic.setting.module.placement.area.eaves": "軒側",
+ "modal.module.basic.setting.module.placement.area.ridge": "棟側",
+ "modal.module.basic.setting.module.placement.area.keraba": "けらぱ",
"modal.module.basic.setting.module.hajebichi": "ハゼピッチ",
"modal.module.basic.setting.module.setting.info1": "※勾配の範囲には制限があります。屋根傾斜が2.5値未満10値を超える場合は、施工が可能かどうか施工マニュアルを確認してください。",
"modal.module.basic.setting.module.setting.info2": "※モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ずご確認ください。",
@@ -109,28 +114,44 @@
"modal.module.basic.setting.module.eaves.bar.fitting": "軒カバーの設置",
"modal.module.basic.setting.module.blind.metal.fitting": "落雪防止金具設置",
"modal.module.basic.setting.module.select": "モジュール/架台選択",
+ "modal.module.basic.settting.module.error1": "架台メーカーを選択してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error2": "工法を選択してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error3": "屋根の下を選択してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error4": "施工法を選択してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error5": "L を選択してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error6": "垂木の間隔を入力してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error7": "下在ビーチを入力してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error8": "モジュール配置領域の値を入力してください。\n(屋根材: {0})(JA)",
+ "modal.module.basic.settting.module.error9": "軒側の値は{0} mm以上でなければなりません。\n(屋根材: {1})(JA)",
+ "modal.module.basic.settting.module.error10": "吊下側の値は{0} mm以上でなければなりません。\n(屋根材: {1})(JA)",
+ "modal.module.basic.settting.module.error11": "ケラバ側の値は{0} mm以上でなければなりません。\n(屋根材: {1})(JA)",
"modal.module.basic.setting.module.placement": "モジュールの配置",
"modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置",
+ "modal.module.basic.setting.module.placement.max.row.amount": "Max単数",
+ "modal.module.basic.setting.module.placement.mix.max.row.amount": "混合Max単数",
+ "modal.module.basic.setting.module.placement.row.amount": "単数",
+ "modal.module.basic.setting.module.placement.column.amount": "熱水",
"modal.module.basic.setting.module.placement.do": "する",
"modal.module.basic.setting.module.placement.do.not": "しない",
"modal.module.basic.setting.module.placement.arrangement.standard": "配置基準",
"modal.module.basic.setting.module.placement.arrangement.standard.center": "中央",
- "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "軒",
- "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "棟",
+ "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "軒側",
+ "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "棟側",
"modal.module.basic.setting.module.placement.maximum": "最大配置",
- "modal.module.basic.setting.pitch.module.placement.standard.setting": "配置基準設定",
- "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "南向き設置",
- "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "指定した辺を基準に設置",
+ "modal.module.basic.setting.pitch.module.placement.standard.setting": "配置基準の設定",
+ "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "南向きに設置す",
+ "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "指定した辺を基準に設置する",
"modal.module.basic.setting.pitch.module.allocation.setting": "割り当て設定",
"modal.module.basic.setting.pitch.module.allocation.setting.info": "※配置パネルの種類が1種類の場合のみ使用できます。",
"modal.module.basic.setting.pitch.module.row.amount": "単数",
"modal.module.basic.setting.pitch.module.row.margin": "上下間隔",
"modal.module.basic.setting.pitch.module.column.amount": "列数",
"modal.module.basic.setting.pitch.module.column.margin": "左右間隔",
- "modal.module.basic.setting.prev": "移転",
+ "modal.module.basic.setting.prev": "前に戻る",
+ "modal.module.basic.setting.row.batch": "単数指定配置",
"modal.module.basic.setting.passivity.placement": "手動配置",
- "modal.module.basic.setting.auto.placement": "設定値に自動配置",
+ "modal.module.basic.setting.auto.placement": "自動配置",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting.alloc.trestle": "架台配置",
@@ -178,7 +199,7 @@
"modal.roof.alloc.select.parallel": "筋配置",
"modal.roof.alloc.select.stairs": "千鳥配置",
"modal.roof.alloc.apply": "選択した屋根材として割り当て",
- "plan.menu.estimate.docDown": "各種資料ダウンロード",
+ "plan.menu.estimate.docDownload": "見積書出力",
"plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "見積書のコピー",
@@ -549,7 +570,7 @@
"color.pink": "ピンク",
"color.gold": "黄金色",
"color.darkblue": "藍色",
- "site.name": "HANASYS設計",
+ "site.name": "HANASYS DESIGN",
"site.sub_name": "太陽光発電システム図面管理サイト",
"site.header.link1": "選択してください。",
"site.header.link2": "オンライン保証システム",
@@ -558,7 +579,7 @@
"board.faq.title": "FAQ",
"board.faq.sub.title": "FAQリスト",
"board.archive.title": "各種資料ダウンロード",
- "board.archive.sub.title": "見積書一覧",
+ "board.archive.sub.title": "掲載資料一覧",
"board.list.header.rownum": "番号",
"board.list.header.title": "タイトル",
"board.list.header.regDt": "登録日",
@@ -594,6 +615,7 @@
"myinfo.message.password.error": "パスワードが間違っています。",
"login": "ログイン",
"login.auto.page.text": "自動ログイン中です。",
+ "login.fail": "계정이 없거나 비밀번호가 잘못되었습니다.",
"login.id.save": "ID保存",
"login.id.placeholder": "IDを入力してください。",
"login.password.placeholder": "パスワードを入力してください。",
@@ -841,7 +863,7 @@
"has.not.sleeve": "袖なし",
"jerkinhead.width": "半切妻幅",
"jerkinhead.slope": "半切妻傾斜",
- "shed.width": "片流幅",
+ "shed.width": "片流れの出幅",
"windage": "片流れ",
"windage.width": "片流れの出幅",
"write": "作成",
@@ -886,7 +908,7 @@
"estimate.detail.drawingEstimateCreateDate": "登録日",
"estimate.detail.lastEditDatetime": "変更日時",
"estimate.detail.saleStoreId": "一次販売店名",
- "estimate.detail.estimateDate": "見積日",
+ "estimate.detail.estimateDate": "見積作成日",
"estimate.detail.otherSaleStoreId": "二次販売店名",
"estimate.detail.noOtherSaleStoreId": "二次店なし",
"estimate.detail.receiveUser": "担当者",
@@ -895,6 +917,7 @@
"estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ(YJOD)",
+ "estimate.detail.agency": "2次店名",
"estimate.detail.roofCns": "屋根材・施工区分",
"estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出",
@@ -957,6 +980,7 @@
"estimate.detail.estimateCopyPopup.close": "閉じる",
"estimate.detail.estimateCopyPopup.copyBtn": "見積コピー",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました。コピーした見積情報に移動します。",
+ "estimate.detail.estimateCopyPopup.copy.alertMessageError": "キャンバスのコピー中にエラーが発生しました.",
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です。",
@@ -997,7 +1021,7 @@
"simulator.table.sub5": "枚数",
"simulator.table.sub6": "合計",
"simulator.table.sub7": "パワーコンディショナー",
- "simulator.table.sub8": "ティーン",
+ "simulator.table.sub8": "台",
"simulator.table.sub9": "予測発電量(kWh)",
"simulator.notice.sub1": "ハンファジャパン年間発電量",
"simulator.notice.sub2": "シミュレーションガイド",
@@ -1034,5 +1058,20 @@
"roof.exceed.count": "屋根材は4つまで選択可能です。",
"outerLine.property.fix": "外壁線の属性設定 を完了しますか?",
"outerLine.property.close": "外壁線の属性設定 を終了しますか?",
- "want.to.complete.auxiliary.creation": "보補助線の作成を完了しますか?"
+ "want.to.complete.auxiliary.creation": "補助線の作成を完了しますか?",
+ "module.layout.setup.has.zero.value": "モジュールの列、行を入力してください.",
+ "modal.placement.initial.setting.plan.drawing.only.number": "(※数字は[半角]入力のみ可能です。)",
+ "wall.line.not.found": "外壁がありません",
+ "roof.line.not.found": "屋根形状がありません",
+ "roof.material.can.not.delete": "割り当てられた配置面があります。",
+ "chidory.can.not.install": "千鳥配置できない工法です。",
+ "module.layout.setup.max.count": "モジュールの最大段数は{0}、最大列数は{1}です。 (JA)",
+ "module.layout.setup.max.count.multiple": "モジュール{0}の最大段数は{1}、最大列数は{2}です。 (JA)",
+ "roofAllocation.not.found": "割り当てる屋根がありません。 (JA)",
+ "modal.module.basic.setting.module.placement.max.size.check": "屋根材別モジュールの最大単数。混合最大単数を確認してください。 (JA)",
+ "modal.module.basic.setting.module.placement.max.row": "最大 単数(JA)",
+ "modal.module.basic.setting.module.placement.max.rows.multiple": "混合単数(JA)",
+ "modal.module.basic.setting.module.placement.mix.asg.yn.error": "混合インストール不可能なモジュールです。 (JA)",
+ "modal.module.basic.setting.module.placement.mix.asg.yn": "ミックス. (JA)",
+ "modal.module.basic.setting.layoutpassivity.placement": "layout配置 (JA)"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 4c051305..0164a546 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -18,6 +18,7 @@
"plan.menu.placement.surface.initial.setting": "배치면 초기설정",
"modal.placement.initial.setting.plan.drawing": "도면 작성방법",
"modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성",
+ "modal.placement.initial.setting.plan.drawing.size.info": "※숫자는 [반각] 입력만 가능합니다.",
"modal.placement.initial.setting.size": "치수 입력방법",
"modal.placement.initial.setting.size.info": "치수 입력방법 안내",
"modal.placement.initial.setting.size.roof": "복시도 입력",
@@ -88,13 +89,17 @@
"plan.menu.module.circuit.setting.default": "모듈/가대설정",
"modal.module.basic.setting.orientation.setting": "방위 설정",
"modal.module.basic.setting.orientation.setting.info": "※시뮬레이션 계산용 방위를 지정합니다. 남쪽의 방위를 설정해주세요.",
- "modal.module.basic.setting.orientation.setting.angle.passivity": "각도를 직접 입력",
+ "modal.module.basic.setting.orientation.setting.angle.passivity": "각도 입력",
"modal.module.basic.setting.module.roof.material": "지붕재",
"modal.module.basic.setting.module.trestle.maker": "가대메이커",
"modal.module.basic.setting.module.rafter.margin": "서까래 간격",
"modal.module.basic.setting.module.construction.method": "공법",
"modal.module.basic.setting.module.under.roof": "지붕밑바탕",
"modal.module.basic.setting.module.setting": "모듈 선택",
+ "modal.module.basic.setting.module.placement.area": "모듈 배치 영역",
+ "modal.module.basic.setting.module.placement.area.eaves": "처마쪽",
+ "modal.module.basic.setting.module.placement.area.ridge": "용마루쪽",
+ "modal.module.basic.setting.module.placement.area.keraba": "케라바쪽",
"modal.module.basic.setting.module.hajebichi": "망둥어 피치",
"modal.module.basic.setting.module.setting.info1": "※ 구배의 범위에는 제한이 있습니다. 지붕경사가 2.5치 미만 10치를 초과하는 경우에는 시공이 가능한지 시공 매뉴얼을 확인해주십시오.",
"modal.module.basic.setting.module.setting.info2": "※ 모듈 배치 시에는 시공 매뉴얼에 기재된 <모듈 배치 조건>을 반드시 확인해주십시오.",
@@ -109,16 +114,32 @@
"modal.module.basic.setting.module.eaves.bar.fitting": "처마커버설치",
"modal.module.basic.setting.module.blind.metal.fitting": "적설방지금구설치",
"modal.module.basic.setting.module.select": "모듈/가대 선택",
+ "modal.module.basic.settting.module.error1": "가대메이커를 선택해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error2": "공법를 선택해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error3": "지붕밑바탕을 선택해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error4": "시공법을 선택해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error5": "L 을 입력해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error6": "서까래 간격을 입력해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error7": "하제비치를 입력해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error8": "모듈 배치 영역 값을 입력해주세요.\n(지붕재: {0})",
+ "modal.module.basic.settting.module.error9": "처마쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
+ "modal.module.basic.settting.module.error10": "용마루쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
+ "modal.module.basic.settting.module.error11": "케라바쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
"modal.module.basic.setting.module.placement": "모듈 배치",
"modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치",
+ "modal.module.basic.setting.module.placement.max.row.amount": "Max 단수",
+ "modal.module.basic.setting.module.placement.mix.max.row.amount": "혼합Max 단수",
+ "modal.module.basic.setting.module.placement.row.amount": "단수",
+ "modal.module.basic.setting.module.placement.column.amount": "열수",
"modal.module.basic.setting.module.placement.do": "한다",
"modal.module.basic.setting.module.placement.do.not": "하지 않는다",
"modal.module.basic.setting.module.placement.arrangement.standard": "배치 기준",
"modal.module.basic.setting.module.placement.arrangement.standard.center": "중앙",
- "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "처마",
- "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "용마루",
+ "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "처마쪽",
+ "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "용마루쪽",
"modal.module.basic.setting.module.placement.maximum": "최대배치",
+ "modal.module.basic.setting.module.placement.margin.check1": "가대메이커를 선택해주세요.",
"modal.module.basic.setting.pitch.module.placement.standard.setting": "배치기준 설정",
"modal.module.basic.setting.pitch.module.placement.standard.setting.south": "남향설치",
"modal.module.basic.setting.pitch.module.placement.standard.setting.select": "지정한 변을 기준으로 설치",
@@ -129,6 +150,7 @@
"modal.module.basic.setting.pitch.module.column.amount": "열수",
"modal.module.basic.setting.pitch.module.column.margin": "좌우간격",
"modal.module.basic.setting.prev": "이전",
+ "modal.module.basic.setting.row.batch": "단수지정 배치",
"modal.module.basic.setting.passivity.placement": "수동 배치",
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로설정",
@@ -178,7 +200,7 @@
"modal.roof.alloc.select.parallel": "병렬식",
"modal.roof.alloc.select.stairs": "계단식",
"modal.roof.alloc.apply": "선택한 지붕재로 할당",
- "plan.menu.estimate.docDown": "문서 다운로드",
+ "plan.menu.estimate.docDownload": "문서 다운로드",
"plan.menu.estimate.save": "저장",
"plan.menu.estimate.reset": "초기화",
"plan.menu.estimate.copy": "견적서 복사",
@@ -594,6 +616,7 @@
"myinfo.message.password.error": "비밀번호가 틀렸습니다.",
"login": "로그인",
"login.auto.page.text": "자동로그인 중 입니다.",
+ "login.fail": "계정이 없거나 비밀번호가 잘못되었습니다.",
"login.id.save": "ID Save",
"login.id.placeholder": "아이디를 입력해주세요.",
"login.password.placeholder": "비밀번호를 입력해주세요.",
@@ -886,7 +909,7 @@
"estimate.detail.drawingEstimateCreateDate": "등록일",
"estimate.detail.lastEditDatetime": "변경일시",
"estimate.detail.saleStoreId": "1차 판매점명",
- "estimate.detail.estimateDate": "견적일",
+ "estimate.detail.estimateDate": "견적작성일",
"estimate.detail.otherSaleStoreId": "2차 판매점명",
"estimate.detail.noOtherSaleStoreId": "2차점 없음",
"estimate.detail.receiveUser": "담당자",
@@ -895,6 +918,7 @@
"estimate.detail.estimateType": "주문분류",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
+ "estimate.detail.agency": "2차점명",
"estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.remarks": "비고",
"estimate.detail.fileFlg": "후일자료제출",
@@ -957,6 +981,7 @@
"estimate.detail.estimateCopyPopup.close": "닫기",
"estimate.detail.estimateCopyPopup.copyBtn": "견적복사",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.",
+ "estimate.detail.estimateCopyPopup.copy.alertMessageError": "캔버스 복사 중 오류 발생.",
"estimate.detail.productFeaturesPopup.title": "제품특이사항",
"estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.",
@@ -1034,5 +1059,20 @@
"roof.exceed.count": "지붕재는 4개까지 선택 가능합니다.",
"outerLine.property.fix": "외벽선 속성 설정을 완료하시겠습니까?",
"outerLine.property.close": "외벽선 속성 설정을 종료하시겠습니까?",
- "want.to.complete.auxiliary.creation": "보조선 작성을 완료하시겠습니까?"
+ "want.to.complete.auxiliary.creation": "보조선 작성을 완료하시겠습니까?",
+ "module.layout.setup.has.zero.value": "모듈의 열, 행을 입력해 주세요.",
+ "modal.placement.initial.setting.plan.drawing.only.number": "(※ 숫자는 [반각]입력만 가능합니다.)",
+ "wall.line.not.found": "외벽선이 없습니다.",
+ "roof.line.not.found": "지붕형상이 없습니다.",
+ "roof.material.can.not.delete": "할당된 배치면이 있습니다.",
+ "chidory.can.not.install": "치조 불가 공법입니다.",
+ "module.layout.setup.max.count": "모듈의 최대 단수는 {0}, 최대 열수는 {1} 입니다.",
+ "module.layout.setup.max.count.multiple": "모듈 {0}번의 최대 단수는 {1}, 최대 열수는 {2} 입니다.",
+ "roofAllocation.not.found": "할당할 지붕이 없습니다.",
+ "modal.module.basic.setting.module.placement.max.size.check": "지붕재별 모듈의 최대 단수. 혼합 최대 단수를 확인하십시오.",
+ "modal.module.basic.setting.module.placement.max.row": "최대 단수",
+ "modal.module.basic.setting.module.placement.max.rows.multiple": "혼합 단수",
+ "modal.module.basic.setting.module.placement.mix.asg.yn.error": "혼합 설치 불가능한 모듈입니다.",
+ "modal.module.basic.setting.module.placement.mix.asg.yn": "혼합",
+ "modal.module.basic.setting.layoutpassivity.placement": "레이아웃 배치"
}
diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js
index ddeed03d..c8bb1ad0 100644
--- a/src/store/canvasAtom.js
+++ b/src/store/canvasAtom.js
@@ -384,3 +384,27 @@ export const isManualModuleSetupState = atom({
key: 'isManualModuleSetupState',
default: false,
})
+
+export const isManualModuleLayoutSetupState = atom({
+ key: 'isManualModuleLayoutSetupState',
+ default: false,
+})
+
+export const moduleSetupOptionState = atom({
+ key: 'moduleSetupOptionState',
+ default: {
+ isChidori: false, //치조 안함
+ setupLocation: 'eaves', //처마
+ },
+})
+
+export const toggleManualSetupModeState = atom({
+ key: 'toggleManualSetupModeState',
+ default: '',
+})
+
+export const moduleRowColArrayState = atom({
+ key: 'moduleRowColArrayState',
+ default: [],
+ dangerouslyAllowMutability: true,
+})
diff --git a/src/store/hotkeyAtom.js b/src/store/hotkeyAtom.js
new file mode 100644
index 00000000..1ad0d43c
--- /dev/null
+++ b/src/store/hotkeyAtom.js
@@ -0,0 +1,6 @@
+import { atom } from 'recoil'
+
+export const hotkeyStore = atom({
+ key: 'hotkeyState',
+ default: [],
+})
diff --git a/src/store/roofAtom.js b/src/store/roofAtom.js
new file mode 100644
index 00000000..36f35b6c
--- /dev/null
+++ b/src/store/roofAtom.js
@@ -0,0 +1,7 @@
+import { atom } from 'recoil'
+
+export const roofsState = atom({
+ key: 'roofs',
+ default: null,
+ dangerouslyAllowMutability: true,
+})
diff --git a/src/store/selectedModuleOptions.js b/src/store/selectedModuleOptions.js
index 5165fb48..7b769844 100644
--- a/src/store/selectedModuleOptions.js
+++ b/src/store/selectedModuleOptions.js
@@ -2,7 +2,7 @@ import { atom } from 'recoil'
export const selectedModuleState = atom({
key: 'selectedModuleState',
- default: [],
+ default: null,
dangerouslyAllowMutability: true,
})
diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js
index 012e5bdc..32a620a1 100644
--- a/src/store/settingAtom.js
+++ b/src/store/settingAtom.js
@@ -221,6 +221,22 @@ export const corridorDimensionSelector = selector({
const settingModalFirstOptions = get(settingModalFirstOptionsState)
return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected)
},
+ set: ({ set }, newValue) => {
+ //0 : 복도치수 , 1 : 실제치수
+
+ set(settingModalFirstOptionsState, (prev) => {
+ return {
+ ...prev,
+ dimensionDisplay: prev.dimensionDisplay.map((item, index) => {
+ if (index === newValue) {
+ return { ...item, selected: true }
+ } else {
+ return { ...item, selected: false }
+ }
+ }),
+ }
+ })
+ },
dangerouslyAllowMutability: true,
})
diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss
index 10a04637..bfd7b1f8 100644
--- a/src/styles/_contents.scss
+++ b/src/styles/_contents.scss
@@ -340,6 +340,14 @@
&.active{
top: calc(92.8px + 50px);
}
+ .canvas-id{
+ display: flex;
+ align-items: center;
+ padding: 9.6px 20px;
+ font-size: 12px;
+ color: #fff;
+ background-color: #1083E3;
+ }
.canvas-plane-wrap{
display: flex;
align-items: center;
diff --git a/src/styles/_grid-detail.scss b/src/styles/_grid-detail.scss
index b0c88984..5f27bd18 100644
--- a/src/styles/_grid-detail.scss
+++ b/src/styles/_grid-detail.scss
@@ -24,6 +24,10 @@
.ag-header-cell{
font-size: 13px;
color: #fff;
+ border-right: 1px solid #738596;
+ &:last-child{
+ border: none;
+ }
}
.ag-header-cell-label{
justify-content: center;
diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss
index 65b104b7..fef7c2fd 100644
--- a/src/styles/_modal.scss
+++ b/src/styles/_modal.scss
@@ -279,10 +279,11 @@ $alert-color: #101010;
border-bottom: 1px solid #424242;
}
}
-.grid-check-form-block{
- display: block;
- > div{
- margin-bottom: 10px;
+.grid-check-form-flex{
+ display: flex;
+ gap: 10px;
+ .d-check-box{
+ flex: 1;
}
}
.grid-option-overflow{
@@ -1313,13 +1314,13 @@ $alert-color: #101010;
.circle {
position: absolute;
- width: 12px;
- height: 12px;
+ width: 10px;
+ height: 10px;
border: 1px solid #fff;
border-radius: 50%;
- top: 95%;
+ top: 88%;
left: 50%;
- transform-origin: 0 -90px; /* 중심에서 반지름 거리만큼 떨어져 위치 */
+ transform-origin: 0 -76px; /* 중심에서 반지름 거리만큼 떨어져 위치 */
cursor:pointer;
z-index: 3;
/* 0번을 180도 위치(아래)에, 13번을 0도 위치(위)에 배치 */
@@ -1366,8 +1367,8 @@ $alert-color: #101010;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- width: 5px;
- height: 5px;
+ width: 4px;
+ height: 4px;
background-color: #fff;
border-radius: 50%;
}
@@ -1381,15 +1382,15 @@ $alert-color: #101010;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- width: 148px;
- height: 148px;
+ width: 121px;
+ height: 121px;
border: 4px solid #fff;
border-radius: 50%;
.compas-arr{
width: 100%;
height: 100%;
background: url(../../public/static/images/canvas/compas.svg)no-repeat center;
- background-size: 122px 122px;
+ background-size: 100px 100px;
}
}
}
@@ -1441,10 +1442,10 @@ $alert-color: #101010;
.roof-module-compas{
margin-bottom: 24px;
.compas-box-inner{
- width: 280px;
- height: 253px;
+ width: 235px;
+ height: 215px;
.circle{
- top: 86%;
+ top: 85%;
// &:nth-child(1),
// &:nth-child(7),
// &:nth-child(13),
@@ -1461,7 +1462,7 @@ $alert-color: #101010;
// }
// }
i{
- top: 22px;
+ top: 19px;
}
&.act{
i{color: #8B8B8B;}
@@ -1482,6 +1483,10 @@ $alert-color: #101010;
.outline-form{
flex: 1;
}
+ .non-flex{
+ min-width: 300px;
+ flex: none;
+ }
}
.module-box-tab{
@@ -2172,31 +2177,46 @@ $alert-color: #101010;
&.tab2{
margin-top: 10px;
gap: 15px;
-
+ .eaves-keraba-table{
+ margin-top: 0;
+ }
}
.module-flex-item{
flex: 1;
- .module-flex-item-tit{
- font-size: 12px;
- font-weight: 500;
- color: #fff;
- padding-bottom: 10px;
- border-bottom: 1px solid #4D4D4D;
- }
+
.flex-item-btn-wrap{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
- margin-bottom: 24px;
+ margin-bottom: 10px;
}
&.non-flex{
display: flex;
flex-direction: column;
justify-content: flex-start;
flex: none;
- padding-top: 27.5px;
- width: 260px;
+ width: 340px;
}
+ .flex-item-button{
+ margin-top: 10px;
+ button{
+ width: 100%;
+ }
+ }
+ }
+}
+.module-flex-item-tit-wrap{
+ display: flex;
+ align-items: center;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #4D4D4D;
+ .module-flex-item-tit{
+ font-size: 12px;
+ font-weight: 500;
+ color: #fff;
+ }
+ button{
+ margin-left: auto;
}
}
@@ -2277,4 +2297,129 @@ $alert-color: #101010;
box-shadow: 0em -2.6em 0em 0em rgba(255, 255, 255, 0.2), 1.8em -1.8em 0 0em rgba(255, 255, 255, 0.2), 2.5em 0em 0 0em rgba(255, 255, 255, 0.2), 1.75em 1.75em 0 0em rgba(255, 255, 255, 0.2), 0em 2.5em 0 0em rgba(255, 255, 255, 0.2), -1.8em 1.8em 0 0em rgba(255, 255, 255, 0.5), -2.6em 0em 0 0em rgba(255, 255, 255, 0.7), -1.8em -1.8em 0 0em #fff;
}
}
+}
+
+.roof-module-inner{
+ display: flex;
+ .compas-wrapper{
+ position: relative;
+ flex: none;
+ width: 300px;
+ padding-right: 15px;
+ &:before{
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 10px;
+ width: 1px;
+ height: 100%;
+ background-color: #424242;
+ }
+ }
+ .compas-table-wrap{
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+
+
+ }
+ .compas-table-box{
+ background-color: #3D3D3D;
+ padding: 10px;
+ .outline-form{
+ span{
+ width: auto;
+ }
+ }
+ .compas-grid-table{
+ display: grid;
+ gap: 10px;
+ grid-template-columns: repeat(2, 1fr);
+ .outline-form{
+ span{
+ width: 65px;
+ &.thin{
+ width: 20px;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+.module-table-block-wrap{
+ .roof-module-table{
+ &.self{
+ table{
+ table-layout: fixed;
+ }
+ }
+ }
+ .self-table-radio{
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+}
+
+.module-area{
+ display: flex;
+ align-items: center;
+ .module-area-title{
+ flex: none;
+ font-size: 12px;
+ color: #fff;
+ font-weight: 500;
+ margin-right: 20px;
+ }
+ .outline-form{
+ flex: 1;
+ }
+}
+
+.placement-name-guide{
+ font-size: 11px;
+ margin-left: 10px;
+ color: #53a7eb;
+ font-weight: 500;
+}
+
+.hexagonal-flex-wrap{
+ display: flex;
+ gap: 10px;
+ .non-flex{
+ flex: none;
+ }
+}
+
+.hexagonal-radio-wrap{
+ padding: 17px 10px;
+}
+
+.hide-check-guide{
+ display: flex;
+ align-items: center;
+ font-size: 12px;
+ color: #fff;
+ margin-top: 10px;
+ font-weight: 500;
+ .arr{
+ width: 13px;
+ height: 13px;
+ margin-left: 10px;
+ background: url(../../public/static/images/canvas/hide-check-arr.svg) no-repeat center;
+ background-size: contain;
+ transform: rotate(180deg);
+ &.act{
+ transform: rotate(0deg);
+ }
+ }
+}
+
+.module-table-box{
+ &.hide{
+ overflow: hidden;
+ height: 0;
+ }
}
\ No newline at end of file
diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss
index 34dcaf33..ac0d2f73 100644
--- a/src/styles/_reset.scss
+++ b/src/styles/_reset.scss
@@ -222,15 +222,16 @@ button{
padding: 0 10px;
line-height: 28px;
font-family: 'Noto Sans JP', sans-serif;
- background-color: transparent;
+ background-color: #353535;
border: 1px solid #484848;
color: #fff;
&.blue{
background-color: #4C6FBF;
border: 1px solid #4C6FBF;
&:hover{
- background-color: #414E6C;
- border: 1px solid #414E6C;
+ background-color: #4C6FBF;
+ border: 1px solid #4C6FBF;
+ font-weight: normal;
}
}
&.white{
@@ -241,13 +242,14 @@ button{
background-color: #fff;
border: 1px solid #fff;
color: #101010;
+ font-weight: normal;
}
}
&:hover{
- font-weight: 400;
- background-color: transparent;
+ background-color: #353535;
border: 1px solid #484848;
color: #fff;
+ font-weight: normal;
}
}
&.self{
@@ -381,7 +383,7 @@ button{
}
}
&::-webkit-scrollbar {
- width: 2px;
+ width: 5px;
background-color: transparent;
}
diff --git a/src/styles/_table.scss b/src/styles/_table.scss
index 5efacd7c..31b96bd9 100644
--- a/src/styles/_table.scss
+++ b/src/styles/_table.scss
@@ -47,6 +47,16 @@ table{
}
.form-flex-wrap{
@include flexbox;
+ .form-flex-select{
+ @include flexbox;
+ label{
+ flex: none;
+ margin-right: 5px;
+ }
+ .form-select{
+ min-width: 300px;
+ }
+ }
}
.date-picker-wrap{
width: 100%;
@@ -236,6 +246,16 @@ table{
.d-check-box{
opacity: 0.5;
}
+ .color-wrap{
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ .color-box{
+ width: 14px;
+ height: 14px;
+ margin-right: 8px;
+ }
+ }
}
}
tbody{
diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js
index 935097b0..60725183 100644
--- a/src/util/canvas-util.js
+++ b/src/util/canvas-util.js
@@ -348,15 +348,15 @@ export const calculateIntersection = (line1, line2) => {
}
// Determine the min and max for line1 and line2 for both x and y
- const line1MinX = Math.min(line1.x1, line1.x2)
- const line1MaxX = Math.max(line1.x1, line1.x2)
- const line2MinX = Math.min(line2.x1, line2.x2)
- const line2MaxX = Math.max(line2.x1, line2.x2)
+ const line1MinX = Math.min(line1.x1, line1.x2) - 5
+ const line1MaxX = Math.max(line1.x1, line1.x2) + 5
+ const line2MinX = Math.min(line2.x1, line2.x2) - 5
+ const line2MaxX = Math.max(line2.x1, line2.x2) + 5
- const line1MinY = Math.min(line1.y1, line1.y2)
- const line1MaxY = Math.max(line1.y1, line1.y2)
- const line2MinY = Math.min(line2.y1, line2.y2)
- const line2MaxY = Math.max(line2.y1, line2.y2)
+ const line1MinY = Math.min(line1.y1, line1.y2) - 5
+ const line1MaxY = Math.max(line1.y1, line1.y2) + 5
+ const line2MinY = Math.min(line2.y1, line2.y2) - 5
+ const line2MaxY = Math.max(line2.y1, line2.y2) + 5
// Check if the intersection X and Y are within the range of both lines
if (
@@ -518,14 +518,23 @@ export const sortedPointLessEightPoint = (points) => {
*/
// 직선의 방정식.
// 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다.
-export function isPointOnLine(line, point) {
- const a = line.y2 - line.y1
+export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }) {
+ /*const a = line.y2 - line.y1
const b = line.x1 - line.x2
const c = line.x2 * line.y1 - line.x1 * line.y2
const result = Math.abs(a * point.x + b * point.y + c) / 100
// 점이 선 위에 있는지 확인
- return result <= 10
+ return result <= 10*/
+ // 직선 방정식 만족 여부 확인
+ const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)
+ if (Math.abs(crossProduct) > 5) return false // 작은 오차 허용
+
+ // 점이 선분의 범위 내에 있는지 확인
+ const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2)
+ const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2)
+
+ return withinXRange && withinYRange
}
/**
* 점과 가까운 line 찾기
diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js
index cded90ee..34bc2320 100644
--- a/src/util/qpolygon-utils.js
+++ b/src/util/qpolygon-utils.js
@@ -305,6 +305,9 @@ export function removeDuplicatePolygons(polygons) {
}
export const isSamePoint = (a, b) => {
+ if (!a || !b) {
+ return false
+ }
return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2
}
diff --git a/startscript-3000.js b/startscript-3000.js
new file mode 100644
index 00000000..9130126e
--- /dev/null
+++ b/startscript-3000.js
@@ -0,0 +1,2 @@
+var exec = require('child_process').exec
+exec('yarn dev -p 3000', { windowsHide: true })
diff --git a/startscript.js b/startscript.js
index 37f52696..e276be78 100644
--- a/startscript.js
+++ b/startscript.js
@@ -1,2 +1,2 @@
var exec = require('child_process').exec
-exec('yarn start', { windowsHide: true })
+exec('yarn dev -p 5000', { windowsHide: true })