This commit is contained in:
Jaeyoung Lee 2025-07-11 11:23:27 +09:00
commit ac9d2b593e
14 changed files with 357 additions and 119 deletions

View File

@ -183,29 +183,64 @@ export const QLine = fabric.util.createClass(fabric.Line, {
return this return this
}, },
containsPoint: function(point) { setCoords: function () {
// 부모 클래스의 setCoords 호출
this.callSuper('setCoords')
// QLine의 경우 추가 처리 - 항상 강제로 재계산
if (this.canvas) {
// 모든 좌표 관련 캐시 초기화
delete this.oCoords
delete this.aCoords
delete this.__corner
// 다시 부모 setCoords 호출
this.callSuper('setCoords')
// 한 번 더 강제로 bounding rect 재계산
this._clearCache && this._clearCache()
}
},
containsPoint: function (point) {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = 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)
}
}
// 기본 boundingRect 사용하되 줌을 고려하여 선택 영역 조정
const boundingRect = this.getBoundingRect(true) const boundingRect = this.getBoundingRect(true)
// 선의 방향 판단 // 선의 방향 판단
const dx = Math.abs(this.x2 - this.x1) const dx = Math.abs(this.x2 - this.x1)
const dy = Math.abs(this.y2 - this.y1) const dy = Math.abs(this.y2 - this.y1)
const isVertical = dx < dy && dx < 10 // 세로선 (가로 변화가 작음) const isVertical = dx < dy && dx < 10
const isDiagonal = dx > 10 && dy > 10 // 대각선 (가로, 세로 모두 변화) const isDiagonal = dx > 10 && dy > 10
// 줌 레벨에 따른 선택 영역 조정
const zoom = this.canvas ? this.canvas.getZoom() : 1
const baseMultiplier = 1 // 줌이 클수록 선택 영역을 줄임
let reducedWidth, reducedHeight let reducedWidth, reducedHeight
if (isDiagonal) { if (isDiagonal) {
// 대각선의 경우: 1/2 크기 reducedWidth = Math.max(boundingRect.width / 2, 10 * baseMultiplier)
reducedWidth = boundingRect.width / 2 reducedHeight = Math.max(boundingRect.height / 2, 10 * baseMultiplier)
reducedHeight = boundingRect.height / 2
} else if (isVertical) { } else if (isVertical) {
// 세로선의 경우: 가로 선택범위 2배 reducedWidth = Math.max(boundingRect.width * 2, 20 * baseMultiplier)
reducedWidth = boundingRect.width * 2 reducedHeight = boundingRect.height
reducedHeight = boundingRect.height / 3
} else { } else {
// 가로선의 경우: 세로 선택범위 2배 reducedWidth = boundingRect.width
reducedWidth = boundingRect.width / 3 reducedHeight = Math.max(boundingRect.height * 2, 20 * baseMultiplier)
reducedHeight = boundingRect.height * 2
} }
// 축소된 영역의 중심점 계산 // 축소된 영역의 중심점 계산
@ -219,9 +254,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
const bottom = centerY + reducedHeight / 2 const bottom = centerY + reducedHeight / 2
// 점이 축소된 영역 내에 있는지 확인 // 점이 축소된 영역 내에 있는지 확인
return point.x >= left && return localPoint.x >= left && localPoint.x <= right && localPoint.y >= top && localPoint.y <= bottom
point.x <= right &&
point.y >= top &&
point.y <= bottom
}, },
}) })

View File

@ -689,13 +689,46 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
return intersects % 2 === 1 return intersects % 2 === 1
}, },
setCoords: function () {
// 부모 클래스의 setCoords 호출
this.callSuper('setCoords')
// QPolygon의 경우 추가 처리 - 항상 강제로 재계산
if (this.canvas) {
// 모든 좌표 관련 캐시 초기화
delete this.oCoords
delete this.aCoords
delete this.__corner
// 다시 부모 setCoords 호출
this.callSuper('setCoords')
// 한 번 더 강제로 bounding rect 재계산
this._clearCache && this._clearCache()
}
},
containsPoint: function (point) { containsPoint: function (point) {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = 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)
}
}
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygon(point) const isInside = this.inPolygon(localPoint)
this.set('selectable', isInside) this.set('selectable', isInside)
return isInside return isInside
} else { } else {
return this.callSuper('containsPoint', point) return this.callSuper('containsPoint', localPoint)
} }
}, },

