Merge pull request 'dev' (#190) from dev into prd-deploy

Reviewed-on: #190
This commit is contained in:
ysCha 2025-07-08 15:25:04 +09:00
commit d72444199f
5 changed files with 147 additions and 64 deletions

View File

@ -4,10 +4,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage'
import { useEffect, useState } from 'react'
import { polygonToTurfPolygon } from '@/util/canvas-util'
import { deepCopyArray } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom'
import * as turf from '@turf/turf'
import { POLYGON_TYPE } from '@/common/common'
import { useModule } from '@/hooks/module/useModule'
import { useSwal } from '@/hooks/useSwal'
@ -41,7 +38,15 @@ export default function PanelEdit(props) {
//
return () => {
const modules = canvas.getObjects().filter((obj) => obj.name === 'module')
modules.forEach((obj) => {
obj.set({
stroke: 'black',
strokeWidth: 0.3,
})
})
canvas?.discardActiveObject() //
canvas.renderAll()
}
}, [])

View File

@ -2,20 +2,20 @@ import * as turf from '@turf/turf'
export const useTurf = () => {
/**
* 배치면 안에 있는지 확인
* @param {*} squarePolygon
* @param {*} turfModuleSetupSurface
* 모듈이 배치면 안에 있는지 확인
* @param {*} module
* @param {*} surface
* @param spare
* @returns
*/
const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface, spare = 1) => {
const checkModuleDisjointSurface = (module, surface, spare = 1) => {
// 표면 영역을 spare만큼 수동 확장
const expandedSurface = {
type: 'Polygon',
coordinates: [
turfModuleSetupSurface.geometry.coordinates[0].map(([x, y]) => {
surface.geometry.coordinates[0].map(([x, y]) => {
// 각 점을 바깥쪽으로 2 단위씩 이동
const coords = turfModuleSetupSurface.geometry.coordinates[0]
const coords = surface.geometry.coordinates[0]
const centerX = coords.slice(0, -1).reduce((sum, [x, y]) => sum + x, 0) / (coords.length - 1)
const centerY = coords.slice(0, -1).reduce((sum, [x, y]) => sum + y, 0) / (coords.length - 1)
@ -24,9 +24,9 @@ export const useTurf = () => {
],
}
const isWithin = turf.booleanContains(expandedSurface, squarePolygon) || turf.booleanWithin(squarePolygon, expandedSurface)
const isWithin = turf.booleanContains(expandedSurface, module) || turf.booleanWithin(module, expandedSurface)
const isContact = turf.booleanIntersects(squarePolygon, expandedSurface) && !turf.booleanOverlap(squarePolygon, expandedSurface)
const isContact = turf.booleanIntersects(module, expandedSurface) && !turf.booleanOverlap(module, expandedSurface)
return isWithin || isContact
}

View File

@ -1,16 +1,16 @@
import { BATCH_TYPE, POLYGON_TYPE, TRESTLE_MATERIAL } from '@/common/common'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { canvasState } from '@/store/canvasAtom'
import { isOverlap, polygonToTurfPolygon, rectToPolygon } from '@/util/canvas-util'
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { polygonToTurfPolygon } from '@/util/canvas-util'
import { useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import * as turf from '@turf/turf'
import { useSwal } from '../useSwal'
import { useModuleBasicSetting } from './useModuleBasicSetting'
import { useMessage } from '../useMessage'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { moduleStatisticsState } from '@/store/circuitTrestleAtom'
import { useCircuitTrestle } from '../useCirCuitTrestle'
import { useTrestle } from './useTrestle'
import { useTurf } from '@/hooks/common/useTurf'
export const MODULE_REMOVE_TYPE = {
LEFT: 'left',
@ -42,6 +42,7 @@ export function useModule() {
const selectedModules = useRecoilValue(selectedModuleState)
const { setModuleStatisticsData, resetCircuits } = useCircuitTrestle()
const { clear: removeTrestleMaterials } = useTrestle()
const { checkModuleDisjointSurface } = useTurf()
const moduleMove = (length, direction) => {
const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴
@ -70,13 +71,16 @@ export function useModule() {
top: module.top,
fill: module.fill,
}
module.set({ top, left })
module.set({ top, left, strokeWidth: 0.3 })
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
module.set({ strokeWidth: 3 })
canvas.renderAll()
})
@ -87,7 +91,11 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
selectedModules.forEach((module) => {
module.set({ left: module.originCoords.left, top: module.originCoords.top, fill: module.originCoords.fill })
module.set({
left: module.originCoords.left,
top: module.originCoords.top,
fill: module.originCoords.fill,
})
module.setCoords()
})
canvas.renderAll()
@ -126,6 +134,7 @@ export function useModule() {
const objects = getObjects()
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
let isWarning = false
activeModule.set({ strokeWidth: 0.3 })
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false)
@ -145,6 +154,8 @@ export function useModule() {
}
})
activeModule.set({ strokeWidth: 3 })
canvas.renderAll()
if (isWarning) {
swalFire({
@ -350,6 +361,7 @@ export function useModule() {
return
}
const activeModule = canvas.getObjects().find((obj) => canvas.getActiveObjects()[0].id === obj.id)
activeModule.set({ strokeWidth: 1 })
if (activeModule.circuit) {
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
if (surfaces.some((surface) => surface.isComplete)) {
@ -383,6 +395,7 @@ export function useModule() {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
stroke: 'black',
direction: module.direction,
arrow: module.arrow,
name: module.name,
@ -411,6 +424,7 @@ export function useModule() {
}
canvas.renderAll()
})
activeModule.set({ strokeWidth: 3 })
if (isWarning) {
swalFire({
@ -982,10 +996,7 @@ export function useModule() {
}
const isOutsideSurface = (module, moduleSetupSurface) => {
return (
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true))
)
return !checkModuleDisjointSurface(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true))
}
const getRowModules = (target) => {
@ -1030,9 +1041,7 @@ export function useModule() {
}
const getObjects = () => {
return canvas
?.getObjects()
.filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER, BATCH_TYPE.SHADOW].includes(obj.name))
return canvas?.getObjects().filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER].includes(obj.name))
}
const resetSurface = () => {

View File

@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import {
@ -8,32 +8,29 @@ import {
currentObjectState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
moduleSetupOptionState,
toggleManualSetupModeState,
moduleRowColArrayState,
moduleSetupOptionState,
moduleSetupSurfaceState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { rectToPolygon, polygonToTurfPolygon, calculateVisibleModuleHeight, getDegreeByChon, toFixedWithoutRounding } from '@/util/canvas-util'
import { calculateVisibleModuleHeight, getDegreeByChon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding } from '@/util/canvas-util'
import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { moduleSetupSurfaceState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import { BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common'
import * as turf from '@turf/turf'
import { useSwal } from '@/hooks/useSwal'
import { compasDegAtom } from '@/store/orientationAtom'
import { QLine } from '@/components/fabric/QLine'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
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'
import { useTurf } from '@/hooks/common/useTurf'
export function useModuleBasicSetting(tabNum) {
@ -91,7 +88,10 @@ export function useModuleBasicSetting(tabNum) {
if (roofConstructions && roofConstructions.length > 0) {
//roofIndex 넣기
const roofConstructionArray = roofConstructions.map((detail) => ({ ...detail.trestleDetail, roofIndex: detail.roofIndex }))
const roofConstructionArray = roofConstructions.map((detail) => ({
...detail.trestleDetail,
roofIndex: detail.roofIndex,
}))
//북면 설치 가능 판매점
if (moduleSelectionData.common.saleStoreNorthFlg == '1') {
@ -137,7 +137,10 @@ export function useModuleBasicSetting(tabNum) {
const offsetObjects = moduleSelectionData.roofConstructions.find((item) => item.addRoof.index === roofIndex)
roof.lines.forEach((line) => {
line.attributes = { ...line.attributes, offset: getOffset(offsetObjects.addRoof, line, roof.pitch, roof.from) }
line.attributes = {
...line.attributes,
offset: getOffset(offsetObjects.addRoof, line, roof.pitch, roof.from),
}
})
//배치면 설치 영역
makeModuleInstArea(roof, detail)
@ -150,7 +153,11 @@ export function useModuleBasicSetting(tabNum) {
const moduleRowArray = []
if (isObjectNotEmpty(detail) && detail.module.length > 0) {
detail.module.forEach((module) => {
moduleRowArray.push({ moduleMaxRows: module.moduleMaxRows, mixModuleMaxRows: module.mixModuleMaxRows, maxRow: detail.moduleMaxRows })
moduleRowArray.push({
moduleMaxRows: module.moduleMaxRows,
mixModuleMaxRows: module.mixModuleMaxRows,
maxRow: detail.moduleMaxRows,
})
})
}
rowColArray.push(moduleRowArray)
@ -831,7 +838,10 @@ export function useModuleBasicSetting(tabNum) {
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' })
swalFire({
text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error'),
icon: 'warning',
})
return
}
}
@ -901,10 +911,10 @@ export function useModuleBasicSetting(tabNum) {
setModuleStatisticsData()
//그림자는 무조건 가장 앞으로
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) {
shadowObj.bringToFront()
}
// const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
// if (shadowObj) {
// shadowObj.bringToFront()
// }
// getModuleStatistics()
} else {
@ -1312,7 +1322,10 @@ export function useModuleBasicSetting(tabNum) {
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' })
swalFire({
text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error'),
icon: 'warning',
})
return
}
}
@ -1764,10 +1777,10 @@ export function useModuleBasicSetting(tabNum) {
}
})
//그림자가 있다면 무조건 그림자를 가장 앞으로 올림
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) {
shadowObj.bringToFront()
}
// const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
// if (shadowObj) {
// shadowObj.bringToFront()
// }
}
})
}
@ -2175,7 +2188,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
moduleOptions = {
...moduleOptions,
fill: module.color,
surfaceId: moduleSetupSurface.id,
moduleInfo: module,
}
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2353,7 +2371,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
moduleOptions = {
...moduleOptions,
fill: module.color,
surfaceId: moduleSetupSurface.id,
moduleInfo: module,
}
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2533,7 +2556,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
moduleOptions = {
...moduleOptions,
fill: module.color,
surfaceId: moduleSetupSurface.id,
moduleInfo: module,
}
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let turfSurface = polygonToTurfPolygon(moduleSetupSurface, true)
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, turfSurface)
@ -2717,7 +2745,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
moduleOptions = {
...moduleOptions,
fill: module.color,
surfaceId: moduleSetupSurface.id,
moduleInfo: module,
}
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2829,10 +2862,10 @@ export function useModuleBasicSetting(tabNum) {
// setModuleIsSetup(moduleArray)
//그림자는 무조건 가장 앞으로
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) {
shadowObj.bringToFront()
}
// const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
// if (shadowObj) {
// shadowObj.bringToFront()
// }
})
// calculateForApi()
@ -2841,7 +2874,10 @@ export function useModuleBasicSetting(tabNum) {
*/
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' })
swalFire({
text: getMessage('modal.module.basic.setting.module.placement.over.max.row', [roofNames]),
icon: 'warning',
})
}
}
@ -2937,7 +2973,11 @@ export function useModuleBasicSetting(tabNum) {
const pointY2 = top
//디버깅
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { stroke: 'red', strokeWidth: 1, selectable: true })
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
})
// canvas?.add(finalLine)
// canvas?.renderAll()
@ -2971,7 +3011,14 @@ export function useModuleBasicSetting(tabNum) {
type: 'flat',
}
} else {
rtnObj = { target: index === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' }
rtnObj = {
target: index === 0 ? 'bottom' : 'top',
x1: pointX1,
y1: pointY1,
x2: pointX2,
y2: pointY2,
type: 'curve',
}
}
rtnObjArray.push(rtnObj)
@ -3058,7 +3105,11 @@ export function useModuleBasicSetting(tabNum) {
const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y)
//디버깅용
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { stroke: 'red', strokeWidth: 1, selectable: true })
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
})
// canvas?.add(finalLine)
// canvas?.renderAll()
@ -3092,7 +3143,14 @@ export function useModuleBasicSetting(tabNum) {
type: 'flat',
}
} else {
rtnObj = { target: index === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2, type: 'curve' }
rtnObj = {
target: index === 0 ? 'left' : 'right',
x1: pointX1,
y1: pointY1,
x2: pointX2,
y2: pointY2,
type: 'curve',
}
}
rtnObjArray.push(rtnObj)
})
@ -3469,7 +3527,10 @@ export function useModuleBasicSetting(tabNum) {
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' })
swalFire({
text: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn.error'),
icon: 'warning',
})
return
}
}
@ -3479,7 +3540,10 @@ export function useModuleBasicSetting(tabNum) {
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 },
]
@ -3799,7 +3863,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
moduleOptions = { ...moduleOptions, fill: module.color, surfaceId: moduleSetupSurface.id, moduleInfo: module }
moduleOptions = {
...moduleOptions,
fill: module.color,
surfaceId: moduleSetupSurface.id,
moduleInfo: module,
}
let tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))

View File

@ -344,10 +344,10 @@
"modal.flow.direction.setting": "流れ方向の設定",
"modal.flow.direction.setting.info": "流れ方向を選択してください。",
"modal.actual.size.setting": "実測値設定",
"modal.actual.size.setting.info": "※実際寸法入力してください。",
"modal.actual.size.setting.info": "※作成した補助線を選択し、寸法を入力してください。",
"modal.actual.size.setting.not.exist.auxiliary.line": "実測値入力する補助線を選択してください",
"modal.actual.size.setting.not.exist.size": "実際の寸法の長さを入力してください",
"modal.actual.size.setting.plane.size.length": "廊下寸法の長さ",
"modal.actual.size.setting.plane.size.length": "伏図寸法の長さ",
"modal.actual.size.setting.actual.size.length": "実寸長",
"plan.message.confirm.save": "プラン保存しますか?",
"plan.message.confirm.copy": "プランコピーしますか?",