dev #534

Merged
ysCha merged 4 commits from dev into prd-deploy 2025-12-29 22:51:46 +09:00
7 changed files with 355 additions and 53 deletions

View File

@ -845,37 +845,33 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = point
// viewport transform만 역변환 (캔버스 줌/팬 보정)
// 결과는 WORLD 좌표 (캔버스 좌표계)
let canvasPoint = point
if (this.canvas) {
const vpt = this.canvas.viewportTransform
if (vpt) {
// viewport transform 역변환
const inverted = fabric.util.invertTransform(vpt)
localPoint = fabric.util.transformPoint(point, inverted)
canvasPoint = fabric.util.transformPoint(point, inverted)
}
}
// 오브젝트의 transform matrix를 고려한 좌표 변환
const matrix = this.calcTransformMatrix()
const invertedMatrix = fabric.util.invertTransform(matrix)
const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix)
// pathOffset을 고려한 최종 좌표 계산
const pathOffset = this.get('pathOffset')
const finalPoint = {
x: Number((transformedPoint.x + pathOffset.x).toFixed(this.toFixed)),
y: Number((transformedPoint.y + pathOffset.y).toFixed(this.toFixed)),
// canvasPoint는 WORLD 좌표
// inPolygonImproved에서 getCurrentPoints()도 WORLD 좌표를 반환
// 따라서 좌표 시스템이 일치함
const checkPoint = {
x: Number(canvasPoint.x.toFixed(this.toFixed)),
y: Number(canvasPoint.y.toFixed(this.toFixed)),
}
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygonImproved(finalPoint)
const isInside = this.inPolygonImproved(checkPoint)
if (!this.selectable) {
this.set('selectable', isInside)
}
return isInside
} else {
return this.inPolygonImproved(finalPoint)
return this.inPolygonImproved(checkPoint)
}
},

View File

