@@ -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..b6499099 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'
@@ -9,7 +9,10 @@ import { compasDegAtom } from '@/store/orientationAtom'
import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useCircuitTrestle } from '../useCirCuitTrestle'
-import { useEffect } from 'react'
+import { useContext } from 'react'
+import { addedRoofsState } from '@/store/settingAtom'
+import { roofsState } from '@/store/roofAtom'
+import { GlobalDataContext } from '@/app/GlobalDataProvider'
/**
* 캔버스 팝업 상태 관리
@@ -19,13 +22,15 @@ import { useEffect } from 'react'
export function useCanvasPopupStatusController(param = 1) {
const popupType = parseInt(param)
- const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
+ const setCompasDeg = useSetRecoilState(compasDegAtom)
const [moduleSelectionDataStore, setModuleSelectionDataStore] = useRecoilState(moduleSelectionDataState)
- const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
+ 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)
+ const { managementState, setManagementState } = useContext(GlobalDataContext)
/**
* 팝업 상태 조회
* @param {number} popupTypeParam
@@ -51,23 +56,36 @@ export function useCanvasPopupStatusController(param = 1) {
* 조회 후 전체 데이터 recoil에 저장
*/
const handleModuleSelectionTotal = async () => {
+ let resultData = {}
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)
+ resultData = { ...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.roofConstructions) {
+ setRoofs(data.roofConstructions)
+ // setManagementState({ ...managementState, roofs: data.roofConstructions.map((roof) => roof.construction.managementState) })
+ // setModuleSelectionDataStore({ ...moduleSelectionDataStore, roofConstructions: data.roofConstruction })
+ resultData = { ...resultData, roofConstructions: data.roofConstructions }
+ }
+ // if (data?.module) setManagementState(data.common.managementState)
}
}
+ setModuleSelectionDataStore(resultData)
}
/**
@@ -80,7 +98,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/useCommonCode.js b/src/hooks/common/useCommonCode.js
index e85cbb64..7af6b764 100644
--- a/src/hooks/common/useCommonCode.js
+++ b/src/hooks/common/useCommonCode.js
@@ -22,9 +22,8 @@ import { useAxios } from '../useAxios'
* const honorificCodes = findCommonCode(200800);
*/
export const useCommonCode = () => {
- const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const globalLocale = useRecoilValue(globalLocaleStore)
- const { promiseGet } = useAxios()
+ const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const findCommonCode = (key) => {
const resultCodes = commonCode[key]?.map((code) => {
@@ -41,18 +40,6 @@ export const useCommonCode = () => {
return resultCodes
}
- useEffect(() => {
- const getCommonCode = async () => {
- await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {
- setCommonCode(Object.groupBy(res.data, ({ clHeadCd }) => clHeadCd))
- })
- }
-
- if (!isObjectNotEmpty(commonCode)) {
- getCommonCode()
- }
- }, [])
-
return {
commonCode,
findCommonCode,
diff --git a/src/hooks/common/useMasterController.js b/src/hooks/common/useMasterController.js
index 60aab800..21220726 100644
--- a/src/hooks/common/useMasterController.js
+++ b/src/hooks/common/useMasterController.js
@@ -18,7 +18,7 @@ export function useMasterController() {
*/
const getRoofMaterialList = async () => {
return await get({ url: '/api/v1/master/getRoofMaterialList' }).then((res) => {
- console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res)
+ // console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res)
return res
})
}
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..a4c83fe7 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])
// 첨부파일 다운로드
@@ -309,6 +310,7 @@ export const useEstimateController = (planNo, flag) => {
//봄 컴포넌트 제품은 0으로
item.openFlg = '0'
+ item.unitOpenFlg = '0'
}
}
})
@@ -321,6 +323,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 +455,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 +463,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/main/useMainContentsController.js b/src/hooks/main/useMainContentsController.js
index f065c90f..1938a37a 100644
--- a/src/hooks/main/useMainContentsController.js
+++ b/src/hooks/main/useMainContentsController.js
@@ -27,7 +27,7 @@ export const useMainContentsController = () => {
*/
const fetchObjectList = async () => {
try {
- const apiUrl = `/api/main-page/object/${session?.storeId}/list`
+ const apiUrl = `/api/main-page/object/${session?.storeId}/${session?.userId}/${session?.builderNo}/list`
await promiseGet({
url: apiUrl,
}).then((res) => {
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 8ac88a31..a3f2eb0e 100644
--- a/src/hooks/module/useModuleBasicSetting.js
+++ b/src/hooks/module/useModuleBasicSetting.js
@@ -1,13 +1,25 @@
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 { POLYGON_TYPE, BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import * as turf from '@turf/turf'
import { useSwal } from '@/hooks/useSwal'
import { compasDegAtom } from '@/store/orientationAtom'
@@ -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,45 +42,42 @@ 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([])
const selectedModules = useRecoilValue(selectedModuleState)
- 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 moduleSetupOption = useRecoilValue(moduleSetupOptionState)
+ const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
+ const setModuleRowColArray = useSetRecoilState(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,34 +86,21 @@ 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 }))
//북면 설치 가능 판매점
- if (moduleSelectionData.common.saleStoreNorthFlg === '1') {
+ if (moduleSelectionData.common.saleStoreNorthFlg == '1') {
setSaleStoreNorthFlg(true)
}
+
+ setTrestleDetailList(roofConstructionArray)
}
} else {
//육지붕 일경우에는 바로 배치면 설치LL
canvas
.getObjects()
- .filter((roof) => roof.name === 'roof')
+ .filter((roof) => roof.name === POLYGON_TYPE.ROOF)
.forEach((roof) => {
makeModuleInstArea(roof, null)
})
@@ -112,62 +108,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, maxRow: detail.moduleMaxRows })
+ })
+ }
+ 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 +195,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 = {
@@ -231,8 +233,26 @@ export function useModuleBasicSetting(tabNum) {
}
})
+ let isNorth = false
+
const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id)
+
if (isExistSurface) {
+ if (canvasSetting.roofSizeSet != '3') {
+ //북면이 있지만
+ if (roof.directionText && roof.directionText.indexOf('北') > -1) {
+ //북쪽일때 해당 서북서, 동북동은 제외한다고 한다
+ if (!(roof.directionText.indexOf('西北西') > -1 || roof.directionText.indexOf('東北東') > -1)) {
+ isNorth = true
+ }
+ }
+
+ isExistSurface.set({
+ isNorth: isNorth, //북면여부
+ isSaleStoreNorthFlg: moduleSelectionData.common.saleStoreNorthFlg == '1' ? true : false, //북면설치가능점 여부
+ })
+ }
+
addTargetMouseEventListener('mousedown', isExistSurface, function () {
toggleSelection(isExistSurface)
})
@@ -240,7 +260,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 +269,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) {
@@ -264,7 +285,6 @@ export function useModuleBasicSetting(tabNum) {
//모듈설치영역?? 생성
const surfaceId = uuidv4()
- let isNorth = false
if (canvasSetting.roofSizeSet != '3') {
//북면이 있지만
@@ -301,6 +321,7 @@ export function useModuleBasicSetting(tabNum) {
trestleDetail: trestleDetail,
isNorth: isNorth,
perPixelTargetFind: true,
+ isSaleStoreNorthFlg: moduleSelectionData.common.saleStoreNorthFlg == '1' ? true : false, //북면설치가능점 여부
// angle: -compasDeg,
})
@@ -346,41 +367,34 @@ export function useModuleBasicSetting(tabNum) {
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
//최초 선택일때
if (!isExist) {
- //설치면이 북면이고 북면설치 허용점이 아니면
- if (setupSurface.isNorth && !saleStoreNorthFlg) {
+ //모듈에 북면 설치 가능 모듈이 있는지 확인함
+ const isNorthModuleYn = moduleSelectionData?.module.itemList.some((module) => module.northModuleYn === 'Y')
+
+ //설치면이 북면이고 북면설치 허용점이 아니면 북면 모듈이 한개도 없으면
+ if (setupSurface.isNorth && !setupSurface.isSaleStoreNorthFlg && !isNorthModuleYn) {
swalFire({ text: getMessage('module.not.batch.north'), icon: 'warning' })
return
}
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
- 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 +426,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)
+ swalFire({ text: getMessage('module.place.select.module'), icon: 'warning' })
+ setIsManualModuleSetup(false)
+ setManualSetupMode(`manualSetup_false`)
return
}
if (checkedModule.length > 1) {
- swalFire({ text: getMessage('module.place.select.one.module') })
- setIsManualModuleSetup(!isManualModuleSetup)
+ swalFire({ text: getMessage('module.place.select.one.module'), icon: 'warning' })
+ 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 })
})
//모듈 기본 옵션
@@ -538,9 +556,15 @@ export function useModuleBasicSetting(tabNum) {
parentId: moduleSetupSurfaces[i].parentId,
})
+ const northModuleYn = checkedModule.some((module) => module.northModuleYn === 'Y')
//북면이고 북면설치상점이 아니면 그냥 return
- if (trestlePolygon.isNorth && !saleStoreNorthFlg) {
- return
+ if (trestlePolygon.isNorth && !trestlePolygon.isSaleStoreNorthFlg) {
+ if (!northModuleYn) {
+ //북면이고 설치 가능 상점이 아닌데 북면 설치 모듈이 있으면
+ return
+ } else {
+ canvas?.add(tempModule) //움직여가면서 추가됨
+ }
} else {
canvas?.add(tempModule) //움직여가면서 추가됨
}
@@ -548,7 +572,8 @@ 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
@@ -596,15 +621,16 @@ export function useModuleBasicSetting(tabNum) {
tempModule.left = holdCellCenterX - toFixedWithoutRounding(width / 2, 2)
}
- //움직이는 모듈왼쪽 -> 가운데
- if (Math.abs(smallLeft - holdCellCenterX) < snapDistance) {
- tempModule.left = holdCellCenterX + intvHor / 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 - holdCellCenterX) < snapDistance) {
- tempModule.left = holdCellCenterX - width - intvHor / 2
- }
-
//설치된 셀에 좌측에 스냅
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
// console.log('모듈 좌측 스냅')
@@ -638,21 +664,16 @@ export function useModuleBasicSetting(tabNum) {
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 (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(smallBottom - holdCellCenterY) < snapDistance) {
- tempModule.top = holdCellCenterY - height - intvHor / 2
- }
-
//설치된 셀에 좌측에 스냅
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
// console.log('모듈 좌측 스냅')
@@ -696,6 +717,7 @@ export function useModuleBasicSetting(tabNum) {
})
}
+ //여기서부턴 배치면 설치 외곽선 라인
// 위쪽 변에 스냅
if (Math.abs(smallTop - trestleTop) < trestleSnapDistance) {
tempModule.top = trestleTop + 1
@@ -760,6 +782,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'), icon: 'warning' })
+ return
+ }
+ }
+
if (!inside) return
if (tempModule) {
const rectPoints = [
@@ -788,7 +822,7 @@ export function useModuleBasicSetting(tabNum) {
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
//겹치면 안됨
if (intersection) {
- swalFire({ text: getMessage('module.place.overobject') })
+ swalFire({ text: getMessage('module.place.overobject'), icon: 'warning' })
isIntersection = false
}
})
@@ -822,12 +856,870 @@ 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') })
+ swalFire({ text: getMessage('module.place.overlab'), icon: 'warning' })
}
} else {
- swalFire({ text: getMessage('module.place.out') })
+ swalFire({ text: getMessage('module.place.out'), icon: 'warning' })
+ }
+ }
+ })
+ }
+ } 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ 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),
+ },
+ ]
+
+ 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,
+ })
+
+ const northModuleYn = checkedModule.some((module) => module.northModuleYn === 'Y')
+ //북면이고 북면설치상점이 아니면 그냥 return
+ if (trestlePolygon.isNorth && !trestlePolygon.isSaleStoreNorthFlg) {
+ if (!northModuleYn) {
+ //북면이고 설치 가능 상점이 아닌데 북면 설치 모듈이 있으면
+ return
+ } else {
+ canvas?.add(tempModule) //움직여가면서 추가됨
+ }
+ } 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ return
+ }
+ }
+ }
+ if (isInstalled) {
+ ++installedHeightModuleCount
+ }
+ }
+ }
+ })
+ //그림자가 있다면 무조건 그림자를 가장 앞으로 올림
+ const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
+ if (shadowObj) {
+ shadowObj.bringToFront()
}
}
})
@@ -850,17 +1742,45 @@ export function useModuleBasicSetting(tabNum) {
}
//자동 모듈 설치(그리드 방식)
- const autoModuleSetup = (placementRef) => {
+ const autoModuleSetup = (type, layoutSetupRef) => {
initEvent() //마우스 이벤트 초기화
+ //실패한 지붕재 배열
+ let failAutoSetupRoof = []
+
+ let checkedLayoutData
+
+ /**
+ * 자동 레이아웃일때 0이 있거나 혼합이 있는지 확인하는 로직
+ */
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ checkedLayoutData = layoutSetupRef.filter((module) => module.checked)
+ const hasZeroLength = checkedLayoutData.some((module) => module.row === 0 || module.col === 0)
+
+ if (hasZeroLength) {
+ swalFire({ text: getMessage('module.layout.setup.has.zero.value'), icon: 'warning' })
+ 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'), icon: 'warning' })
+ // return
+ // }
+ }
+
if (checkedModule.length === 0) {
- swalFire({ text: getMessage('module.place.select.module') })
+ swalFire({ text: getMessage('module.place.select.module'), icon: 'warning' })
return
}
- const isChidori = placementRef.isChidori.current === 'true' ? true : false
- const setupLocation = placementRef.setupLocation.current
- const isMaxSetup = placementRef.isMaxSetup.current === 'true' ? true : false
+ const isChidori = moduleSetupOption.isChidori
+ const setupLocation = moduleSetupOption.setupLocation
+ const isMaxSetup = false
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
@@ -870,39 +1790,40 @@ export function useModuleBasicSetting(tabNum) {
const batchObjects = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.OBJECT_SURFACE) //도머s 객체
if (moduleSetupSurfaces.length === 0) {
- swalFire({ text: getMessage('module.place.no.surface') })
+ swalFire({ text: getMessage('module.place.no.surface'), icon: 'warning' })
return
}
//어짜피 자동으로 누르면 선택안된데도 다 날아간다
- canvas.getObjects().forEach((obj) => {
- if (obj.name === '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 = []
+ // canvas.getObjects().forEach((obj) => {
+ // if (obj.name === POLYGON_TYPE.MODULE) {
+ // canvas.remove(obj)
// }
// })
- notSelectedTrestlePolygons.forEach((obj) => {
+ //자동일때만 선택 안된 모듈 삭제
+ moduleSetupSurfaces.forEach((obj) => {
if (obj.modules) {
obj.modules.forEach((module) => {
canvas?.remove(module)
+ canvas?.renderAll()
})
obj.modules = []
}
})
+ if (type === MODULE_SETUP_TYPE.AUTO) {
+ notSelectedTrestlePolygons.forEach((obj) => {
+ if (obj.modules) {
+ obj.modules.forEach((module) => {
+ canvas?.remove(module)
+ canvas?.renderAll()
+ })
+ obj.modules = []
+ }
+ })
+ }
+
let moduleOptions = {
stroke: 'black',
strokeWidth: 0.3,
@@ -912,11 +1833,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 = []
@@ -933,36 +1852,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
@@ -973,17 +1862,123 @@ export function useModuleBasicSetting(tabNum) {
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
}
+ /**
+ * 자동 레이아웃 설치 일시 row col 초과 여부 확인
+ * 체크된 모듈중에 북면 모듈이 있으면 북면 모듈만 따로 계산하고 아니면 전체 모듈을 계산
+ * 북면 모듈이 있고 북면이 들어오면 북면의 row를 계산한다
+ *
+ * @param {*} trestleDetailData
+ * @returns
+ */
+ const checkAutoLayoutModuleSetup = (moduleSetupSurface, trestleDetailData) => {
+ //북면 모듈이 없을때
+ //체크된 모듈 중에 북면 모듈이 있는지 확인하는 로직
+ const isIncludeNorthModule = checkedModule.some((module) => module.northModuleYn === 'Y')
+ const maxCol = trestleDetailData.moduleMaxCols //최대 열수 -> 얘는 멀티랑 관계없음
+
+ //북면 모듈이 없으면
+ if (!isIncludeNorthModule) {
+ const isMultipleModules = checkedModule.length > 1 //모듈이 여러개인지 체크하고
+
+ //멀티 모듈이면 모듈밖에 내려온 정보의 maxRow 단일 모듈이면 모듈에 maxRow
+ const maxRow = isMultipleModules
+ ? trestleDetailData.moduleMaxRows
+ : trestleDetailData.module.find((item) => item.moduleTpCd === checkedModule[0].moduleTpCd).moduleMaxRows //멀티모듈이면 밖에 maxRows로 판단 아니면 module->itemmList를 가지고 판단
+
+ //멀티 모듈이면 모듈밖에 내려온 정보의 row 합, 단일 모듈이면 모듈에 row
+ const sumRowCount = isMultipleModules
+ ? layoutSetupRef.filter((item) => item.checked).reduce((acc, cur) => acc + cur.row, 0)
+ : layoutSetupRef.find((item) => item.moduleId === checkedModule[0].itemId).row //멀티모듈이면 전체 합, 체크된 한개의 열
+
+ //col는 moduleItems 배열 밖에 내려옴
+ const sumColCount = layoutSetupRef.filter((item) => item.col).some((item) => item.col > maxCol)
+
+ // 혼합일때 모듈 개별의 maxRow를 체크해서 가능여부 확인
+ const isPassedObject =
+ isMultipleModules && layoutSetupRef.find((item, index) => item.checked && item.row > trestleDetailData.module[index].mixModuleMaxRows) //체크된 배열은 checked여부로 체크해서 index와 동일함
+ //전체 카은트된 열수가 크거나 혼합카운트가 존재 하면 실패
+ if (sumRowCount > maxRow || sumColCount || isPassedObject) {
+ failAutoSetupRoof.push(moduleSetupSurface)
+ return false
+ }
+ } else {
+ const normalModule = checkedModule.filter((item) => item.northModuleYn === 'N')
+ const northModule = checkedModule.filter((item) => item.northModuleYn === 'Y')
+ const northModuleIds = northModule.map((item) => item.itemId)
+ let isPassedNormalModule = false
+
+ //만약 북면 모듈이 2개면 이 하위 로직 가져다가 쓰면됨 northModule === 만 바꾸면 될듯
+ // northModule을 배열로 만들고 include로 해서 체크 해야됨
+ if (normalModule.length > 0 && !moduleSetupSurface.isNorth) {
+ //C1C2 모듈일 경우ㅁㅁ
+ const isMultipleModules = normalModule.length > 1 //모듈이 여러개면
+ const maxRow = isMultipleModules
+ ? trestleDetailData.moduleMaxRows
+ : trestleDetailData.module.find((item) => item.moduleTpCd === normalModule[0].moduleTpCd).moduleMaxRows //멀티모듈이면 밖에 maxRows로 판단 아니면 module->itemmList를 가지고 판단
+
+ //북면 모듈 id를 제외한 모듈의 단 체크
+ const sumRowCount = isMultipleModules
+ ? layoutSetupRef.filter((item) => item.checked && !northModuleIds.includes(item.moduleId)).reduce((acc, cur) => acc + cur.row, 0)
+ : layoutSetupRef.find((item) => item.moduleId === normalModule[0].itemId).row //멀티모듈이면 전체 합, 체크된 한개의 열
+
+ //북면 모듈 id를 제외한 모듈의 열 체크
+ const sumColCount = layoutSetupRef.filter((item) => item.col && !northModuleIds.includes(item.moduleId)).some((item) => item.col > maxCol)
+
+ // 혼합일때 모듈 개별의 row를 체크함
+ const isPassedObject =
+ isMultipleModules &&
+ layoutSetupRef.find(
+ (item, index) => item.checked && !item.moduleId.includes(northModuleIds) && item.row > trestleDetailData.module[index].mixModuleMaxRows,
+ )
+
+ // 합산 단수가 맥스단수보다 크거나 열이 맥스열수보다 크거나 혼합일때 모듈 개별의 row가 맥스단수보다 크면 실패
+ if (sumRowCount > maxRow || sumColCount || isPassedObject) {
+ failAutoSetupRoof.push(moduleSetupSurface)
+ return false
+ } else {
+ isPassedNormalModule = true
+ }
+ }
+
+ //위에서 일반 모듈이 설치가 완료면 그냥 넘어간다
+ //일반 모듈이 pass라면 일반 모듈이 설치됨
+ //만약 일반모듈이 체크가 안되어 있으면 밑에 로직을 탐
+ if (!isPassedNormalModule) {
+ //북면 모듈이 있고 북면에 있을때
+ if (northModule.length > 0 && (moduleSetupSurface.isNorth || !moduleSetupSurface.isNorth)) {
+ //북면 모듈이 있는데 일반 모듈이 있을때 북면이 아니면 그냥 북면은 그냥 pass
+ const isMultipleModules = northModule.length > 1 //모듈이 여러개면
+ const maxRow = isMultipleModules
+ ? trestleDetailData.moduleMaxRows
+ : trestleDetailData.module.find((item) => item.moduleTpCd === northModule[0].moduleTpCd).moduleMaxRows //멀티모듈이면 밖에 maxRows로 판단 아니면 module->itemmList를 가지고 판단
+
+ const sumRowCount = isMultipleModules
+ ? layoutSetupRef.filter((item) => northModuleIds.includes(item.moduleId)).reduce((acc, cur) => acc + cur.row, 0)
+ : layoutSetupRef.find((item) => item.moduleId === northModule[0].itemId).row //멀티모듈이면 전체 합, 체크된 한개의 열
+
+ const sumColCount = layoutSetupRef.filter((item) => item.col && northModuleIds.includes(item.moduleId)).some((item) => item.col > maxCol)
+
+ // 혼합일때 모듈 개별의 row를 체크함
+ const isPassedObject =
+ isMultipleModules &&
+ layoutSetupRef.find(
+ (item, index) =>
+ item.checked && northModuleIds.includes(item.moduleId) && item.row > trestleDetailData.module[index].mixModuleMaxRows,
+ )
+
+ // 합산 단수가 맥스단수보다 크거나 열이 맥스열수보다 크거나 혼합일때 모듈 개별의 row가 맥스단수보다 크면 실패
+ if (sumRowCount > maxRow || sumColCount || isPassedObject) {
+ failAutoSetupRoof.push(moduleSetupSurface)
+ return false
+ }
+ }
+ }
+ }
+ return true
+ }
+
//흐름 방향이 남쪽(아래)
- const downFlowSetupModule = (
- surfaceMaxLines,
- maxLengthLine,
- moduleSetupArray,
- moduleSetupSurface,
- containsBatchObjects,
- isCenter = false,
- intvHor,
- intvVer,
- ) => {
+ const downFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
let setupModule = []
const trestleDetailData = moduleSetupSurface.trestleDetail
@@ -993,11 +1988,38 @@ export function useModuleBasicSetting(tabNum) {
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
let isChidoriLine = false
let flowLines
+ let installedModuleMixYn
+ const isNorthSurface = moduleSetupSurface.isNorth
+ const isIncludeNorthModule = checkedModule.some((module) => module.northModuleYn === 'Y') //체크된 모듈 중에 북면 모듈이 있는지 확인하는 로직
+
+ let layoutRow = 0
+ let layoutCol = 0
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ const isPassed = checkAutoLayoutModuleSetup(moduleSetupSurface, trestleDetailData)
+ if (!isPassed) {
+ return
+ }
+ }
+
+ for (let moduleIndex = 0; moduleIndex < checkedModule.length; moduleIndex++) {
+ const module = checkedModule[moduleIndex]
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT && checkedLayoutData) {
+ const layout = checkedLayoutData.find((item) => module.itemId === item.moduleId)
+ layoutRow = layout.row
+ layoutCol = layout.col
+ }
+ //혼합여부에 따라 설치 여부 결정
+ if (installedModuleMixYn && installedModuleMixYn !== module.mixAsgYn) {
+ continue
+ }
+ //북면일때
+ const isNorthModuleYn = module.northModuleYn === 'Y'
- 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번 깔았던 모듈 기준으로 잡야아함
@@ -1010,24 +2032,44 @@ export function useModuleBasicSetting(tabNum) {
}
}
+ if (moduleSetupSurface.isSaleStoreNorthFlg) {
+ //북면일때
+ if (isIncludeNorthModule) {
+ if (!isNorthModuleYn && isNorthSurface) {
+ continue
+ } //흐름 방향이 북쪽(위)
+ }
+ } else {
+ if (isNorthSurface) {
+ if (!isNorthModuleYn) {
+ continue
+ }
+ }
+ }
+
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
const moduleArray = []
let calcAreaWidth = Math.abs(flowLines.right.x1 - flowLines.left.x1) //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
let calcModuleWidthCount = calcAreaWidth / (width + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
- let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
- let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
-
let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
let calcModuleHeightCount = calcAreaHeight / (height + intvVer + 1)
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
+ calcModuleHeightCount = layoutRow
+ }
+
+ let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
+ let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
+
let calcStartPoint = flowLines.right.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * width) / 2 : 0 //반씩 나눠서 중앙에 맞춤 bottom 기준으로 양변이 직선일때만 가운데 정렬
let startPointX = flowLines.left.x1 + calcStartPoint //시작점을 만든다
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
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
@@ -1035,7 +2077,7 @@ export function useModuleBasicSetting(tabNum) {
let chidoriLength = 0
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
- if (moduleIndex > 0) {
+ if (installedModuleHeightCount > 0) {
// moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
}
@@ -1045,18 +2087,18 @@ export function useModuleBasicSetting(tabNum) {
let moduleY = flowLines.bottom.y1 - height * i - 1 //살짝 여유를 준다
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
- if (moduleIndex > 0) {
- moduleY = installedLastHeightCoord - intvVer
+ if (installedModuleHeightCount > 0) {
+ moduleY = installedLastHeightCoord
}
//첫번째는 붙여서 두번째는 마진을 주고 설치
- heightMargin = i === 0 ? 0 : intvVer * i
+ heightMargin = installedModuleHeightCount === 0 ? 0 : intvVer
for (let j = 0; j < totalModuleWidthCount; j++) {
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
}
@@ -1107,23 +2149,15 @@ export function useModuleBasicSetting(tabNum) {
}
if (isInstall) {
++installedModuleHeightCount
+ installedModuleMixYn = module.mixAsgYn
}
}
setupModule.push(moduleArray)
- })
+ }
}
- const topFlowSetupModule = (
- surfaceMaxLines,
- maxLengthLine,
- moduleSetupArray,
- moduleSetupSurface,
- containsBatchObjects,
- isCenter = false,
- intvHor,
- intvVer,
- ) => {
+ const topFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
let setupModule = []
const trestleDetailData = moduleSetupSurface.trestleDetail
@@ -1133,8 +2167,36 @@ export function useModuleBasicSetting(tabNum) {
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
let isChidoriLine = false
let flowLines
+ let installedModuleMixYn
+ const isNorthSurface = moduleSetupSurface.isNorth
+ const isIncludeNorthModule = checkedModule.some((module) => module.northModuleYn === 'Y') //체크된 모듈 중에 북면 모듈이 있는지 확인하는 로직
+
+ let layoutRow = 0
+ let layoutCol = 0
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ const isPassed = checkAutoLayoutModuleSetup(moduleSetupSurface, trestleDetailData)
+ if (!isPassed) {
+ return
+ }
+ }
+
+ for (let moduleIndex = 0; moduleIndex < checkedModule.length; moduleIndex++) {
+ const module = checkedModule[moduleIndex]
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT && checkedLayoutData) {
+ const layout = checkedLayoutData.find((item) => module.itemId === item.moduleId)
+ layoutRow = layout.row
+ layoutCol = layout.col
+ }
+
+ //혼합여부에 따라 설치 여부 결정
+ if (installedModuleMixYn && installedModuleMixYn !== module.mixAsgYn) {
+ continue
+ }
+
+ const isNorthModuleYn = module.northModuleYn === 'Y'
- 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
@@ -1150,20 +2212,45 @@ export function useModuleBasicSetting(tabNum) {
}
}
- //흐름 방향이 북쪽(위)
+ if (moduleSetupSurface.isSaleStoreNorthFlg) {
+ //북면가능 설치 대리점이면
+ //북면일때
+ if (isIncludeNorthModule) {
+ //북면 모듈이 있는지 확인하는 로직
+ if (!isNorthModuleYn && isNorthSurface) {
+ //북면 모듈이 있으면 북면 모듈만 깔고 나머지는 스킵
+ continue
+ } //흐름 방향이 북쪽(위)
+ }
+ } else {
+ // 불면설치 불가 대리점이면
+ if (isNorthSurface) {
+ //북면일때
+ if (!isNorthModuleYn) {
+ //북면 모듈이 아니면 스킵
+ continue
+ }
+ }
+ }
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
const moduleArray = []
let calcAreaWidth = flowLines.right.x1 - flowLines.left.x1 //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
let calcModuleWidthCount = calcAreaWidth / (width + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
+ let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
+ let calcModuleHeightCount = calcAreaHeight / (height + intvVer + 1)
+
+ //단수지정 자동이면
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
+ calcModuleHeightCount = layoutRow
+ }
+
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다 //??어쩔때는 붙고 어쩔때는 안붙고 멋대로???
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
- let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
- let calcModuleHeightCount = calcAreaHeight / (height + intvVer + 1)
-
let calcStartPoint = flowLines.left.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * width) / 2 : 0 //반씩 나눠서 중앙에 맞춤 bottom 기준으로 양변이 직선일때만 가운데 정렬
let startPointX = flowLines.right.x1 - calcStartPoint //시작점을 만든다
@@ -1185,11 +2272,11 @@ export function useModuleBasicSetting(tabNum) {
let isInstall = false
let moduleY = flowLines.top.y1 + height * i //탑의 y점에서부터 아래로 그려 내려간다
- if (moduleIndex > 0) {
- moduleY = installedLastHeightCoord + intvVer + 1
+ if (installedModuleHeightCount > 0) {
+ moduleY = installedLastHeightCoord
}
- heightMargin = i === 0 ? 0 : intvVer * i //모듈간에 마진이 있어 마진값도 넣음
+ heightMargin = installedModuleHeightCount === 0 ? 0 : intvVer //모듈간에 마진이 있어 마진값도 넣음
for (let j = 0; j < totalModuleWidthCount; j++) {
//모듈 열수 만큼 반복
let moduleX = startPointX - width * j - 1 //시작점에서 우 -> 좌로 그려 내려간다
@@ -1240,25 +2327,17 @@ export function useModuleBasicSetting(tabNum) {
}
if (isInstall) {
++installedModuleHeightCount
+ installedModuleMixYn = module.mixAsgYn
}
}
setupModule.push(moduleArray)
- })
+ }
}
//남, 북과 같은 로직으로 적용하려면 좌우는 열 -> 행 으로 그려야함
//변수명은 bottom 기준으로 작성하여 동일한 방향으로 진행한다
- const leftFlowSetupModule = (
- surfaceMaxLines,
- maxLengthLine,
- moduleSetupArray,
- moduleSetupSurface,
- containsBatchObjects,
- isCenter = false,
- intvHor,
- intvVer,
- ) => {
+ const leftFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
let setupModule = []
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
@@ -1268,8 +2347,36 @@ export function useModuleBasicSetting(tabNum) {
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
let isChidoriLine = false
let flowLines
+ let installedModuleMixYn
+ const isNorthSurface = moduleSetupSurface.isNorth
+ const isIncludeNorthModule = checkedModule.some((module) => module.northModuleYn === 'Y') //체크된 모듈 중에 북면 모듈이 있는지 확인하는 로직
- checkedModule.forEach((module, moduleIndex) => {
+ let layoutRow = 0
+ let layoutCol = 0
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ const isPassed = checkAutoLayoutModuleSetup(moduleSetupSurface, trestleDetailData)
+ if (!isPassed) {
+ return
+ }
+ }
+
+ for (let moduleIndex = 0; moduleIndex < checkedModule.length; moduleIndex++) {
+ const module = checkedModule[moduleIndex]
+
+ //단수 지정이면
+ if (type === MODULE_SETUP_TYPE.LAYOUT && checkedLayoutData) {
+ const layout = checkedLayoutData.find((item) => module.itemId === item.moduleId)
+ layoutRow = layout.row
+ layoutCol = layout.col
+ }
+
+ //혼합여부에 따라 설치 여부 결정
+ if (installedModuleMixYn && installedModuleMixYn !== module.mixAsgYn) {
+ continue
+ }
+
+ const isNorthModuleYn = module.northModuleYn === 'Y'
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
@@ -1288,19 +2395,46 @@ export function useModuleBasicSetting(tabNum) {
}
}
+ if (moduleSetupSurface.isSaleStoreNorthFlg) {
+ //북면가능 설치 대리점이면
+ //북면일때
+ if (isIncludeNorthModule) {
+ //북면 모듈이 있는지 확인하는 로직
+ if (!isNorthModuleYn && isNorthSurface) {
+ //북면 모듈이 있으면 북면 모듈만 깔고 나머지는 스킵
+ continue
+ } //흐름 방향이 북쪽(위)
+ }
+ } else {
+ // 불면설치 불가 대리점이면
+ if (isNorthSurface) {
+ //북면일때
+ if (!isNorthModuleYn) {
+ //북면 모듈이 아니면 스킵
+ continue
+ }
+ }
+ }
+
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
const moduleArray = []
let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
let calcModuleWidthCount = calcAreaWidth / (height + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
+ let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
+ let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
+
+ //단수지정 자동이면
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
+ calcModuleHeightCount = layoutRow
+ }
+
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
- let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
- let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
-
let calcStartPoint = flowLines.bottom.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
let startPointX = flowLines.top.y1 + calcStartPoint //시작점을 만든다
@@ -1324,12 +2458,12 @@ export function useModuleBasicSetting(tabNum) {
let moduleY = flowLines.left.x1 + width * i + 1 //살짝 여유를 준다
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
- if (moduleIndex > 0) {
- moduleY = installedLastHeightCoord + intvHor
+ if (installedModuleHeightCount > 0) {
+ moduleY = installedLastHeightCoord
}
//첫번째는 붙여서 두번째는 마진을 주고 설치
- heightMargin = i === 0 ? 0 : intvHor * i
+ heightMargin = installedModuleHeightCount === 0 ? 0 : intvHor
for (let j = 0; j < totalModuleWidthCount; j++) {
let moduleX = startPointX + height * j + 1 //5정도 마진을 준다
@@ -1380,22 +2514,14 @@ export function useModuleBasicSetting(tabNum) {
if (isInstall) {
++installedModuleHeightCount
+ installedModuleMixYn = module.mixAsgYn
}
}
setupModule.push(moduleArray)
- })
+ }
}
- const rightFlowSetupModule = (
- surfaceMaxLines,
- maxLengthLine,
- moduleSetupArray,
- moduleSetupSurface,
- containsBatchObjects,
- isCenter = false,
- intvHor,
- intvVer,
- ) => {
+ const rightFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
let setupModule = []
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
@@ -1405,8 +2531,35 @@ export function useModuleBasicSetting(tabNum) {
let installedModuleHeightCount = 0 //마지막으로 설치된 모듈의 카운트
let isChidoriLine = false
let flowLines
+ let installedModuleMixYn
+ const isNorthSurface = moduleSetupSurface.isNorth
+ const isIncludeNorthModule = checkedModule.some((module) => module.northModuleYn === 'Y') //체크된 모듈 중에 북면 모듈이 있는지 확인하는 로직
- checkedModule.forEach((module, moduleIndex) => {
+ let layoutRow = 0
+ let layoutCol = 0
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ const isPassed = checkAutoLayoutModuleSetup(moduleSetupSurface, trestleDetailData)
+ if (!isPassed) {
+ return
+ }
+ }
+
+ for (let moduleIndex = 0; moduleIndex < checkedModule.length; moduleIndex++) {
+ const module = checkedModule[moduleIndex]
+
+ if (type === MODULE_SETUP_TYPE.LAYOUT && checkedLayoutData) {
+ const layout = checkedLayoutData.find((item) => module.itemId === item.moduleId)
+ layoutRow = layout.row
+ layoutCol = layout.col
+ }
+
+ //혼합여부에 따라 설치 여부 결정
+ if (installedModuleMixYn && installedModuleMixYn !== module.mixAsgYn) {
+ continue
+ }
+
+ const isNorthModuleYn = module.northModuleYn === 'Y'
const tmpModuleData = trestleDetailData.module.filter((moduleObj) => module.moduleTpCd === moduleObj.moduleTpCd)[0]
//혼합모듈일때는 mixModuleMaxRows 값이 0 이상임
let moduleMaxRows = tmpModuleData.mixModuleMaxRows === 0 ? tmpModuleData.moduleMaxRows : tmpModuleData.mixModuleMaxRows
@@ -1425,18 +2578,45 @@ export function useModuleBasicSetting(tabNum) {
}
}
+ if (moduleSetupSurface.isSaleStoreNorthFlg) {
+ //북면가능 설치 대리점이면
+ //북면일때
+ if (isIncludeNorthModule) {
+ //북면 모듈이 있는지 확인하는 로직
+ if (!isNorthModuleYn && isNorthSurface) {
+ //북면 모듈이 있으면 북면 모듈만 깔고 나머지는 스킵
+ continue
+ } //흐름 방향이 북쪽(위)
+ }
+ } else {
+ // 불면설치 불가 대리점이면
+ if (isNorthSurface) {
+ //북면일때
+ if (!isNorthModuleYn) {
+ //북면 모듈이 아니면 스킵
+ continue
+ }
+ }
+ }
+
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
const moduleArray = []
let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
let calcModuleWidthCount = calcAreaWidth / (height + intvHor + 1) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
+ let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
+ let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
+
+ //단수지정 자동이면
+ if (type === MODULE_SETUP_TYPE.LAYOUT) {
+ calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
+ calcModuleHeightCount = layoutRow
+ }
+
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
- let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
- let calcModuleHeightCount = calcAreaHeight / (width + intvVer + 1)
-
let calcStartPoint = flowLines.top.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
let startPointX = flowLines.bottom.y2 - calcStartPoint //시작점을 만든다
@@ -1460,12 +2640,12 @@ export function useModuleBasicSetting(tabNum) {
let moduleY = flowLines.right.x1 - width * i - 1 //살짝 여유를 준다
//두번째 모듈 -> 혼합일 경우의 설치될 모듈 높이를 계산
- if (moduleIndex > 0) {
- moduleY = installedLastHeightCoord - intvHor
+ if (installedModuleHeightCount > 0) {
+ moduleY = installedLastHeightCoord
}
//첫번째는 붙여서 두번째는 마진을 주고 설치
- heightMargin = i === 0 ? 0 : intvHor * i
+ heightMargin = installedModuleHeightCount === 0 ? 0 : intvHor
for (let j = 0; j < totalModuleWidthCount; j++) {
let moduleX = startPointX - height * j - 1 //5정도 마진을 준다
@@ -1519,11 +2699,12 @@ export function useModuleBasicSetting(tabNum) {
if (isInstall) {
++installedModuleHeightCount
+ installedModuleMixYn = module.mixAsgYn
}
}
setupModule.push(moduleArray)
- })
+ }
}
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
@@ -1532,7 +2713,6 @@ export function useModuleBasicSetting(tabNum) {
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
const containsBatchObjects = objectsIncludeSurface(turfModuleSetupSurface) //배치면에 오브젝트(도머, 개구등)이 있는지 확인하는 로직
- const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
return acc.length > cur.length ? acc : cur
@@ -1553,62 +2733,33 @@ export function useModuleBasicSetting(tabNum) {
if (setupLocation === 'eaves') {
// 흐름방향이 남쪽일때
if (moduleSetupSurface.direction === 'south') {
- downFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
if (moduleSetupSurface.direction === 'west') {
- leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
if (moduleSetupSurface.direction === 'east') {
- rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
if (moduleSetupSurface.direction === 'north') {
- topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
} else if (setupLocation === 'ridge') {
//용마루
if (moduleSetupSurface.direction === 'south') {
- topFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
if (moduleSetupSurface.direction === 'west') {
- rightFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
if (moduleSetupSurface.direction === 'east') {
- leftFlowSetupModule(surfaceMaxLines, maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, false, intvHor, intvVer)
+ leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
}
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)
+ downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, 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) {
@@ -1634,8 +2785,22 @@ export function useModuleBasicSetting(tabNum) {
// moduleSetupArray: setupedModules,
// })
// setModuleIsSetup(moduleArray)
+
+ //그림자는 무조건 가장 앞으로
+ const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
+ if (shadowObj) {
+ shadowObj.bringToFront()
+ }
})
// calculateForApi()
+
+ /**
+ * 자동 레이아웃일떄 설치 가능한 애들은 설치 해주고 실패한 애들은 이름, 최대 설치 가능한 높이 알려줄라고
+ */
+ if (type === MODULE_SETUP_TYPE.LAYOUT && failAutoSetupRoof.length) {
+ const roofNames = failAutoSetupRoof.map((roof) => roof.roofMaterial.roofMatlNmJp).join(', ')
+ swalFire({ text: getMessage('modal.module.basic.setting.module.placement.over.max.row', [roofNames]), icon: 'warning' })
+ }
}
const coordToTurfPolygon = (points) => {
@@ -1666,8 +2831,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(
@@ -1677,8 +2843,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: Infinity, y1: Infinity, index: -1 },
)
+
flowArray.push(topFlow)
let rtnObjArray = []
@@ -1728,14 +2895,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()
@@ -1786,8 +2946,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(
@@ -1797,8 +2958,9 @@ export function useModuleBasicSetting(tabNum) {
}
return acc
},
- { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ { x1: -Infinity, y1: -Infinity, index: -1 },
)
+
flowArray.push(rightFlow)
let rtnObjArray = []
@@ -1854,11 +3016,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()
@@ -1940,12 +3098,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
}
@@ -1954,13 +3107,13 @@ export function useModuleBasicSetting(tabNum) {
let moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
if (isManualModuleSetup) {
if (checkedModule.length === 0) {
- swalFire({ text: getMessage('module.place.select.module') })
+ swalFire({ text: getMessage('module.place.select.module'), icon: 'warning' })
setIsManualModuleSetup(!isManualModuleSetup)
return
}
if (checkedModule.length > 1) {
- swalFire({ text: getMessage('module.place.select.one.module') })
+ swalFire({ text: getMessage('module.place.select.one.module'), icon: 'warning' })
setIsManualModuleSetup(!isManualModuleSetup)
return
}
@@ -1978,7 +3131,7 @@ export function useModuleBasicSetting(tabNum) {
) //도머s 객체
let moduleOptions = {
- fill: '#BFFD9F',
+ fill: checkedModule[0].color,
stroke: 'black',
strokeWidth: 0.3,
selectable: true, // 선택 가능하게 설정
@@ -2011,14 +3164,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)
})
@@ -2027,6 +3181,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()
@@ -2090,131 +3248,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()
@@ -2235,15 +3420,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'), icon: 'warning' })
+ 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 },
]
@@ -2266,7 +3459,7 @@ export function useModuleBasicSetting(tabNum) {
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
//겹치면 안됨
if (intersection) {
- swalFire({ text: getMessage('module.place.overobject') })
+ swalFire({ text: getMessage('module.place.overobject'), icon: 'warning' })
isIntersection = false
}
})
@@ -2284,10 +3477,10 @@ export function useModuleBasicSetting(tabNum) {
manualDrawModules.push(manualModule)
setModuleStatisticsData()
} else {
- swalFire({ text: getMessage('module.place.overlab') })
+ swalFire({ text: getMessage('module.place.overlab'), icon: 'warning' })
}
} else {
- swalFire({ text: getMessage('module.place.out') })
+ swalFire({ text: getMessage('module.place.out'), icon: 'warning' })
}
}
})
@@ -2298,10 +3491,7 @@ export function useModuleBasicSetting(tabNum) {
if (moduleSetupSurfaces) {
//수동모드 해제시 모듈 설치면 선택 잠금
moduleSetupSurfaces.forEach((obj) => {
- obj.set({
- selectable: true,
- evented: true,
- })
+ obj.set({ selectable: true, evented: true })
})
}
@@ -2314,6 +3504,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'), icon: 'warning' })
+ return
+ }
+
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
?.getObjects()
@@ -2379,6 +3579,7 @@ export function useModuleBasicSetting(tabNum) {
targetRoof.setCoords()
targetSurface.setCoords()
moduleSetupSurfaces.push(targetSurface)
+ // drawDirectionArrow(targetSurface)
}
canvas.remove(obj)
})
@@ -2387,6 +3588,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()
@@ -2469,8 +3674,6 @@ export function useModuleBasicSetting(tabNum) {
return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
}
- let moduleGroup = []
-
const flatRoofDownFlowSetupModule = (
surfaceMaxLines,
maxLengthLine,
@@ -2742,11 +3945,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..6a6ce498 100644
--- a/src/hooks/module/useModuleSelection.js
+++ b/src/hooks/module/useModuleSelection.js
@@ -3,7 +3,7 @@ import { useContext, useEffect, useState } from 'react'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useCommonCode } from '@/hooks/common/useCommonCode'
-import { selectedModuleState, moduleSelectionInitParamsState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
+import { selectedModuleState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
@@ -12,23 +12,20 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
export function useModuleSelection(props) {
const canvas = useRecoilValue(canvasState)
- const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
-
+ const { managementState, setManagementState } = 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 [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
+
+ // const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
const { getModuleTypeItemList } = useMasterController()
const { findCommonCode } = useCommonCode()
const resetStatisticsData = useResetRecoilState(moduleStatisticsState)
-
const { restoreModuleInstArea } = useModuleBasicSetting()
const bindInitData = () => {
@@ -43,21 +40,21 @@ export function useModuleSelection(props) {
//탭별 파라메터 초기화
useEffect(() => {
bindInitData()
- const initParams = {
- illuminationTp: managementState?.surfaceTypeValue, //면조도
- illuminationTpNm: managementState?.surfaceType, //면조도명
- instHt: managementState?.installHeight, //설치높이
- stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
- stdSnowLd: managementState?.verticalSnowCover, //기준적설량
- saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부
- }
+ // const initParams = {
+ // illuminationTp: managementState?.surfaceTypeValue, //면조도
+ // illuminationTpNm: managementState?.surfaceType, //면조도명
+ // instHt: managementState?.installHeight, //설치높이
+ // stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
+ // stdSnowLd: managementState?.verticalSnowCover, //기준적설량
+ // saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부
+ // }
- if (selectedModules) {
- initParams.moduleTpCd = selectedModules.itemTp
- initParams.moduleItemId = selectedModules.itemId
- }
+ // if (selectedModules) {
+ // initParams.moduleTpCd = selectedModules.itemTp
+ // initParams.moduleItemId = selectedModules.itemId
+ // }
- setModuleSelectionInitParams(initParams)
+ // setModuleSelectionInitParams(initParams)
}, [managementState])
useEffect(() => {
@@ -87,7 +84,7 @@ export function useModuleSelection(props) {
//새로고침시 데이터 날아가는거 방지
if (managementState === null) {
- setManagementState(managementStateLoaded)
+ setManagementState(managementState)
} else {
bindInitData()
}
@@ -95,7 +92,7 @@ export function useModuleSelection(props) {
getModuleData(roofsIds)
//모듈설치면 초기화
- restoreModuleInstArea()
+ // restoreModuleInstArea()
resetStatisticsData()
}, [])
@@ -125,19 +122,19 @@ export function useModuleSelection(props) {
setSelectedModules(option) //선택값 저장
//init 데이터에 선택된 모듈 추가
- setModuleSelectionInitParams({
- ...moduleSelectionInitParams,
- moduleTpCd: option.itemTp,
- moduleItemId: option.itemId,
- })
+ // setModuleSelectionInitParams({
+ // ...moduleSelectionInitParams,
+ // moduleTpCd: option.itemTp,
+ // moduleItemId: option.itemId,
+ // })
}
const handleChangeSurfaceType = (option) => {
- setModuleSelectionInitParams({
- ...moduleSelectionInitParams,
- illuminationTp: option.clCode,
- illuminationTpNm: option.clCodeNm,
- })
+ // setModuleSelectionInitParams({
+ // ...moduleSelectionInitParams,
+ // illuminationTp: option.clCode,
+ // illuminationTpNm: option.clCodeNm,
+ // })
setManagementState({
...managementState,
@@ -147,10 +144,10 @@ export function useModuleSelection(props) {
}
const handleChangeWindSpeed = (option) => {
- setModuleSelectionInitParams({
- ...moduleSelectionInitParams,
- stdWindSpeed: option.clCode,
- })
+ // setModuleSelectionInitParams({
+ // ...moduleSelectionInitParams,
+ // stdWindSpeed: option.clCode,
+ // })
setManagementState({
...managementState,
@@ -160,10 +157,10 @@ export function useModuleSelection(props) {
const handleChangeInstallHeight = (option) => {
setInstallHeight(option)
- setModuleSelectionInitParams({
- ...moduleSelectionInitParams,
- instHt: option,
- })
+ // setModuleSelectionInitParams({
+ // ...moduleSelectionInitParams,
+ // instHt: option,
+ // })
setManagementState({
...managementState,
@@ -173,10 +170,10 @@ export function useModuleSelection(props) {
const handleChangeVerticalSnowCover = (option) => {
setVerticalSnowCover(option)
- setModuleSelectionInitParams({
- ...moduleSelectionInitParams,
- stdSnowLd: option,
- })
+ // setModuleSelectionInitParams({
+ // ...moduleSelectionInitParams,
+ // stdSnowLd: option,
+ // })
setManagementState({
...managementState,
@@ -184,53 +181,23 @@ export function useModuleSelection(props) {
})
}
- useEffect(() => {
- // console.log('installHeight', installHeight)
- }, [installHeight])
-
- useEffect(() => {
- // console.log('verticalSnowCover', verticalSnowCover)
- }, [verticalSnowCover])
-
- //TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨
-
- // useEffect(() => {
- // getConstructionListData(constructionListParams)
- // }, [constructionListParams])
-
- // const getConstructionListData = async (params) => {
- // if (params.trestleMkrCd && params.constMthdCd && params.roofBaseCd) {
- // const optionsList = await getConstructionList(params)
- // console.log('optionsList', optionsList)
- // setConstructionList(optionsList.data)
- // }
- // }
-
- //state 배열에 데이터 추가 함수
- // const addRoofTabParams = (key, value, excludeArray = []) => {
- // const index = roofTabParams.findIndex((obj) => obj.roofTab === roofTab)
- // if (index !== -1) {
- // roofTabParams[index][key] = value
- // if (excludeArray.length > 0) {
- // excludeArray.forEach((exclude) => {
- // roofTabParams[index][exclude] = ''
- // })
- // }
- // setRoofTabParams((prev) => [...prev.slice(0, index), roofTabParams[index], ...prev.slice(index + 1)])
- // }
- // }
-
return {
- moduleSelectionInitParams,
+ // moduleSelectionInitParams,
selectedModules,
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..0143e92e
--- /dev/null
+++ b/src/hooks/module/useModuleTrestle.js
@@ -0,0 +1,302 @@
+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_LENGTH':
+ 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,
+ raft: action.roof?.raft ?? 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)
+ setEavesMargin(0)
+ setRidgeMargin(0)
+ setKerabaMargin(0)
+ return
+ }
+
+ handleSetConstMthdList()
+ if (!trestleState?.constMthdCd) {
+ setRoofBaseList([])
+ setConstructionList([])
+ setTrestleDetail(null)
+ setEavesMargin(0)
+ setRidgeMargin(0)
+ setKerabaMargin(0)
+ return
+ }
+
+ handleSetRoofBaseList()
+ if (!trestleState?.roofBaseCd) {
+ setConstructionList([])
+ setTrestleDetail(null)
+ setEavesMargin(0)
+ setRidgeMargin(0)
+ setKerabaMargin(0)
+ return
+ }
+
+ handleSetConstructionList()
+ if (!trestleState?.constTp) {
+ setTrestleDetail(null)
+ setEavesMargin(0)
+ setRidgeMargin(0)
+ setKerabaMargin(0)
+ return
+ }
+
+ if (!trestleState?.eavesMargin) {
+ handleSetTrestleDetailData()
+ }
+ }
+ }, [trestleState])
+
+ const handleSetTrestleList = () => {
+ setPopSpinnerStore(true)
+ getTrestleList({
+ moduleTpCd: trestleState?.moduleTpCd ?? '',
+ roofMatlCd: trestleState?.roofMatlCd ?? '',
+ raftBaseCd: trestleState?.raft ?? '',
+ })
+ .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?.raft ?? '',
+ 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?.raft ?? '',
+ 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.raft ?? '',
+ 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.length ?? '',
+ workingWidth: lengthBase ?? '',
+ },
+ ])
+ .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..32eef2a2 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,136 @@ 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
+ let moduleIntvlHor, moduleIntvlVer
+ if (+roofSizeSet === 3) {
+ //육지붕의 경우 값 고정
+ moduleIntvlHor = 300
+ moduleIntvlVer = 100
+ } else {
+ moduleIntvlHor = trestleDetail.moduleIntvlHor
+ moduleIntvlVer = trestleDetail.moduleIntvlVer
+ }
+
+ 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 +3046,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 2688502a..7c75ed30 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 1510825b..82c47447 100644
--- a/src/hooks/roofcover/useAuxiliaryDrawing.js
+++ b/src/hooks/roofcover/useAuxiliaryDrawing.js
@@ -5,16 +5,6 @@ import { useEvent } from '@/hooks/useEvent'
import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid'
-import {
- outerLineAngle1State,
- outerLineAngle2State,
- outerLineArrow1State,
- outerLineArrow2State,
- outerLineDiagonalState,
- outerLineLength1State,
- outerLineLength2State,
- outerLineTypeState,
-} from '@/store/outerLineAtom'
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
@@ -23,6 +13,16 @@ import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { POLYGON_TYPE } from '@/common/common'
import { useMessage } from '../useMessage'
+import {
+ auxiliaryLineAngle1State,
+ auxiliaryLineAngle2State,
+ auxiliaryLineArrow1State,
+ auxiliaryLineArrow2State,
+ auxiliaryLineDiagonalState,
+ auxiliaryLineLength1State,
+ auxiliaryLineLength2State,
+ auxiliaryLineTypeState,
+} from '@/store/auxiliaryLineAtom'
// 보조선 작성
export function useAuxiliaryDrawing(id, isUseEffect = true) {
@@ -49,20 +49,20 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const length2Ref = useRef(0)
const angle1Ref = useRef('')
const angle2Ref = useRef('')
- const [length1, setLength1] = useRecoilState(outerLineLength1State)
- const [length2, setLength2] = useRecoilState(outerLineLength2State)
- const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
- const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
- const [type, setType] = useRecoilState(outerLineTypeState)
- const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
- const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
- const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState)
+ const [length1, setLength1] = useRecoilState(auxiliaryLineLength1State)
+ const [length2, setLength2] = useRecoilState(auxiliaryLineLength2State)
+ const [arrow1, setArrow1] = useRecoilState(auxiliaryLineArrow1State)
+ const [arrow2, setArrow2] = useRecoilState(auxiliaryLineArrow2State)
+ const [type, setType] = useRecoilState(auxiliaryLineTypeState)
+ const [angle1, setAngle1] = useRecoilState(auxiliaryLineAngle1State)
+ const [angle2, setAngle2] = useRecoilState(auxiliaryLineAngle2State)
+ const [auxiliaryLineDiagonalLength, setAuxiliaryLineDiagonalLength] = useRecoilState(auxiliaryLineDiagonalState)
const arrow1Ref = useRef(arrow1)
const arrow2Ref = useRef(arrow2)
const typeRef = useRef(type)
- const outerLineDiagonalLengthRef = useRef(0)
+ const auxiliaryLineDiagonalLengthRef = useRef(0)
const intersectionPoints = useRef([])
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
@@ -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
}
@@ -124,7 +124,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
setAngle1(0)
setAngle2(0)
- setOuterLineDiagonalLength(0)
+ setAuxiliaryLineDiagonalLength(0)
}
const move = (object, x, y) => {
@@ -408,7 +408,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
//대각선 완료될 경우 확인
const checkDiagonal = (direction) => {
const activeElem = document.activeElement
- const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이
+ const diagonalLength = auxiliaryLineDiagonalLengthRef.current.value // 대각선 길이
const length1Value = length1Ref.current.value
@@ -919,9 +919,9 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
angle2,
setAngle2,
angle2Ref,
- outerLineDiagonalLength,
- setOuterLineDiagonalLength,
- outerLineDiagonalLengthRef,
+ auxiliaryLineDiagonalLength,
+ setAuxiliaryLineDiagonalLength,
+ auxiliaryLineDiagonalLengthRef,
type,
setType,
handleFix,
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 facebd71..e0418a75 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) {
@@ -248,7 +249,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 00c17c1d..f8ad7d30 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -11,6 +11,8 @@ import {
roofDisplaySelector,
roofMaterialsSelector,
selectedRoofMaterialSelector,
+ settingModalFirstOptionsState,
+ corridorDimensionSelector,
} from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common'
@@ -27,6 +29,8 @@ 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)
@@ -52,9 +56,11 @@ export function useRoofAllocationSetting(id) {
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(() => {
/** 배치면 초기설정에서 선택한 지붕재 배열 설정 */
@@ -69,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])
@@ -100,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)
}
@@ -113,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) {
@@ -185,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)
@@ -245,28 +234,42 @@ 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 })
}
/**
@@ -290,22 +293,46 @@ export function useRoofAllocationSetting(id) {
*/
const handleSaveContext = () => {
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)
+ })
+ })
}
/**
@@ -315,11 +342,7 @@ export function useRoofAllocationSetting(id) {
if (!checkInnerLines()) {
apply()
} else {
- swalFire({
- type: 'alert',
- icon: 'error',
- text: getMessage('실제치수를 입력해 주세요.'),
- })
+ swalFire({ type: 'alert', icon: 'error', text: getMessage('실제치수를 입력해 주세요.') })
}
}
@@ -334,11 +357,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
}
})
@@ -386,10 +405,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)
@@ -397,9 +413,7 @@ export function useRoofAllocationSetting(id) {
roofs.forEach((roof) => {
if (roof.isFixed) return
- roof.set({
- isFixed: true,
- })
+ roof.set({ isFixed: true })
/** 모양 패턴 설정 */
setSurfaceShapePattern(
@@ -411,6 +425,8 @@ export function useRoofAllocationSetting(id) {
drawDirectionArrow(roof)
})
+ setRoofMaterials(newRoofList)
+ setRoofsStore(newRoofList)
/** 외곽선 삭제 */
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine' || obj.name === 'pitchText')
removeTargets.forEach((obj) => {
@@ -419,9 +435,12 @@ export function useRoofAllocationSetting(id) {
setEditingLines([])
closeAll()
setSelectedMenu('surface')
+ //지붕면 완성 후 실측치 로 보이도록 수정
+ setCorridorDimension(1)
/** 모듈 선택 데이터 초기화 */
- modifyModuleSelectionData()
+ // modifyModuleSelectionData()
+ setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
}
/**
@@ -434,10 +453,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' })
}
})
})
@@ -452,7 +468,7 @@ export function useRoofAllocationSetting(id) {
const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id)
const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) {
- return { ...selectedRoofMaterial, selected: roof.selected }
+ return { ...selectedRoofMaterial, selected: roof.selected, index }
}
return roof
})
@@ -542,7 +558,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 1b4036c0..33931c8e 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -178,7 +178,7 @@ export function useRoofShapeSetting(id) {
let direction
if (outerLines.length < 2) {
- swalFire({ text: '외벽선이 없습니다.', icon: 'error' })
+ swalFire({ text: getMessage('wall.line.not.found') })
return
}
@@ -218,6 +218,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
+ }
}
})
}
@@ -703,6 +718,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 a69a4a19..31d8f95f 100644
--- a/src/hooks/roofcover/useWallLineOffsetSetting.js
+++ b/src/hooks/roofcover/useWallLineOffsetSetting.js
@@ -63,8 +63,9 @@ export function useWallLineOffsetSetting(id) {
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
- if (!outerLineFix || outerLines.length === 0) {
- swalFire({ text: '외벽선이 없습니다.' })
+
+ if (outerLines.length === 0) {
+ 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..d53faa39 100644
--- a/src/hooks/surface/usePlacementShapeDrawing.js
+++ b/src/hooks/surface/usePlacementShapeDrawing.js
@@ -14,7 +14,7 @@ import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid'
import { useEffect, useRef } from 'react'
-import { distanceBetweenPoints } from '@/util/canvas-util'
+import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
import { fabric } from 'fabric'
import { calculateAngle } from '@/util/qpolygon-utils'
import {
@@ -32,16 +32,18 @@ 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'
import PlacementSurfaceLineProperty from '@/components/floor-plan/modal/placementShape/PlacementSurfaceLineProperty'
+import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
// 배치면 그리기
export function usePlacementShapeDrawing(id) {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
- const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
+ const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseLine } =
useEvent()
// const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
// useContext(EventContext)
@@ -50,6 +52,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)
@@ -115,6 +119,7 @@ export function usePlacementShapeDrawing(id) {
}, [type])
const clear = () => {
+ addCanvasMouseEventListener('mouse:move', mouseMove)
setLength1(0)
setLength2(0)
@@ -170,6 +175,80 @@ export function usePlacementShapeDrawing(id) {
}
}
+/*
+mouseMove
+ */
+ const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ const roofAdsorptionPoints = useRef([])
+ const intersectionPoints = useRef([])
+ const { getAdsorptionPoints } = useAdsorptionPoint()
+
+ const mouseMove = (e) => {
+ removeMouseLine();
+ const pointer = canvas.getPointer(e.e)
+ const roofsPoints = roofs.map((roof) => roof.points).flat()
+ roofAdsorptionPoints.current = [...roofsPoints]
+
+ const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed)
+ const otherAdsorptionPoints = []
+
+ auxiliaryLines.forEach((line1) => {
+ auxiliaryLines.forEach((line2) => {
+ if (line1 === line2) {
+ return
+ }
+
+ const intersectionPoint = calculateIntersection(line1, line2)
+ if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) {
+ return
+ }
+ otherAdsorptionPoints.push(intersectionPoint)
+ })
+ })
+
+ let innerLinePoints = []
+ canvas
+ .getObjects()
+ .filter((obj) => obj.innerLines)
+ .forEach((polygon) => {
+ polygon.innerLines.forEach((line) => {
+ innerLinePoints.push({ x: line.x1, y: line.y1 })
+ innerLinePoints.push({ x: line.x2, y: line.y2 })
+ })
+ })
+
+ const adsorptionPoints = [
+ ...getAdsorptionPoints(),
+ ...roofAdsorptionPoints.current,
+ ...otherAdsorptionPoints,
+ ...intersectionPoints.current,
+ ...innerLinePoints,
+ ]
+
+ let arrivalPoint = { x: pointer.x, y: pointer.y }
+ let adsorptionPoint = findClosestPoint(pointer, adsorptionPoints)
+
+ if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) <= adsorptionRange) {
+ arrivalPoint = { ...adsorptionPoint }
+ }
+ const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], {
+ stroke: 'red',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'mouseLine',
+ })
+
+ const verticalLine = new fabric.Line([arrivalPoint.x, -1 * canvas.height, arrivalPoint.x, 2 * canvas.height], {
+ stroke: 'red',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'mouseLine',
+ })
+ canvas?.add(horizontalLine, verticalLine)
+ canvas?.renderAll()
+
+ }
+
useEffect(() => {
canvas
?.getObjects()
@@ -253,11 +332,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..71140097 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, POLYGON_TYPE } 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,8 +204,20 @@ 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) {
+ if (obj.type === 'QPolygon') {
+ const originStroke = obj.stroke
obj.set({ stroke: 'red' })
+
+ if (currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
+ if (obj.name === POLYGON_TYPE.MODULE) {
+ obj.set({ strokeWidth: 3 })
+ }
+ if (obj.name === POLYGON_TYPE.ROOF) {
+ canvas.discardActiveObject()
+ obj.set({ stroke: originStroke })
+ }
+ }
}
})
canvas.renderAll()
@@ -215,6 +233,9 @@ export function useCanvasEvent() {
if (obj.name !== 'moduleSetupSurface') {
obj.set({ stroke: 'black' })
}
+ if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
+ obj.set({ strokeWidth: 0.3 })
+ }
}
})
}
@@ -229,14 +250,22 @@ export function useCanvasEvent() {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
+ if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
+ //모듈 미선택시 라인 두께 변경
+ obj.set({ strokeWidth: 0.3 })
+ }
}
})
}
if (selected?.length > 0) {
selected.forEach((obj) => {
- if (obj.type === 'QPolygon' && obj.name !== 'module') {
+ if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })
+ if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
+ //모듈 선택시 라인 두께 변경
+ obj.set({ strokeWidth: 3 })
+ }
}
})
}
diff --git a/src/hooks/useCirCuitTrestle.js b/src/hooks/useCirCuitTrestle.js
index 027b965a..60c2b74f 100644
--- a/src/hooks/useCirCuitTrestle.js
+++ b/src/hooks/useCirCuitTrestle.js
@@ -24,14 +24,14 @@ export function useCircuitTrestle(executeEffect = false) {
const [selectedModels, setSelectedModels] = useRecoilState(selectedModelsState)
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const selectedModules = useRecoilValue(selectedModuleState)
- const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
+ const { managementState } = useContext(GlobalDataContext)
const canvas = useRecoilValue(canvasState)
const setModuleStatistics = useSetRecoilState(moduleStatisticsState)
const resetModuleStatistics = useResetRecoilState(moduleStatisticsState)
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/useEstimate.js b/src/hooks/useEstimate.js
index fae0b558..3536fda9 100644
--- a/src/hooks/useEstimate.js
+++ b/src/hooks/useEstimate.js
@@ -13,7 +13,7 @@ import { useTrestle } from '@/hooks/module/useTrestle'
import { usePlan } from '@/hooks/usePlan'
export function useEstimate() {
- const { managementStateLoaded } = useContext(GlobalDataContext)
+ const { managementState } = useContext(GlobalDataContext)
const { setIsGlobalLoading } = useContext(QcastContext)
const router = useRouter()
const loginUserState = useRecoilValue(loginUserStore)
@@ -31,16 +31,18 @@ export function useEstimate() {
* @param {Object} estimateParam - 견적서 저장 데이터
*/
const saveEstimate = async (estimateParam) => {
+ console.log('managementState', managementState)
+
const userId = loginUserState.userId
- const saleStoreId = managementStateLoaded.saleStoreId
+ const saleStoreId = managementState.saleStoreId
const objectNo = currentCanvasPlan.objectNo
const planNo = currentCanvasPlan.planNo
const slope = estimateParam.roofSurfaceList[0].slope
const angle = estimateParam.roofSurfaceList[0].angle
- const surfaceType = managementStateLoaded.surfaceType
- const setupHeight = managementStateLoaded.installHeight
- const standardWindSpeedId = managementStateLoaded.standardWindSpeedId
- const snowfall = managementStateLoaded.verticalSnowCover
+ const surfaceType = managementState.surfaceType
+ const setupHeight = managementState.installHeight
+ const standardWindSpeedId = managementState.standardWindSpeedId
+ const snowfall = managementState.verticalSnowCover
const drawingFlg = '1'
const saveEstimateData = {
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/useLine.js b/src/hooks/useLine.js
index bdbd5d6f..fcf14312 100644
--- a/src/hooks/useLine.js
+++ b/src/hooks/useLine.js
@@ -53,8 +53,8 @@ export const useLine = () => {
})
canvas
?.getObjects()
- .find((obj) => obj.parentId === line.id)
- .set({
+ ?.find((obj) => obj.parentId === line.id)
+ ?.set({
visible: true,
})
canvas?.renderAll()
diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js
index 013e00c4..15783727 100644
--- a/src/hooks/usePlan.js
+++ b/src/hooks/usePlan.js
@@ -20,6 +20,8 @@ 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'
+import { unescapeString } from '@/util/common-utils'
/**
* 플랜 처리 훅
@@ -52,6 +54,9 @@ export function usePlan(params = {}) {
const { fetchBasicSettings, basicSettingCopySave } = useCanvasSetting()
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
+ /** 전역 로딩바 컨텍스트 */
+ const { setIsGlobalLoading } = useContext(QcastContext)
+
/**
* 플랜 복사 시 모듈이 있을경우 모듈 데이터 복사하기 위한 처리
*/
@@ -260,7 +265,7 @@ export function usePlan(params = {}) {
objectNo,
planNo: parseInt(newPlan.planNo),
popupType: 1,
- popupStatus: sourceDegree.popupStatus,
+ popupStatus: unescapeString(sourceDegree.popupStatus),
}
console.log('🚀 ~ postObjectPlan ~ degreeData:', degreeData)
await post({ url: `/api/v1/canvas-popup-status`, data: degreeData })
@@ -402,19 +407,28 @@ export function usePlan(params = {}) {
}
})
} else {
- if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
- await saveCanvas(true)
- clearRecoilState()
- }
- setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
- setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
+ swalFire({
+ text: getMessage('plan.message.confirm.save'),
+ type: 'confirm',
+ confirmFn: async () => {
+ //저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
+ await saveCanvas(true)
+ clearRecoilState()
+ setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
+ setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
+ },
+ denyFn: async () => {
+ setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
+ setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
+ },
+ })
}
}
useEffect(() => {
setSelectedPlan(currentCanvasPlan)
handleCurrentPlanUrl()
- resetCurrentObject()
+ // resetCurrentObject()
resetModuleSetupSurface()
}, [currentCanvasPlan])
@@ -442,23 +456,46 @@ export function usePlan(params = {}) {
* @param {string} objectNo - 물건번호
*/
const handleAddPlan = async (userId, objectNo) => {
+ let isSelected = false
+
if (currentCanvasPlan?.id) {
- await saveCanvas(false)
+ swalFire({
+ text: getMessage('plan.message.confirm.save'),
+ type: 'confirm',
+ confirmFn: async () => {
+ //저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
+ await saveCanvas(true)
+ handleAddPlanCopyConfirm(userId, objectNo)
+ },
+ denyFn: async () => {
+ handleAddPlanCopyConfirm(userId, objectNo)
+ },
+ })
}
- 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)
}
+ const handleAddPlanCopyConfirm = async (userId, objectNo) => {
+ 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)
+ }
+ }
/**
* 물건번호(object) plan 삭제 (canvas 삭제 전 planNo 삭제)
*
diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js
index 735a85f5..7e2e28ee 100644
--- a/src/hooks/usePolygon.js
+++ b/src/hooks/usePolygon.js
@@ -183,6 +183,10 @@ export const usePolygon = () => {
* @param showDirectionText
*/
const drawDirectionArrow = (polygon, showDirectionText = true) => {
+ if (!polygon) {
+ return
+ }
+
if (polygon.points.length < 3) {
return
}
diff --git a/src/lib/authActions.js b/src/lib/authActions.js
index 5a63d684..5e7055d0 100644
--- a/src/lib/authActions.js
+++ b/src/lib/authActions.js
@@ -48,6 +48,7 @@ export async function setSession(data) {
session.pwdInitYn = data.pwdInitYn
session.custCd = data.custCd
session.isLoggedIn = true
+ session.builderNo = data.builderNo
await session.save()
}
diff --git a/src/locales/ja.json b/src/locales/ja.json
index b0db31ce..35f0b757 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -3,7 +3,7 @@
"welcome": "ようこそ。 {0}さん",
"header.menus.home": "ホーム",
"header.menus.management": "見積書管理画面",
- "header.menus.management.newStuff": "新規見積登録",
+ "header.menus.management.newStuff": "新規物件登録",
"header.menus.management.detail": "物件詳細",
"header.menus.management.stuffList": "物件検索",
"header.menus.community": "コミュニティ",
@@ -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,20 @@
"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.under.roof": "屋根下地",
"modal.module.basic.setting.module.setting": "モジュールの選択",
+ "modal.module.basic.setting.module.placement.area": "モジュール配置領域",
+ "modal.module.basic.setting.module.placement.margin": "モジュール間の間隙",
+ "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.placement.margin.horizontal": "左右",
+ "modal.module.basic.setting.module.placement.margin.vertical": "上下",
"modal.module.basic.setting.module.hajebichi": "ハゼピッチ",
"modal.module.basic.setting.module.setting.info1": "※勾配の範囲には制限があります。屋根傾斜が2.5値未満10値を超える場合は、施工が可能かどうか施工マニュアルを確認してください。",
"modal.module.basic.setting.module.setting.info2": "※モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ずご確認ください。",
@@ -105,32 +113,50 @@
"modal.module.basic.setting.module.standard.snowfall.amount": "積雪量",
"modal.module.basic.setting.module.standard.construction": "標準施工",
"modal.module.basic.setting.module.enforce.construction": "強化施工",
- "modal.module.basic.setting.module.multiple.construction": "多設施工",
+ "modal.module.basic.setting.module.multiple.construction": "多雪施工",
"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.settting.module.error12": "施工方法を選択してください。\n(屋根材: {0})",
"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": "自動配置",
+ "modal.module.basic.setting.auto.row.batch": "自動レイアウト指定",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting.alloc.trestle": "架台配置",
@@ -142,7 +168,7 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "標準枚数",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "最大枚数",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流",
- "modal.circuit.trestle.setting.power.conditional.select.check1": "同じ傾斜同じ方向の面積ケース同じ面として回路分ける。",
+ "modal.circuit.trestle.setting.power.conditional.select.check1": "同一勾配・同一方面の面である場合、同じ面として回路分けを行う",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX接続(過積)で回路を分ける。",
"modal.circuit.trestle.setting.circuit.allocation": "回路割り当て",
"modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て",
@@ -153,7 +179,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.info": "同じ回路のモジュールを選択状態にした後、[番号確定]ボタンを押すと番号が割り当てられます。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "選択したパワーコンディショナー",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "設定する回路番号(1~)",
- "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "標準回路{0}章~{1}章",
+ "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "標準回路{0}~{1}直",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "選択したパワーコンディショナーの回路番号の初期化",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "選択したパワーコンディショナーの回路割り当てを初期化します。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化",
@@ -162,7 +188,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error02": "シリーズを選択してください。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定",
"modal.circuit.trestle.setting.step.up.allocation": "昇圧設定",
- "modal.circuit.trestle.setting.step.up.allocation.serial.amount": "シリアル枚数",
+ "modal.circuit.trestle.setting.step.up.allocation.serial.amount": "直列枚数",
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "総回路数",
"modal.circuit.trestle.setting.step.up.allocation.connected": "接続する",
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "昇圧回路数",
@@ -178,7 +204,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": "見積書のコピー",
@@ -299,7 +325,7 @@
"modal.object.setting.offset.width": "幅の出幅",
"modal.object.setting.offset.slope": "勾配",
"modal.object.setting.direction.select": "方向の選択",
- "modal.placement.surface.setting.info": "ⓘ①の長さ入力後、対角線長を入力すると②の長さを自動計算します。",
+ "modal.placement.surface.setting.info": "①の長さを入力後、③の長さを入力すると②の長さを自動計算します。",
"modal.placement.surface.setting.diagonal.length": "斜めの長さ",
"modal.color.picker.title": "色設定",
"modal.color.picker.default.color": "基本色",
@@ -549,7 +575,7 @@
"color.pink": "ピンク",
"color.gold": "黄金色",
"color.darkblue": "藍色",
- "site.name": "HANASYS設計",
+ "site.name": "HANASYS DESIGN",
"site.sub_name": "太陽光発電システム図面管理サイト",
"site.header.link1": "選択してください。",
"site.header.link2": "オンライン保証システム",
@@ -594,6 +620,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 +868,7 @@
"has.not.sleeve": "袖なし",
"jerkinhead.width": "半切妻幅",
"jerkinhead.slope": "半切妻傾斜",
- "shed.width": "片流幅",
+ "shed.width": "片流れの出幅",
"windage": "片流れ",
"windage.width": "片流れの出幅",
"write": "作成",
@@ -895,6 +922,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": "後日資料提出",
@@ -918,6 +946,7 @@
"estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください。",
+ "estimate.detail.showPrice.pricingBtn.confirm": "価格登録初期化されますがよろしいですか?",
"estimate.detail.showPrice.description1": "製品価格OPEN",
"estimate.detail.showPrice.description2": "追加の変更品目",
"estimate.detail.showPrice.description3": "添付必須",
@@ -957,6 +986,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 +1027,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 +1064,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": "屋根材別モジュールの単体の単体での最大段数、2種混合の段数を確認して下さい",
+ "modal.module.basic.setting.module.placement.max.row": "単体で\rの最大段数",
+ "modal.module.basic.setting.module.placement.max.rows.multiple": "2種混合時\rの最大段数",
+ "modal.module.basic.setting.module.placement.mix.asg.yn.error": "混合インストール不可能なモジュールです。 (JA)",
+ "modal.module.basic.setting.module.placement.mix.asg.yn": "混合",
+ "modal.module.basic.setting.module.placement.over.max.row": "{0} 最大段数超過しました。最大段数表を参考にしてください。"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 5cfbc50b..daf9ad60 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,20 @@
"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.margin": "모듈 배치 간격",
+ "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.placement.margin.horizontal": "좌우",
+ "modal.module.basic.setting.module.placement.margin.vertical": "상하",
"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 +117,33 @@
"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.settting.module.error12": "시공법을 선택해주세요.\n(지붕재: {0})",
"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,8 +154,10 @@
"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": "설정값으로 자동 배치",
+ "modal.module.basic.setting.auto.placement": "자동 배치",
+ "modal.module.basic.setting.auto.row.batch": "자동 단·열수 지정 배치 ",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting.alloc.trestle": "가대할당",
@@ -142,7 +169,7 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "표준매수",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "최대매수",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류",
- "modal.circuit.trestle.setting.power.conditional.select.check1": "동일경사 동일방면의 면적인 경우 같은 면으로서 회로를 나눈다.",
+ "modal.circuit.trestle.setting.power.conditional.select.check1": "동일 구배·동일 방면의 면인 경우, 같은 면으로서 회로를 나눈다",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX 접속(과적)으로 회로를 나눈다.",
"modal.circuit.trestle.setting.circuit.allocation": "회로 할당",
"modal.circuit.trestle.setting.circuit.allocation.auto": "자동회로 할당",
@@ -153,7 +180,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.info": "동일한 회로의 모듈을 선택 상태로 만든 후 [번호 확정] 버튼을 누르면 번호가 할당됩니다.",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "선택된 파워컨디셔너",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "설정할 회로번호(1~)",
- "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "표준회로{0}장~{1}장",
+ "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "표준회로{0}~{1}직",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "선택된 파워컨디셔너의 회로번호 초기화",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "선택된 파워 컨디셔너의 회로할당을 초기화합니다.",
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화",
@@ -178,7 +205,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 +621,7 @@
"myinfo.message.password.error": "비밀번호가 틀렸습니다.",
"login": "로그인",
"login.auto.page.text": "자동로그인 중 입니다.",
+ "login.fail": "계정이 없거나 비밀번호가 잘못되었습니다.",
"login.id.save": "ID Save",
"login.id.placeholder": "아이디를 입력해주세요.",
"login.password.placeholder": "비밀번호를 입력해주세요.",
@@ -895,6 +923,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": "후일자료제출",
@@ -918,6 +947,7 @@
"estimate.detail.header.unitPrice": "정가",
"estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. 제품 선택 후 Pricing을 진행해주세요.",
+ "estimate.detail.showPrice.pricingBtn.confirm": "가격등록을 초기화 하시겠습니까?",
"estimate.detail.showPrice.description1": "제품 가격 OPEN",
"estimate.detail.showPrice.description2": "추가 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수",
@@ -957,6 +987,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 +1065,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": "지붕재별 모듈 단체의 최대 단수, 2종 혼합 단수를 확인하십시오.",
+ "modal.module.basic.setting.module.placement.max.row": "최대 단수",
+ "modal.module.basic.setting.module.placement.max.rows.multiple": "2종 혼합 최대단수",
+ "modal.module.basic.setting.module.placement.mix.asg.yn.error": "혼합 설치 불가능한 모듈입니다.",
+ "modal.module.basic.setting.module.placement.mix.asg.yn": "혼합",
+ "modal.module.basic.setting.module.placement.over.max.row": "{0}의 최대단수를 초과했습니다. 최대단수표를 참고해 주세요."
}
diff --git a/src/store/auxiliaryLineAtom.js b/src/store/auxiliaryLineAtom.js
new file mode 100644
index 00000000..acecf0b3
--- /dev/null
+++ b/src/store/auxiliaryLineAtom.js
@@ -0,0 +1,71 @@
+import { atom } from 'recoil'
+
+// 보조선 타입
+export const AUXILIARY_LINE_TYPE = {
+ OUTER_LINE: 'outerLine', // 외벽선
+ RIGHT_ANGLE: 'rightAngle', // 직각
+ DOUBLE_PITCH: 'doublePitch',
+ ANGLE: 'angle', // 각도
+ DIAGONAL_LINE: 'diagonalLine', // 대각선
+}
+
+/**
+ * 보조선 작성에서 사용하는 recoilState
+ */
+
+export const auxiliaryLineLength1State = atom({
+ //길이1
+ key: 'auxiliaryLineLength1State',
+ default: 0,
+})
+
+export const auxiliaryLineLength2State = atom({
+ // 길이2
+ key: 'auxiliaryLineLength2State',
+ default: 0,
+})
+
+export const auxiliaryLineArrow1State = atom({
+ // 방향1
+ key: 'auxiliaryLineArrow1State',
+ default: '',
+})
+
+export const auxiliaryLineArrow2State = atom({
+ // 방향2
+ key: 'auxiliaryLineArrow2State',
+ default: '',
+})
+
+export const auxiliaryLineAngle1State = atom({
+ // 각도1
+ key: 'auxiliaryLineAngle1State',
+ default: 0,
+})
+
+export const auxiliaryLineAngle2State = atom({
+ // 각도2
+ key: 'auxiliaryLineAngle2State',
+ default: 0,
+})
+
+export const auxiliaryLineDiagonalState = atom({
+ // 대각선
+ key: 'auxiliaryLineDiagonalState',
+ default: 0,
+})
+
+export const auxiliaryLineTypeState = atom({
+ key: 'auxiliaryLineTypeState',
+ default: AUXILIARY_LINE_TYPE.OUTER_LINE,
+})
+
+export const auxiliaryLinePointsState = atom({
+ key: 'auxiliaryLinePointsState',
+ default: [],
+})
+
+export const auxiliaryLineFixState = atom({
+ key: 'auxiliaryLineFixState',
+ default: false,
+})
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..58f9a3ca 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;
@@ -798,28 +806,28 @@
color: #45576F;
margin-left: 5px;
}
-.drag-file-box{
- padding: 10px;
- .btn-area{
- padding-bottom: 15px;
- border-bottom: 1px solid #ECF0F4;
- .file-upload{
- display: inline-block;
- height: 30px;
- background-color: #94A0AD;
- padding: 0 10px;
- border-radius: 2px;
- font-size: 13px;
- line-height: 30px;
- color: #fff;
- font-weight: 500;
- cursor: pointer;
- transition: background .15s ease-in-out;
- &:hover{
- background-color: #607F9A;
- }
+.btn-area{
+ padding-bottom: 15px;
+ border-bottom: 1px solid #ECF0F4;
+ .file-upload{
+ display: inline-block;
+ height: 30px;
+ background-color: #94A0AD;
+ padding: 0 10px;
+ border-radius: 2px;
+ font-size: 13px;
+ line-height: 30px;
+ color: #fff;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background .15s ease-in-out;
+ &:hover{
+ background-color: #607F9A;
}
}
+}
+.drag-file-box{
+ padding: 10px;
.drag-file-area{
position: relative;
margin-top: 15px;
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..7b94f2b4 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,143 @@ $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;
+ }
+}
+
+
+.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;
+ }
+}
+
+// 2025-05-07 지붕모듈
+.module-input-area{
+ display: flex;
+ align-items: center;
+ margin-top: 10px;
+ .module-area-title{
+ flex: none;
+ font-size: 12px;
+ color: #fff;
+ font-weight: 500;
+ margin-right: 20px;
+ }
+ .module-input-wrap{
+ width: 100%;
+ display: flex;
+ .outline-form{
+ width: calc(33.333% - 10px);
+ margin-right: 15px;
+ &:last-child{
+ margin-right: 0;
+ }
+ .input-grid{
+ width: 100%;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss
index 34dcaf33..ecb7f2cb 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{
@@ -375,13 +377,14 @@ button{
font-size: 12px;
color: #fff;
line-height: 1.4;
+ text-align: left;
}
&:hover{
background-color: #2C2C2C;
}
}
&::-webkit-scrollbar {
- width: 2px;
+ width: 5px;
background-color: transparent;
}
@@ -1021,4 +1024,23 @@ input:checked + .slider {
// alert z-index
.swal2-container{
z-index: 120000;
+}
+
+// textarea
+.textarea-form{
+ width: 100%;
+ outline: none;
+ resize: none;
+ border: none;
+ border: 1px solid #eee;
+ min-height: 150px;
+ padding: 10px;
+ font-size: 13px;
+ color: #45576F;
+ border-radius: 2px;
+ font-family: "Noto Sans JP", sans-serif;
+ transition: border .15s ease-in-out;
+ &:focus{
+ border-color: #94a0ad;
+ }
}
\ No newline at end of file
diff --git a/src/styles/_submodal.scss b/src/styles/_submodal.scss
index de53d584..40beae5e 100644
--- a/src/styles/_submodal.scss
+++ b/src/styles/_submodal.scss
@@ -288,55 +288,56 @@
}
// 커뮤니티
-.community_detail{
- .community_detail-tit{
- font-size: 16px;
- color: #101010;
- font-weight: 600;
- padding-bottom: 14px;
- border-bottom: 2px solid #101010;
- }
- .community_detail-file-wrap{
- padding: 24px 0;
- border-bottom: 1px solid #E5E5E5;
- dt{
- font-size: 13px;
- color: #101010;
- font-weight: 500;
- margin-bottom: 15px;
- }
- dd{
- font-size: 12px;
- font-weight: 400;
- margin-bottom: 3px;
- color: #344356;
- &:last-child{
- margin-bottom: 0;
- }
- }
- }
- .community_detail-inner{
- max-height: 300px;
- overflow-y: auto;
- margin-top: 20px;
- margin-bottom: 20px;
+.community_detail-tit{
+ font-size: 16px;
+ color: #101010;
+ font-weight: 600;
+ padding-bottom: 14px;
+ border-bottom: 2px solid #101010;
+}
+.community_detail-file-wrap{
+ padding: 24px 0;
+ border-bottom: 1px solid #E5E5E5;
+ dt{
font-size: 13px;
+ color: #101010;
+ font-weight: 500;
+ }
+ dd{
+ font-size: 12px;
font-weight: 400;
- color: #45576F;
- line-height: 26px;
- word-break: keep-all;
- &::-webkit-scrollbar {
- width: 4px;
- background-color: transparent;
+ margin-bottom: 3px;
+ color: #344356;
+ &:nth-child(2){
+ margin-top: 15px;
}
- &::-webkit-scrollbar-thumb {
- background-color: #C1CCD7;
- }
- &::-webkit-scrollbar-track {
- background-color: transparent;
+ &:last-child{
+ margin-bottom: 0;
}
}
}
+.community_detail-inner{
+ max-height: 300px;
+ overflow-y: auto;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ font-size: 13px;
+ font-weight: 400;
+ color: #45576F;
+ line-height: 26px;
+ word-break: keep-all;
+ &::-webkit-scrollbar {
+ width: 4px;
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-thumb {
+ background-color: #C1CCD7;
+ }
+ &::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+}
+
// 견적 복사
.estimate-copy-info-item{
@@ -368,4 +369,117 @@
color: #999;
}
}
+}
+
+// 1:1문의
+.one-on-one{
+ .select-wrap{
+ width: 250px;
+ }
+ .input-wrap{
+ flex: 1 1 auto;
+ }
+ &.btn-area{
+ padding-bottom: 0;
+ border: none;
+ margin-left: 10px;
+ }
+ &.drag-file-box{
+ border: 1px solid #eee;
+ .drag-file-area{
+ margin-top: 0;
+ }
+ }
+}
+
+.oneonone-header-wrap{
+ padding-bottom: 14px;
+ border-bottom: 2px solid #101010;
+ .oneonone-title{
+ font-size: 16px;
+ color: #101010;
+ font-weight: 600;
+ margin-bottom: 14px;
+ }
+}
+.oneonone-infor{
+ display: flex;
+ align-items: center;
+ .profile{
+ position: relative;
+ padding-left: 26px;
+ padding-right: 8px;
+ font-size: 13px;
+ font-weight: 400;
+ color: #101010;
+ &::before{
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 0;
+ transform: translateY(-50%);
+ width: 24px;
+ height: 24px;
+ background: url(./../../public/static/images/sub/oneonone_profile_icon.svg)no-repeat center;
+ }
+ &::after{
+ content: '';
+ position: absolute;
+ top: 50%;
+ right: 0;
+ transform: translateY(-50%);
+ width: 1px;
+ height: 8px;
+ background-color: #CCCCCC;
+ }
+ }
+ .date{
+ padding-left: 8px;
+ font-size: 13px;
+ font-weight: 400;
+ color: #101010;
+ }
+}
+.oneonone-detail{
+ padding: 20px;
+ border: 1px solid #101010;
+ border-top: none;
+ .community_detail-file-wrap{
+ padding-top: 0;
+ }
+ .community_detail-inner{
+ max-height: 110px;
+ margin-top: 24px;
+ margin-bottom: 0;
+ }
+}
+
+.oneonone-answer{
+ margin-top: 8px;
+ padding: 20px;
+ border: 1px solid #101010;
+ .community_detail-inner{
+ max-height: 110px;
+
+ }
+ .community_detail-file-wrap{
+ border-top: 1px solid #D4DCE7;
+ padding: 16px 0 0 0;
+ border-bottom: none;
+ }
+}
+
+.answer-title-wrap{
+ display: flex;
+ align-items: center;
+ padding-bottom: 14px;
+ border-bottom: 1px solid #D4DCE7;
+ .answer-title{
+ font-size: 14px;
+ color: #101010;
+ font-weight: 600;
+ }
+ .oneonone-infor{
+ margin-left: auto;
+ }
}
\ No newline at end of file
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 3e3f4059..1d067ad8 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 (
diff --git a/src/util/common-utils.js b/src/util/common-utils.js
index 842eace0..0a1265fe 100644
--- a/src/util/common-utils.js
+++ b/src/util/common-utils.js
@@ -145,9 +145,19 @@ export const unescapeString = (str) => {
''': "'",
}
+ /*
+ 1. 한번 변환은 {" 로 변환됨 : 에러 발생 => while 변경
+ 2. 변환할 내용이 없으면 리턴값이 undefined
+
if (regex.test(str)) {
return str.replace(regex, (matched) => chars[matched] || matched)
}
+ */
+
+ while (regex.test(str)) {
+ str = str.replace(regex, (matched) => chars[matched] || matched);
+ }
+ return str
}
export const isNullOrUndefined = (value) => {
diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js
index a3f78cb6..c6bf5701 100644
--- a/src/util/qpolygon-utils.js
+++ b/src/util/qpolygon-utils.js
@@ -7649,7 +7649,8 @@ export const calcLinePlaneSize = (points) => {
* @param degree
* @returns number
*/
-export const calcLineActualSize = (points, degree) => {
+export const calcLineActualSize = (points, degree = 0) => {
+ const { x1, y1, x2, y2 } = points
const planeSize = calcLinePlaneSize(points)
const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180)))
return Big(planeSize).div(theta).round().toNumber()
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 })