diff --git a/src/components/floor-plan/modal/basic/BasicSetting.jsx b/src/components/floor-plan/modal/basic/BasicSetting.jsx index 8eda4a09..20578b24 100644 --- a/src/components/floor-plan/modal/basic/BasicSetting.jsx +++ b/src/components/floor-plan/modal/basic/BasicSetting.jsx @@ -35,6 +35,12 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { } }, []) + const placementRef = { + isChidori: useRef('true'), + setupLocation: useRef(null), + isMaxSetup: useRef('false'), + } + return (
@@ -55,7 +61,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { {tabNum === 1 && } {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && } - {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && } + {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && } {/*배치면 초기설정 - 입력방법: 육지붕*/} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && } @@ -78,7 +84,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { - diff --git a/src/components/floor-plan/modal/basic/step/Placement.jsx b/src/components/floor-plan/modal/basic/step/Placement.jsx index 4771ab2a..8acaff70 100644 --- a/src/components/floor-plan/modal/basic/step/Placement.jsx +++ b/src/components/floor-plan/modal/basic/step/Placement.jsx @@ -1,7 +1,12 @@ import { useMessage } from '@/hooks/useMessage' +import { forwardRef, useState } from 'react' -export default function Placement() { +const Placement = forwardRef((props, refs) => { const { getMessage } = useMessage() + const [isChidori, setIsChidori] = useState('true') + + console.log(refs) + const moduleData = { header: [ { type: 'check', name: '', prop: 'check', width: 70 }, @@ -24,6 +29,12 @@ export default function Placement() { }, ], } + + const handleChangeChidori = (e) => { + setIsChidori(e.target.value) + refs.isChidori.current = e.target.value + } + return ( <>
@@ -96,12 +107,20 @@ export default function Placement() {
- +
- - + +
@@ -137,4 +156,6 @@ export default function Placement() {
) -} +}) + +export default Placement diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 16debf6e..7d8ea54d 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -31,7 +31,7 @@ export function useModuleBasicSetting() { setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경 const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset //모듈설치영역?? 생성 - const setupSurface = new QPolygon(offsetPoints, { + let setupSurface = new QPolygon(offsetPoints, { stroke: 'red', fill: 'transparent', strokeDashArray: [10, 4], @@ -45,6 +45,8 @@ export function useModuleBasicSetting() { parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌 name: POLYGON_TYPE.MODULE_SETUP_SURFACE, flowDirection: roof.direction, + flipX: roof.flipX, + flipY: roof.flipY, }) setupSurface.setViewLengthText(false) @@ -65,7 +67,6 @@ export function useModuleBasicSetting() { //설치 범위 지정 클릭 이벤트 const toggleSelection = (setupSurface) => { console.log('setupSurface', setupSurface) - const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId) //최초 선택일때 if (!isExist) { @@ -380,7 +381,9 @@ export function useModuleBasicSetting() { } //자동 모듈 설치(그리드 방식) - const autoModuleSetup = () => { + const autoModuleSetup = (placementRef) => { + const isChidori = placementRef.isChidori.current + initEvent() const moduleSetupSurfaces = moduleSetupSurface //선택 설치면 @@ -405,6 +408,9 @@ export function useModuleBasicSetting() { if (moduleIsSetup.length > 0) { alert('기존 모듈은 제거됩니다.') + moduleIsSetup.forEach((module) => { + canvas?.remove(module) + }) } notSelectedTrestlePolygons.forEach((obj) => { @@ -484,16 +490,11 @@ export function useModuleBasicSetting() { width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4 height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2 } - const cols = Math.floor((bbox[2] - bbox[0]) / width) - const rows = Math.floor((bbox[3] - bbox[1]) / height) + let cols = Math.floor((bbox[2] - bbox[0]) / width) + let rows = Math.floor((bbox[3] - bbox[1]) / height) + + // cols = cols * 2 - let startCoords = { x: 0, y: 0 } - if (moduleSetupSurface.flowDirection === 'south') { - startCoords = { - x: surfaceBbox.minX, - y: surfaceBbox.maxY, - } - } for (let col = 0; col <= cols; col++) { for (let row = 0; row <= rows; row++) { let x = 0, @@ -530,13 +531,43 @@ export function useModuleBasicSetting() { x = bbox[0] + col * width y = bbox[1] + row * height } - square = [ - [x, y], - [x + width, y], - [x + width, y + height], - [x, y + height], - [x, y], - ] + + if (isChidori === 'true') { + if (row % 2 !== 0) { + square = [ + [x, y], + [x + width, y], + [x + width, y + height], + [x, y + height], + [x, y], + ] + } else { + square = [ + [x - width / 2, y], + [x - width / 2 + width, y], + [x - width / 2 + width, y + height], + [x - width / 2, y + height], + [x - width / 2, y], + ] + } + } else { + square = [ + [x, y], + [x + width, y], + [x + width, y + height], + [x, y + height], + [x, y], + ] + } + + // square = [ + // [x - width / 2, y], + // [x - width / 2 + width, y], + // [x - width / 2 + width, y + height], + // [x - width / 2, y + height], + // [x - width / 2, y], + // ] + const squarePolygon = turf.polygon([square]) const disjointFromTrestle = turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) @@ -720,7 +751,7 @@ export function useModuleBasicSetting() { ) } - function batchObjectGroupToTurfPolygon(group) { + const batchObjectGroupToTurfPolygon = (group) => { const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon') let allPoints = [] @@ -732,32 +763,76 @@ export function useModuleBasicSetting() { return hull } - //도형의 내각을 구하는 로직 - function calculateInteriorAngles(polygon) { - const points = polygon.get('points') - const angles = [] + function calculatePerpendicularLine(hypotenuseStart, hypotenuseEnd, height) { + const { x: x1, y: y1 } = hypotenuseStart + const { x: x2, y: y2 } = hypotenuseEnd - for (let i = 0; i < points.length; i++) { - // 현재 점과 이전 및 다음 점 정의 - const current = points[i] - const prev = points[(i - 1 + points.length) % points.length] - const next = points[(i + 1) % points.length] + // 1. 빗변의 기울기 계산 + const slope = (y2 - y1) / (x2 - x1) - // 벡터 계산 - const vecA = { x: prev.x - current.x, y: prev.y - current.y } - const vecB = { x: next.x - current.x, y: next.y - current.y } + // 2. 중점 계산 + const xC = (x1 + x2) / 2 + const yC = (y1 + y2) / 2 - // 두 벡터 간 각도 계산 - const dotProduct = vecA.x * vecB.x + vecA.y * vecB.y - const magA = Math.sqrt(vecA.x * vecA.x + vecA.y * vecA.y) - const magB = Math.sqrt(vecB.x * vecB.x + vecB.y * vecB.y) - const angleRad = Math.acos(dotProduct / (magA * magB)) - const angleDeg = (angleRad * 180) / Math.PI + // 3. 수직 기울기의 정규화 인자 계산 + const norm = Math.sqrt(1 + Math.pow(slope, 2)) - // 내부 각도 저장 - angles.push(180 - angleDeg) + // 4. 수직선의 끝점 계산 + const xOffset = -height / norm + const yOffset = (height * slope) / norm + + const point1 = { x: xC + xOffset, y: yC + yOffset } + const point2 = { x: xC - xOffset, y: yC - yOffset } + + return { point1, point2 } + } + + // 예제 사용 + const hypotenuseStart = { x: 2, y: 3 } + const hypotenuseEnd = { x: 8, y: 9 } + const height = 4 + + const result = calculatePerpendicularLine(hypotenuseStart, hypotenuseEnd, height) + console.log(result) + + const calcMinXByHeightDistance = (surface) => { + let minXIndex = surface.lines.reduce((minIdx, current, index, arr) => { + console.log('reduce', minIdx, current, index, arr) + + return current.x1 < arr[minIdx].x1 ? index : minIdx + }, surface.lines[0].x1) + + console.log('minXIndex', minXIndex) + + function calculateIntersection(diagonalA, lineB) { + // Diagonal A coordinates + const { x1: ax1, y1: ay1, x2: ax2, y2: ay2 } = diagonalA + + // Line B coordinates + const { x2: bx2, y2: by2 } = lineB + + // Calculate slope (m) and intercept (c) for diagonal A + const slopeA = (ay2 - ay1) / (ax2 - ax1) + const interceptA = ay1 - slopeA * ax1 + + // Use fixed y (from lineB's y2) + const yFixed = by2 + + // Calculate x on diagonal A where y = yFixed + const xFixed = (yFixed - interceptA) / slopeA + + // Return the intersection point + return { x: xFixed, y: yFixed } } - return angles + + console.log('line', line) + + // Example usage: + const diagonalA = { x1: 490.4, y1: 94.7, x2: 303.7, y2: 654.7 } + const lineB = { x1: 303.7, y1: 654.7, x2: 303.7, y2: 541.3 } + + const intersection = calculateIntersection(diagonalA, lineB) + console.log(`Intersection point: x = ${intersection.x}, y = ${intersection.y}`) } return { diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index 116fe639..3731443d 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -102,6 +102,15 @@ export function useSurfaceShapeBatch() { canvas?.add(obj) + canvas?.renderAll() + closePopup(id) + }) + + addCanvasMouseEventListener('mouse:down', (e) => { + isDrawing = false + + canvas?.remove(obj) + //각도 추가 let originAngle = 0 //기본 남쪽 let direction = 'south' @@ -119,21 +128,31 @@ export function useSurfaceShapeBatch() { direction = 'north' } - obj.set({ direction: direction }) - obj.set({ originAngle: originAngle }) - - canvas?.renderAll() + //회전, flip등이 먹은 기준으로 새로생성 + const batchSurface = new QPolygon(obj.getCurrentPoints(), { + fill: 'transparent', + stroke: 'red', + strokeWidth: 1, + strokeDasharray: [10, 4], + fontSize: 12, + selectable: true, + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + lockScalingX: true, // X 축 크기 조정 잠금 + lockScalingY: true, // Y 축 크기 조정 잠금 + name: POLYGON_TYPE.ROOF, + originX: 'center', + originY: 'center', + pitch: globalPitch, + surfaceId: surfaceId, + direction: direction, + }) + canvas?.add(batchSurface) + setSurfaceShapePattern(batchSurface, roofDisplay.column) + drawDirectionArrow(batchSurface) closePopup(id) - }) - - addCanvasMouseEventListener('mouse:down', (e) => { - isDrawing = false - obj.set('name', POLYGON_TYPE.ROOF) - obj.set('surfaceId', surfaceId) initEvent() - setSurfaceShapePattern(obj, roofDisplay.column) - closePopup(id) - drawDirectionArrow(obj) }) } } @@ -880,12 +899,45 @@ export function useSurfaceShapeBatch() { canvas?.renderAll() } - const changeSurfaceFlowDirection = (roof, direction, orientation) => { - roof.set({ - direction: direction, + const updateFlippedPoints = (polygon) => { + if (!(polygon instanceof fabric.Polygon)) { + console.error('The object is not a Polygon.') + return + } + + const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = 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 } }) - drawDirectionArrow(roof) - canvas?.renderAll() + + // flipX, flipY를 초기화 + polygon.flipX = false + polygon.flipY = false + + // points 업데이트 + polygon.set({ points: newPoints }) + polygon.setCoords() + + return polygon } return { diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 1f5f59cb..97b34c2c 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -285,8 +285,6 @@ export function useContextMenu() { }, [currentContextMenu]) useEffect(() => { - console.log(currentObject) - if (currentObject?.name) { console.log('object', currentObject) switch (currentObject.name) {