View File

@ -8,8 +8,12 @@ import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall' import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing' import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useEffect } from 'react'
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) { export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
@ -52,6 +56,15 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
cutAuxiliary, cutAuxiliary,
} = useAuxiliaryDrawing(id) } = useAuxiliaryDrawing(id)
useEffect(() => {
return () => {
const auxiliaryLines = canvas.getObjects().filter((line) => line.name === 'auxiliaryLine' && !line.isAuxiliaryFixed) // .
if (auxiliaryLines.length > 0) {
handleFix()
}
}
}, [])
const outerLineProps = { const outerLineProps = {
length1, length1,
setLength1, setLength1,

View File

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

View File

@ -1,7 +1,7 @@
import getConfigs from './config.common' import getConfigs from './config.common'
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 development 환경에 맞는 값을 지정합니다.) // 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 development 환경에 맞는 값을 지정합니다.)
const baseUrl = 'https://dev.hanssys.jp' const baseUrl = 'https://dev.hanasys.jp'
const mode = 'development' const mode = 'development'
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다. // 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.

View File

@ -2,20 +2,20 @@ import * as turf from '@turf/turf'
export const useTurf = () => { export const useTurf = () => {
/** /**
* 배치면 안에 있는지 확인 * 모듈이 배치면 안에 있는지 확인
* @param {*} squarePolygon * @param {*} module
* @param {*} turfModuleSetupSurface * @param {*} surface
* @param spare * @param spare
* @returns * @returns
*/ */
const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface, spare = 1) => { const checkModuleDisjointSurface = (module, surface, spare = 1) => {
// 표면 영역을 spare만큼 수동 확장 // 표면 영역을 spare만큼 수동 확장
const expandedSurface = { const expandedSurface = {
type: 'Polygon', type: 'Polygon',
coordinates: [ coordinates: [
turfModuleSetupSurface.geometry.coordinates[0].map(([x, y]) => { surface.geometry.coordinates[0].map(([x, y]) => {
// 각 점을 바깥쪽으로 2 단위씩 이동 // 각 점을 바깥쪽으로 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 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) 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 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 { canvasState } from '@/store/canvasAtom'
import { isOverlap, polygonToTurfPolygon, rectToPolygon } from '@/util/canvas-util' import { polygonToTurfPolygon } from '@/util/canvas-util'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { useSwal } from '../useSwal' import { useSwal } from '../useSwal'
import { useModuleBasicSetting } from './useModuleBasicSetting' import { useModuleBasicSetting } from './useModuleBasicSetting'
import { useMessage } from '../useMessage' import { useMessage } from '../useMessage'
import { selectedModuleState } from '@/store/selectedModuleOptions' import { selectedModuleState } from '@/store/selectedModuleOptions'
import { moduleStatisticsState } from '@/store/circuitTrestleAtom'
import { useCircuitTrestle } from '../useCirCuitTrestle' import { useCircuitTrestle } from '../useCirCuitTrestle'
import { useTrestle } from './useTrestle' import { useTrestle } from './useTrestle'
import { useTurf } from '@/hooks/common/useTurf'
export const MODULE_REMOVE_TYPE = { export const MODULE_REMOVE_TYPE = {
LEFT: 'left', LEFT: 'left',
@ -42,6 +42,7 @@ export function useModule() {
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const { setModuleStatisticsData, resetCircuits } = useCircuitTrestle() const { setModuleStatisticsData, resetCircuits } = useCircuitTrestle()
const { clear: removeTrestleMaterials } = useTrestle() const { clear: removeTrestleMaterials } = useTrestle()
const { checkModuleDisjointSurface } = useTurf()
const moduleMove = (length, direction) => { const moduleMove = (length, direction) => {
const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴 const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴
@ -70,13 +71,16 @@ export function useModule() {
top: module.top, top: module.top,
fill: module.fill, fill: module.fill,
} }
module.set({ top, left }) module.set({ top, left, strokeWidth: 0.3 })
module.setCoords() module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface)) { if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface)) {
isWarning = true isWarning = true
module.set({ fill: 'red' }) module.set({ fill: 'red' })
} }
module.set({ strokeWidth: 3 })
canvas.renderAll() canvas.renderAll()
}) })
@ -87,7 +91,11 @@ export function useModule() {
type: 'alert', type: 'alert',
confirmFn: () => { confirmFn: () => {
selectedModules.forEach((module) => { 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() module.setCoords()
}) })
canvas.renderAll() canvas.renderAll()
@ -126,6 +134,7 @@ export function useModule() {
const objects = getObjects() const objects = getObjects()
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId) const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
let isWarning = false let isWarning = false
activeModule.set({ strokeWidth: 0.3 })
modules.forEach((module) => { modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false) const { top, left } = getPosotion(module, direction, length, false)
@ -145,6 +154,8 @@ export function useModule() {
} }
}) })
activeModule.set({ strokeWidth: 3 })
canvas.renderAll() canvas.renderAll()
if (isWarning) { if (isWarning) {
swalFire({ swalFire({
@ -350,6 +361,7 @@ export function useModule() {
return return
} }
const activeModule = canvas.getObjects().find((obj) => canvas.getActiveObjects()[0].id === obj.id) const activeModule = canvas.getObjects().find((obj) => canvas.getActiveObjects()[0].id === obj.id)
activeModule.set({ strokeWidth: 1 })
if (activeModule.circuit) { if (activeModule.circuit) {
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
if (surfaces.some((surface) => surface.isComplete)) { if (surfaces.some((surface) => surface.isComplete)) {
@ -383,6 +395,7 @@ export function useModule() {
obj.set({ obj.set({
parentId: module.parentId, parentId: module.parentId,
initOptions: module.initOptions, initOptions: module.initOptions,
stroke: 'black',
direction: module.direction, direction: module.direction,
arrow: module.arrow, arrow: module.arrow,
name: module.name, name: module.name,
@ -411,6 +424,7 @@ export function useModule() {
} }
canvas.renderAll() canvas.renderAll()
}) })
activeModule.set({ strokeWidth: 3 })
if (isWarning) { if (isWarning) {
swalFire({ swalFire({
@ -982,10 +996,7 @@ export function useModule() {
} }
const isOutsideSurface = (module, moduleSetupSurface) => { const isOutsideSurface = (module, moduleSetupSurface) => {
return ( return !checkModuleDisjointSurface(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true))
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true))
)
} }
const getRowModules = (target) => { const getRowModules = (target) => {
@ -1030,9 +1041,7 @@ export function useModule() {
} }
const getObjects = () => { const getObjects = () => {
return canvas return canvas?.getObjects().filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER].includes(obj.name))
?.getObjects()
.filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER, BATCH_TYPE.SHADOW].includes(obj.name))
} }
const resetSurface = () => { const resetSurface = () => {

View File

@ -1,4 +1,4 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { import {
@ -8,32 +8,29 @@ import {
currentObjectState, currentObjectState,
isManualModuleLayoutSetupState, isManualModuleLayoutSetupState,
isManualModuleSetupState, isManualModuleSetupState,
moduleSetupOptionState,
toggleManualSetupModeState,
moduleRowColArrayState, moduleRowColArrayState,
moduleSetupOptionState,
moduleSetupSurfaceState,
toggleManualSetupModeState,
} from '@/store/canvasAtom' } 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 { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils' import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { moduleSetupSurfaceState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent' 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 * as turf from '@turf/turf'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { compasDegAtom } from '@/store/orientationAtom' import { compasDegAtom } from '@/store/orientationAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { useRoofFn } from '@/hooks/common/useRoofFn' import { useRoofFn } from '@/hooks/common/useRoofFn'
import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useMasterController } from '@/hooks/common/useMasterController'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle' import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { usePolygon } from '@/hooks/usePolygon'
import { useTurf } from '@/hooks/common/useTurf' import { useTurf } from '@/hooks/common/useTurf'
export function useModuleBasicSetting(tabNum) { export function useModuleBasicSetting(tabNum) {
@ -91,7 +88,10 @@ export function useModuleBasicSetting(tabNum) {
if (roofConstructions && roofConstructions.length > 0) { if (roofConstructions && roofConstructions.length > 0) {
//roofIndex 넣기 //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') { if (moduleSelectionData.common.saleStoreNorthFlg == '1') {
@ -137,7 +137,10 @@ export function useModuleBasicSetting(tabNum) {
const offsetObjects = moduleSelectionData.roofConstructions.find((item) => item.addRoof.index === roofIndex) const offsetObjects = moduleSelectionData.roofConstructions.find((item) => item.addRoof.index === roofIndex)
roof.lines.forEach((line) => { 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) makeModuleInstArea(roof, detail)
@ -150,7 +153,11 @@ export function useModuleBasicSetting(tabNum) {
const moduleRowArray = [] const moduleRowArray = []
if (isObjectNotEmpty(detail) && detail.module.length > 0) { if (isObjectNotEmpty(detail) && detail.module.length > 0) {
detail.module.forEach((module) => { 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) rowColArray.push(moduleRowArray)
@ -831,7 +838,10 @@ export function useModuleBasicSetting(tabNum) {
const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
//현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능 //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
if (checkedModule[0].mixAsgYn !== mixAsgYn) { 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 return
} }
} }
@ -901,10 +911,10 @@ export function useModuleBasicSetting(tabNum) {
setModuleStatisticsData() setModuleStatisticsData()
//그림자는 무조건 가장 앞으로 //그림자는 무조건 가장 앞으로
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW) // const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) { // if (shadowObj) {
shadowObj.bringToFront() // shadowObj.bringToFront()
} // }
// getModuleStatistics() // getModuleStatistics()
} else { } else {
@ -1312,7 +1322,10 @@ export function useModuleBasicSetting(tabNum) {
const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
//현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능 //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
if (checkedModule[0].mixAsgYn !== mixAsgYn) { 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 return
} }
} }
@ -1764,10 +1777,10 @@ export function useModuleBasicSetting(tabNum) {
} }
}) })
//그림자가 있다면 무조건 그림자를 가장 앞으로 올림 //그림자가 있다면 무조건 그림자를 가장 앞으로 올림
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW) // const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) { // if (shadowObj) {
shadowObj.bringToFront() // shadowObj.bringToFront()
} // }
} }
}) })
} }
@ -2108,6 +2121,7 @@ export function useModuleBasicSetting(tabNum) {
let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직 let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1 let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
calcAreaHeight = isNaN(calcAreaHeight) ? moduleSetupSurface.height : calcAreaHeight
let calcModuleHeightCount = calcAreaHeight / (height + intvVer) let calcModuleHeightCount = calcAreaHeight / (height + intvVer)
if (type === MODULE_SETUP_TYPE.LAYOUT) { if (type === MODULE_SETUP_TYPE.LAYOUT) {
@ -2175,7 +2189,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[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 tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2353,7 +2372,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[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 tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2533,7 +2557,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[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 tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let turfSurface = polygonToTurfPolygon(moduleSetupSurface, true) let turfSurface = polygonToTurfPolygon(moduleSetupSurface, true)
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, turfSurface) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, turfSurface)
@ -2717,7 +2746,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[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 tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))
@ -2829,10 +2863,10 @@ export function useModuleBasicSetting(tabNum) {
// setModuleIsSetup(moduleArray) // setModuleIsSetup(moduleArray)
//그림자는 무조건 가장 앞으로 //그림자는 무조건 가장 앞으로
const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW) // const shadowObj = canvas?.getObjects().find((obj) => obj.name === BATCH_TYPE.SHADOW)
if (shadowObj) { // if (shadowObj) {
shadowObj.bringToFront() // shadowObj.bringToFront()
} // }
}) })
// calculateForApi() // calculateForApi()
@ -2841,7 +2875,10 @@ export function useModuleBasicSetting(tabNum) {
*/ */
if (type === MODULE_SETUP_TYPE.LAYOUT && failAutoSetupRoof.length) { if (type === MODULE_SETUP_TYPE.LAYOUT && failAutoSetupRoof.length) {
const roofNames = failAutoSetupRoof.map((roof) => roof.roofMaterial.roofMatlNmJp).join(', ') 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 +2974,11 @@ export function useModuleBasicSetting(tabNum) {
const pointY2 = top 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?.add(finalLine)
// canvas?.renderAll() // canvas?.renderAll()
@ -2971,7 +3012,14 @@ export function useModuleBasicSetting(tabNum) {
type: 'flat', type: 'flat',
} }
} else { } 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) rtnObjArray.push(rtnObj)
@ -3058,7 +3106,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 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?.add(finalLine)
// canvas?.renderAll() // canvas?.renderAll()
@ -3092,7 +3144,14 @@ export function useModuleBasicSetting(tabNum) {
type: 'flat', type: 'flat',
} }
} else { } 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) rtnObjArray.push(rtnObj)
}) })
@ -3469,7 +3528,10 @@ export function useModuleBasicSetting(tabNum) {
const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn const mixAsgYn = trestlePolygon.modules[0].moduleInfo.mixAsgYn
//현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능 //현재 체크된 모듈기준으로 혼합가능인지 확인 Y === Y, N === N 일때만 설치 가능
if (checkedModule[0].mixAsgYn !== mixAsgYn) { 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 return
} }
} }
@ -3479,7 +3541,10 @@ export function useModuleBasicSetting(tabNum) {
const rectPoints = [ const rectPoints = [
{ x: tempModule.left, y: tempModule.top }, { 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 },
{ 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 }, { x: tempModule.left, y: tempModule.top + tempModule.height * tempModule.scaleY },
] ]
@ -3799,7 +3864,12 @@ export function useModuleBasicSetting(tabNum) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
let points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[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 tempModule = new QPolygon(points, { ...moduleOptions, turfPoints: squarePolygon })
let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true)) let disjointFromTrestle = checkModuleDisjointSurface(squarePolygon, polygonToTurfPolygon(moduleSetupSurface, true))