@ -5,12 +5,13 @@ import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { POLYGON_TYPE } from '@/common/common'
import { POLYGON_TYPE, MENU } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom'
import { useTrestle } from '@/hooks/module/useTrestle'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
@ -29,11 +30,12 @@ const ALLOCATION_TYPE = {
}
export default function CircuitTrestleSetting({ id }) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const { closePopup, addPopup } = usePopup()
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
const { swalFire } = useSwal()
const { saveEstimate } = useEstimate()
const canvas = useRecoilValue(canvasState)
const setCurrentMenu = useSetRecoilState(currentMenuState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [tabNum, setTabNum] = useState(1)
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
@ -106,6 +108,167 @@ export default function CircuitTrestleSetting({ id }) {
}
}, [])
// rack reopen
useEffect(() => {
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
if (modules.length === 0) {
return
}
/**
* 설치 모듈 크기 검증
* - 남쪽: 아래쪽 모듈이 위쪽 모듈보다 커야
* - 북쪽: 위쪽 모듈이 아래쪽 모듈보다 커야
* - 동쪽: 오른쪽 모듈이 왼쪽 모듈보다 커야
* - 서쪽: 왼쪽 모듈이 오른쪽 모듈보다 커야
*/
const validateModuleSizeForRack = (surface) => {
const { modules, direction, trestleDetail } = surface
const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail
if (rackYn === 'N' || !modules || modules.length < 2) {
return true //
}
//
const centerPoints = modules.map((module) => {
const { x, y } = module.getCenterPoint()
const { width, height } = module
return {
x,
y,
width: Math.floor(width),
height: Math.floor(height),
area: Math.floor(width) * Math.floor(height),
}
})
//
const isVertical = direction === 'south' || direction === 'north'
const primaryInterval = isVertical ? moduleIntvlVer : moduleIntvlHor
const secondaryInterval = isVertical ? moduleIntvlHor : moduleIntvlVer
// :
const getSortFn = () => {
switch (direction) {
case 'south': return (a, b) => b.y - a.y // (y )
case 'north': return (a, b) => a.y - b.y // (y )
case 'east': return (a, b) => b.x - a.x // (x )
case 'west': return (a, b) => a.x - b.x // (x )
default: return () => 0
}
}
// ( )
const findTargetModules = (current, margin) => {
return centerPoints.filter(cp => {
const sameAxis = isVertical
? Math.abs(cp.x - current.x) < margin
: Math.abs(cp.y - current.y) < margin
const targetDirection = direction === 'south' ? cp.y < current.y
: direction === 'north' ? cp.y > current.y
: direction === 'east' ? cp.x < current.x
: cp.x > current.x
return sameAxis && targetDirection
})
}
//
const getClosestTarget = (filtered) => {
if (filtered.length === 0) return null
return filtered.reduce((closest, cp) => {
switch (direction) {
case 'south': return cp.y > closest.y ? cp : closest
case 'north': return cp.y < closest.y ? cp : closest
case 'east': return cp.x > closest.x ? cp : closest
case 'west': return cp.x < closest.x ? cp : closest
default: return closest
}
})
}
//
const getGap = (current, target) => {
if (isVertical) {
return direction === 'south'
? (current.y - current.height / 2) - (target.y + target.height / 2)
: (target.y - target.height / 2) - (current.y + current.height / 2)
} else {
return direction === 'east'
? (current.x - current.width / 2) - (target.x + target.width / 2)
: (target.x - target.width / 2) - (current.x + current.width / 2)
}
}
//
const isAdjacent = (current, target) => {
const gap = getGap(current, target)
return gap >= 0 && gap <= primaryInterval + 1
}
//
const sortedPoints = [...centerPoints].sort(getSortFn())
for (const current of sortedPoints) {
// 1. :
const directTargets = findTargetModules(current, secondaryInterval)
const closestTarget = getClosestTarget(directTargets)
if (closestTarget && isAdjacent(current, closestTarget) && closestTarget.area > current.area) {
return false //
}
// 2. :
const size = isVertical ? current.width : current.height
const halfOffset = (size + secondaryInterval) / 2
for (const sign of [-1, 1]) {
const offsetValue = sign * halfOffset
const findHalfTarget = centerPoints.filter((cp) => {
const offsetAxis = isVertical
? Math.abs(cp.x - (current.x + offsetValue)) <= primaryInterval
: Math.abs(cp.y - (current.y + offsetValue)) <= primaryInterval
const targetDirection = direction === 'south' ? cp.y < current.y
: direction === 'north' ? cp.y > current.y
: direction === 'east' ? cp.x < current.x
: cp.x > current.x
return offsetAxis && targetDirection
})
const closestHalf = getClosestTarget(findHalfTarget)
if (closestHalf && isAdjacent(current, closestHalf) && closestHalf.area > current.area) {
return false //
}
}
}
return true //
}
//
const moduleSetupSurfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
for (const surface of moduleSetupSurfaces) {
if (!validateModuleSizeForRack(surface)) {
swalFire({
text: getMessage('module.size.validation.rack.error'),
icon: 'error',
confirmFn: () => {
//
closePopup(id)
//
setCurrentMenu(MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING)
// /
const newPopupId = uuidv4()
clearTrestle()
setAllModuleSurfaceIsComplete(false)
addPopup(newPopupId, 1, <BasicSetting id={newPopupId} />)
},
})
return
}
}
}, [])
const capture = async (type) => {
beforeCapture(type)

View File

@ -103,17 +103,6 @@ export default function PassivityCircuitAllocation(props) {
surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
})
if (surfaceList.length > 1) {
if (Object.keys(surfaceType).length > 1) {
swalFire({
text: getMessage('module.circuit.fix.not.same.roof.error'),
type: 'alert',
icon: 'warning',
})
return
}
}
if (!circuitNumber || circuitNumber === 0) {
swalFire({
text: getMessage('module.circuit.minimun.error'),
@ -145,7 +134,11 @@ export default function PassivityCircuitAllocation(props) {
originSurfaceList.concat(originSurfaceList).forEach((surface) => {
surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
})
break
}
case 'OUTDMULTI': {
if (surfaceList.length > 1) {
if (Object.keys(surfaceType).length > 1) {
swalFire({
@ -156,10 +149,6 @@ export default function PassivityCircuitAllocation(props) {
return
}
}
break
}
case 'OUTDMULTI': {
if (selectedModels.length > 1) {
let result = false

View File

@ -84,6 +84,7 @@ export function useModule() {
canvas.renderAll()
})
const surfaceId = selectedModules[0].surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -96,11 +97,15 @@ export function useModule() {
top: module.originCoords.top,
fill: module.originCoords.fill,
})
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
}
}
@ -157,6 +162,7 @@ export function useModule() {
activeModule.set({ strokeWidth: 3 })
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -165,11 +171,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
}
@ -202,6 +212,7 @@ export function useModule() {
})
canvas.renderAll()
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
@ -210,11 +221,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
})
}
@ -269,6 +284,7 @@ export function useModule() {
canvas.renderAll()
})
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -276,11 +292,13 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
surface.set({ modules: [...surface.modules, ...copyModules] })
recalculateAllModulesCoords(surfaceId)
}
})
@ -333,6 +351,7 @@ export function useModule() {
}
})
const surfaceId = modules[0].surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -340,11 +359,13 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.set({ modules: [...moduleSetupSurface.modules, ...copyModules] })
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
@ -426,6 +447,7 @@ export function useModule() {
})
activeModule.set({ strokeWidth: 3 })
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
@ -433,12 +455,14 @@ export function useModule() {
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.set({ modules: [...moduleSetupSurface.modules, ...copyModules] })
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
}
@ -477,6 +501,7 @@ export function useModule() {
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, leftModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -484,7 +509,7 @@ export function useModule() {
isWarning = true
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = rightModules
} else if (type === MODULE_REMOVE_TYPE.RIGHT) {
leftModules.forEach((module) => {
@ -495,6 +520,7 @@ export function useModule() {
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, rightModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -502,7 +528,7 @@ export function useModule() {
isWarning = true
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = leftModules
} else if (type === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE) {
const sideModules = [...leftModules, ...rightModules]
@ -514,6 +540,7 @@ export function useModule() {
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width / 2 })
module.dirty = true
module.setCoords()
canvas.renderAll()
})
@ -526,6 +553,7 @@ export function useModule() {
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width / 2 })
module.dirty = true
module.setCoords()
canvas.renderAll()
})
@ -547,6 +575,7 @@ export function useModule() {
targetModules = sideModules
}
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.remove.module'),
@ -557,17 +586,24 @@ export function useModule() {
canvas.add(...columnModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
columnModules.forEach((module) => {
module.dirty = true
module.setCoords()
})
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = moduleSetupSurface.modules.filter(
(module) => !columnModules.map((copyModule) => copyModule.id).includes(module.id),
)
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const moduleRowRemove = (type) => {
@ -599,6 +635,7 @@ export function useModule() {
}
if (height === -1) height = module.top - activeModule.top
module.set({ top: module.top - height })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, topModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -606,7 +643,7 @@ export function useModule() {
module.set({ fill: 'red' })
}
})
canvas.renderAll()
canvas.requestRenderAll()
targetModules = bottomModules
} else if (type === MODULE_REMOVE_TYPE.BOTTOM) {
topModules.forEach((module) => {
@ -617,6 +654,7 @@ export function useModule() {
}
if (height === -1) height = activeModule.top - module.top
module.set({ top: module.top + activeModule.height })
module.dirty = true
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, bottomModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
@ -624,6 +662,7 @@ export function useModule() {
module.set({ fill: 'red' })
}
})
canvas.requestRenderAll()
targetModules = topModules
} else if (type === MODULE_REMOVE_TYPE.VERTICAL_SIDE) {
topModules.forEach((module) => {
@ -635,6 +674,7 @@ export function useModule() {
// if (height === -1) height = activeModule.top - module.top
if (height === -1) height = activeModule.height
module.set({ top: module.top + height / 2 })
module.dirty = true
module.setCoords()
})
@ -647,10 +687,11 @@ export function useModule() {
// if (height === -1) height = module.top - activeModule.top
if (height === -1) height = activeModule.height
module.set({ top: module.top - height / 2 })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
const sideModules = [...topModules, ...bottomModules]
sideModules.forEach((module) => {
if (
@ -668,6 +709,7 @@ export function useModule() {
targetModules = sideModules
}
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning && type !== MODULE_REMOVE_TYPE.NONE) {
targetModules.forEach((rect) => rect.set({ fill: 'red' }))
swalFire({
@ -679,13 +721,20 @@ export function useModule() {
canvas.add(...rowModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
rowModules.forEach((module) => {
module.dirty = true
module.setCoords()
})
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
}
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
const moduleColumnInsert = (type) => {
@ -756,6 +805,7 @@ export function useModule() {
module.setCoords()
})
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
@ -764,16 +814,19 @@ export function useModule() {
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = [...moduleSetupSurface.modules, ...copyModules]
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const isFixedModule = () => {
@ -863,6 +916,7 @@ export function useModule() {
})
canvas.renderAll()
const surfaceId = activeModule.surfaceId
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
@ -871,16 +925,19 @@ export function useModule() {
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
moduleSetupSurface.modules = [...moduleSetupSurface.modules, ...copyModules]
setModuleStatisticsData()
recalculateAllModulesCoords(surfaceId)
}
setModuleStatisticsData()
}
const alignModule = (type, surfaceArray) => {
@ -924,6 +981,7 @@ export function useModule() {
}
})
canvas.renderAll()
const surfaceId = surface.id
if (isWarning) {
swalFire({
title: getMessage('can.not.align.module'),
@ -932,11 +990,15 @@ export function useModule() {
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.dirty = true
module.setCoords()
})
canvas.renderAll()
canvas.requestRenderAll()
recalculateAllModulesCoords(surfaceId)
},
})
} else {
recalculateAllModulesCoords(surfaceId)
}
})
}
@ -964,6 +1026,7 @@ export function useModule() {
canvas.remove(activeModule)
canvas.renderAll()
setModuleStatisticsData()
recalculateAllModulesCoords(activeModule.surfaceId)
}
const moduleRoofRemove = (surfaceArray) => {
@ -1049,6 +1112,38 @@ export function useModule() {
removeTrestleMaterials()
}
/**
* 모든 모듈의 좌표를 재계산
* / 삭제, 복사, 추가 호출하여 선택 영역(bounding box) 업데이트
* @param {string} surfaceId - 특정 surface의 모듈만 재계산 (선택적)
*/
const recalculateAllModulesCoords = (surfaceId = null) => {
if (!canvas) return
const modules = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.filter((obj) => (surfaceId ? obj.surfaceId === surfaceId : true))
// 모듈의 캐시를 초기화하고 좌표 재계산
modules.forEach((module) => {
// Fabric.js 내부 캐시 초기화
delete module.oCoords
delete module.aCoords
delete module.lineCoords
delete module.__corner
delete module.matrixCache
delete module.ownMatrixCache
// dirty 플래그 설정 및 좌표 재계산
module.dirty = true
module.setCoords()
})
// 렌더링
canvas.renderAll()
}
return {
moduleMove,
moduleMultiMove,
@ -1063,5 +1158,6 @@ export function useModule() {
modulesRemove,
moduleRoofRemove,
alignModule,
recalculateAllModulesCoords,
}
}

View File

@ -123,6 +123,7 @@ export function useModuleBasicSetting(tabNum) {
.getObjects()
.filter((roof) => roof.name === POLYGON_TYPE.ROOF)
.forEach((roof) => {
changeLineType(roof)
if (!roof.roofMaterial) return
const roofIndex = roof.roofMaterial.index //지붕의 지붕재의 순번
@ -168,6 +169,61 @@ export function useModuleBasicSetting(tabNum) {
}
}, [trestleDetailList])
const changeLineType = (polygon) => {
if (!polygon || !polygon.lines || polygon.lines.length === 0) return
const direction = polygon.direction
// 1. 모든 라인을 케라바(GABLE)로 초기화
polygon.lines.forEach((line) => {
line.attributes = {
...line.attributes,
type: LINE_TYPE.WALLLINE.GABLE,
}
})
// 방향에 따른 좌표 설정
const directionConfig = {
south: { coord1: 'y1', coord2: 'y2', findExtreme: Math.max },
north: { coord1: 'y1', coord2: 'y2', findExtreme: Math.min },
east: { coord1: 'x1', coord2: 'x2', findExtreme: Math.max },
west: { coord1: 'x1', coord2: 'x2', findExtreme: Math.min },
}
const config = directionConfig[direction] || directionConfig.south
const { coord1, coord2, findExtreme } = config
// 2. 직선만 필터링 (대각선 제외)
// 남/북: y1 === y2 인 경우 수평 직선
// 동/서: x1 === x2 인 경우 수직 직선
const straightLines = polygon.lines.filter((line) => line[coord1] === line[coord2])
if (straightLines.length === 0) return
// 3. 가장 끝에 있는 직선 찾기
// 남쪽: 가장 하단 (y값이 가장 큰), 북쪽: 가장 상단 (y값이 가장 작은)
// 동쪽: 가장 오른쪽 (x값이 가장 큰), 서쪽: 가장 왼쪽 (x값이 가장 작은)
const extremeValue = findExtreme(...straightLines.map((line) => line[coord1]))
const eavesLines = straightLines.filter((line) => line[coord1] === extremeValue)
// 4. 직선에 대해 타입 설정
straightLines.forEach((line) => {
if (eavesLines.includes(line)) {
// 가장 끝에 있는 직선은 eaves
line.attributes = {
...line.attributes,
type: LINE_TYPE.WALLLINE.EAVES,
}
} else {
// 나머지 직선은 ridge
line.attributes = {
...line.attributes,
type: LINE_TYPE.SUBLINE.RIDGE,
}
}
})
}
const roofOutlineColor = (roofIndex) => {
if (roofIndex === 1) {
return '#FFC000'

View File

@ -134,6 +134,7 @@
"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})",
"module.size.validation.rack.error": "モジュール配置が不正です。 正しく配置し直してください。",
"modal.module.basic.setting.module.placement": "モジュールの配置",
"modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置",

View File

@ -134,6 +134,7 @@
"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})",
"module.size.validation.rack.error": "모듈 배치가 잘못되었습니다. 올바르게 다시 배치해 주세요.",
"modal.module.basic.setting.module.placement": "모듈 배치",
"modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치",