치도리 배치 기능

This commit is contained in:
yjnoh 2024-11-21 09:04:15 +09:00
parent da90a6d26a
commit 76af0e208d
5 changed files with 220 additions and 68 deletions

View File

@ -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 (
<WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lx-2`}>
@ -55,7 +61,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
{/*배치면 초기설정 - 입력방법: 육지붕*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
@ -78,7 +84,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
<button className="btn-frame modal mr5" onClick={manualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act" onClick={autoModuleSetup}>
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>

View File

@ -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 (
<>
<div className="module-table-flex-wrap mb10">
@ -96,12 +107,20 @@ export default function Placement() {
<div className="self-item-td">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" />
<input
type="radio"
name="radio01"
id="ra01"
defaultChecked
checked={isChidori === 'true'}
value={'true'}
onChange={handleChangeChidori}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div>
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" />
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
<input type="radio" name="radio02" id="ra02" value={'false'} checked={isChidori === 'false'} onChange={handleChangeChidori} />
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div>
</div>
</div>
@ -137,4 +156,6 @@ export default function Placement() {
</div>
</>
)
}
})
export default Placement

View File

@ -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 {

View File

@ -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 {

View File

@ -285,8 +285,6 @@ export function useContextMenu() {
}, [currentContextMenu])
useEffect(() => {
console.log(currentObject)
if (currentObject?.name) {
console.log('object', currentObject)
switch (currentObject.name) {