View File

@ -680,7 +680,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], { const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: true,
name: 'auxiliaryLine', name: 'auxiliaryLine',
isFixed: true, isFixed: true,
attributes: { ...line1.attributes }, attributes: { ...line1.attributes },
@ -693,7 +693,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], { const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: true,
name: 'auxiliaryLine', name: 'auxiliaryLine',
isFixed: true, isFixed: true,
attributes: { ...line1.attributes }, attributes: { ...line1.attributes },
@ -737,7 +737,6 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
// line2.set(originLine2) // line2.set(originLine2)
// canvas.renderAll() // canvas.renderAll()
const intersectionPoint = calculateIntersection(line1, line2) const intersectionPoint = calculateIntersection(line1, line2)
if (!intersectionPoint) { if (!intersectionPoint) {
return return
@ -865,6 +864,8 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
return return
} }
cutAuxiliary()
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거 //lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거

View File

@ -1,28 +1,25 @@
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentAngleTypeSelector, currentObjectState } from '@/store/canvasAtom'
import { useContext, useEffect, useRef, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { import {
correntObjectNoState,
addedRoofsState, addedRoofsState,
basicSettingState, basicSettingState,
correntObjectNoState,
corridorDimensionSelector,
roofDisplaySelector, roofDisplaySelector,
roofMaterialsSelector, roofMaterialsSelector,
selectedRoofMaterialSelector, selectedRoofMaterialSelector,
settingModalFirstOptionsState,
corridorDimensionSelector,
} from '@/store/settingAtom' } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting' import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useRoofFn } from '@/hooks/common/useRoofFn' import { useRoofFn } from '@/hooks/common/useRoofFn'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
@ -44,8 +41,10 @@ export function useRoofAllocationSetting(id) {
const roofMaterials = useRecoilValue(roofMaterialsSelector) const roofMaterials = useRecoilValue(roofMaterialsSelector)
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState) const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState)
const [currentRoofMaterial, setCurrentRoofMaterial] = useState(roofMaterials[0]) /** 팝업 내 기준 지붕재 */ const [currentRoofMaterial, setCurrentRoofMaterial] = useState(roofMaterials[0])
const [roofList, setRoofList] = useRecoilState(addedRoofsState) /** 배치면 초기설정에서 선택한 지붕재 배열 */ /** 팝업 내 기준 지붕재 */
const [roofList, setRoofList] = useRecoilState(addedRoofsState)
/** 배치면 초기설정에서 선택한 지붕재 배열 */
const [editingLines, setEditingLines] = useState([]) const [editingLines, setEditingLines] = useState([])
const [currentRoofList, setCurrentRoofList] = useState([]) const [currentRoofList, setCurrentRoofList] = useState([])
const currentAngleType = useRecoilValue(currentAngleTypeSelector) const currentAngleType = useRecoilValue(currentAngleTypeSelector)
@ -178,7 +177,11 @@ export function useRoofAllocationSetting(id) {
selectedRoofMaterial: selectRoofs.find((roof) => roof.selected), 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) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
@ -339,11 +342,18 @@ export function useRoofAllocationSetting(id) {
* 지붕면 할당 * 지붕면 할당
*/ */
const handleAlloc = () => { const handleAlloc = () => {
if (!checkInnerLines()) { const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
apply() roofBases.forEach((roof) => {
} else { if (roof.separatePolygon.length === 0) {
swalFire({ type: 'alert', icon: 'error', text: getMessage('modal.roof.allocation.auxiliary.size.form') }) roof.innerLines.forEach((line) => {
} if ((!line.attributes.actualSize || line.attributes?.actualSize === 0) && line.length > 1) {
line.set({ attributes: { ...line.attributes, actualSize: line.attributes.planeSize } })
}
})
}
})
apply()
} }
/** /**
@ -546,7 +556,12 @@ export function useRoofAllocationSetting(id) {
const newRoofList = currentRoofList.map((roof, idx) => { const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) { if (idx === index) {
const result = const result =
currentAngleType === 'slope' ? { pitch: value, angle: getDegreeByChon(value) } : { pitch: getChonByDegree(value), angle: value } currentAngleType === 'slope'
? {
pitch: value,
angle: getDegreeByChon(value),
}
: { pitch: getChonByDegree(value), angle: value }
return { ...roof, ...result } return { ...roof, ...result }
} }
return roof return roof

View File

@ -16,7 +16,6 @@ import { useTempGrid } from '@/hooks/useTempGrid'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { gridDisplaySelector } from '@/store/settingAtom' import { gridDisplaySelector } from '@/store/settingAtom'
import { MENU, POLYGON_TYPE } from '@/common/common' import { MENU, POLYGON_TYPE } from '@/common/common'
import useMenu from '@/hooks/common/useMenu'
export function useEvent() { export function useEvent() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -105,6 +104,10 @@ export function useEvent() {
canvas.setViewportTransform(canvas.viewportTransform) canvas.setViewportTransform(canvas.viewportTransform)
canvas.requestRenderAll() canvas.requestRenderAll()
canvas.getObjects().forEach((obj) => {
obj.setCoords()
})
// 이벤트의 기본 동작 방지 (스크롤 방지) // 이벤트의 기본 동작 방지 (스크롤 방지)
opt.e.preventDefault() opt.e.preventDefault()
opt.e.stopPropagation() opt.e.stopPropagation()

View File

@ -1,14 +1,7 @@
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util'
distanceBetweenPoints,
findAndRemoveClosestPoint,
getDegreeByChon,
getDegreeInOrientation,
isPointOnLine,
toFixedWithoutRounding,
} from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
import { flowDisplaySelector } from '@/store/settingAtom' import { flowDisplaySelector } from '@/store/settingAtom'
@ -1073,8 +1066,14 @@ export const usePolygon = () => {
) )
}) })
allLines = allLines.filter((line) => {
return Math.abs(line.startPoint.x - line.endPoint.x) > 2 || Math.abs(line.startPoint.y - line.endPoint.y) > 2
})
// 나눠서 중복 제거된 roof return // 나눠서 중복 제거된 roof return
const newRoofs = getSplitRoofsPoints(allLines) let newRoofs = getSplitRoofsPoints(allLines)
newRoofs = newRoofs.filter((roof) => roof.length !== 0)
newRoofs.forEach((roofPoint, index) => { newRoofs.forEach((roofPoint, index) => {
let defense, pitch let defense, pitch
@ -1212,10 +1211,45 @@ export const usePolygon = () => {
} }
const getSplitRoofsPoints = (allLines) => { const getSplitRoofsPoints = (allLines) => {
// 모든 좌표점들을 수집
const allPoints = []
allLines.forEach((line, lineIndex) => {
allPoints.push({ point: line.startPoint, lineIndex, pointType: 'start' })
allPoints.push({ point: line.endPoint, lineIndex, pointType: 'end' })
})
// X 좌표 통일
for (let i = 0; i < allPoints.length; i++) {
for (let j = i + 1; j < allPoints.length; j++) {
const point1 = allPoints[i].point
const point2 = allPoints[j].point
if (Math.abs(point1.x - point2.x) < 1) {
const maxX = Math.max(point1.x, point2.x)
point1.x = maxX
point2.x = maxX
}
}
}
// Y 좌표 통일
for (let i = 0; i < allPoints.length; i++) {
for (let j = i + 1; j < allPoints.length; j++) {
const point1 = allPoints[i].point
const point2 = allPoints[j].point
if (Math.abs(point1.y - point2.y) < 1) {
const maxY = Math.max(point1.y, point2.y)
point1.y = maxY
point2.y = maxY
}
}
}
// ==== Utility functions ==== // ==== Utility functions ====
function isSamePoint(p1, p2, epsilon = 1) { function isSamePoint(p1, p2, epsilon = 1) {
return Math.abs(p1.x - p2.x) <= 2 && Math.abs(p1.y - p2.y) <= 2 return Math.abs(p1.x - p2.x) <= epsilon && Math.abs(p1.y - p2.y) <= epsilon
} }
function normalizePoint(p, epsilon = 1) { function normalizePoint(p, epsilon = 1) {
@ -1241,6 +1275,7 @@ export const usePolygon = () => {
const startKey = pointToKey(start, epsilon) const startKey = pointToKey(start, epsilon)
return (graph[startKey] || []).some((neighbor) => isSamePoint(neighbor.point, end, epsilon)) return (graph[startKey] || []).some((neighbor) => isSamePoint(neighbor.point, end, epsilon))
} }
// ==== Dijkstra pathfinding ==== // ==== Dijkstra pathfinding ====
function findShortestPath(start, end, graph, epsilon = 1) { function findShortestPath(start, end, graph, epsilon = 1) {
@ -1329,11 +1364,15 @@ export const usePolygon = () => {
} }
const startPoint = { ...line.startPoint } // 시작점 const startPoint = { ...line.startPoint } // 시작점
let arrivalPoint = { ...line.endPoint } // 도착점 let arrivalPoint = { ...line.endPoint } // 도착점
roofs.push(getPath(startPoint, arrivalPoint, graph)) roofs.push(getPath(startPoint, arrivalPoint, graph))
}) })
return removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100)) return removeDuplicatePolygons(
roofs.filter((roof) => roof.length < 100),
allLines.some((line) => line.name === 'auxiliaryLine'),
)
} }
const splitPolygonWithSeparate = (separates) => { const splitPolygonWithSeparate = (separates) => {

View File

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

View File

@ -292,7 +292,7 @@ function arePolygonsEqual(polygon1, polygon2) {
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index])) return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
} }
export function removeDuplicatePolygons(polygons) { export function removeDuplicatePolygons(polygons, hasAuxiliaryLine = false) {
let uniquePolygons = [] let uniquePolygons = []
// x가 전부 같거나, y가 전부 같은 경우 제거 // x가 전부 같거나, y가 전부 같은 경우 제거
@ -303,15 +303,33 @@ export function removeDuplicatePolygons(polygons) {
} }
}) })
uniquePolygons = uniquePolygons.filter((polygon) => { if (!hasAuxiliaryLine) {
return isValidPoints(polygon) uniquePolygons = uniquePolygons.filter((polygon) => {
}) return isValidPoints(polygon)
})
}
return uniquePolygons return uniquePolygons
} }
// 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false // 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false
const isValidPoints = (points) => { const isValidPoints = (points) => {
// 연속된 3개 이상의 점이 같은 x 또는 y 값을 가지는지 확인 (원형 배열로 처리)
for (let i = 0; i < points.length; i++) {
const point1 = points[i]
const point2 = points[(i + 1) % points.length]
const point3 = points[(i + 2) % points.length]
// x값이 같은 연속된 3개 점 확인
if (point1.x === point2.x && point2.x === point3.x) {
return false
}
// y값이 같은 연속된 3개 점 확인
if (point1.y === point2.y && point2.y === point3.y) {
return false
}
}
function isColinear(p1, p2, p3) { function isColinear(p1, p2, p3) {
return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y) return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y)
} }