dev #217

Merged
ysCha merged 2 commits from dev into prd-deploy 2025-07-18 15:09:22 +09:00
3 changed files with 129 additions and 8 deletions
Showing only changes of commit 2f6a4a3ee8 - Show all commits

View File

@ -1,17 +1,18 @@
import { useEffect } from 'react'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue } from 'recoil'
import { wordDisplaySelector } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent'
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { fontSelector } from '@/store/fontAtom'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { canvasState } from '@/store/canvasAtom'
import { v4 as uuidv4 } from 'uuid'
import { usePopup } from '@/hooks/usePopup'
import Distance from '@/components/floor-plan/modal/distance/Distance'
import { usePolygon } from '@/hooks/usePolygon'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { BATCH_TYPE } from '@/common/common'
import { useMouse } from '@/hooks/useMouse'
export function useCommonUtils() {
const canvas = useRecoilValue(canvasState)
@ -25,6 +26,7 @@ export function useCommonUtils() {
const { addPopup, closeAll, targetClose } = usePopup()
const { drawDirectionArrow, addLengthText } = usePolygon()
const { applyDormers } = useObjectBatch({})
const { getIntersectMousePoint } = useMouse()
useEffect(() => {
commonTextMode()
@ -213,7 +215,7 @@ export function useCommonUtils() {
addCanvasMouseEventListener('mouse:down', (e) => {
let groupObjects = []
const pointer = canvas.getPointer(e.e)
const pointer = getIntersectMousePoint(e)
let point
@ -654,7 +656,11 @@ export function useCommonUtils() {
clonedObj.setCoords()
clonedObj.fire('modified')
clonedObj.fire('polygonMoved')
clonedObj.set({ direction: obj.direction, directionText: obj.directionText, roofMaterial: obj.roofMaterial })
clonedObj.set({
direction: obj.direction,
directionText: obj.directionText,
roofMaterial: obj.roofMaterial,
})
obj.lines.forEach((line, index) => {
clonedObj.lines[index].set({ attributes: line.attributes })

View File

@ -210,6 +210,14 @@ export function useEvent() {
})
})
const modulePoints = []
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
modules.forEach((module) => {
module.points.forEach((point) => {
modulePoints.push({ x: point.x, y: point.y })
})
})
let adsorptionPoints = [
...getAdsorptionPoints(),
...roofAdsorptionPoints.current,
@ -229,6 +237,7 @@ export function useEvent() {
y: line.y2,
}
}),
...modulePoints,
]
adsorptionPoints = removeDuplicatePoints(adsorptionPoints)

View File

@ -303,13 +303,119 @@ export function removeDuplicatePolygons(polygons, hasAuxiliaryLine = false) {
}
})
if (!hasAuxiliaryLine) {
uniquePolygons = uniquePolygons.filter((polygon) => {
return isValidPoints(polygon)
uniquePolygons = uniquePolygons.filter((polygon) => {
return !checkPolygonSelfIntersection(polygon)
})
// uniquePolygons = uniquePolygons.filter((polygon) => {
// return isValidPoints(polygon)
// })
console.log('uniquePolygons2', uniquePolygons)
return uniquePolygons
}
/**
* 선분이 교차하는지 확인하는 함수
* @param {Object} p1 번째 선분의 시작점 {x, y}
* @param {Object} q1 번째 선분의 끝점 {x, y}
* @param {Object} p2 번째 선분의 시작점 {x, y}
* @param {Object} q2 번째 선분의 끝점 {x, y}
* @returns {boolean} 교차하면 true, 아니면 false
*/
function doSegmentsIntersect(p1, q1, p2, q2) {
// CCW (Counter-Clockwise) 방향 확인 함수
function orientation(p, q, r) {
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
if (val === 0) return 0 // 일직선
return val > 0 ? 1 : 2 // 시계방향 또는 반시계방향
}
// 점 q가 선분 pr 위에 있는지 확인
function onSegment(p, q, r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y)
}
// 같은 끝점을 공유하는 경우는 교차로 보지 않음
if ((p1.x === p2.x && p1.y === p2.y) || (p1.x === q2.x && p1.y === q2.y) || (q1.x === p2.x && q1.y === p2.y) || (q1.x === q2.x && q1.y === q2.y)) {
return false
}
const o1 = orientation(p1, q1, p2)
const o2 = orientation(p1, q1, q2)
const o3 = orientation(p2, q2, p1)
const o4 = orientation(p2, q2, q1)
// 일반적인 교차 경우
if (o1 !== o2 && o3 !== o4) return true
// 특별한 경우들 (한 점이 다른 선분 위에 있는 경우)
if (o1 === 0 && onSegment(p1, p2, q1)) return true
if (o2 === 0 && onSegment(p1, q2, q1)) return true
if (o3 === 0 && onSegment(p2, p1, q2)) return true
if (o4 === 0 && onSegment(p2, q1, q2)) return true
return false
}
/**
* 다각형의 자기 교차를 검사하는 메인 함수
* @param {Array} coordinates 다각형의 좌표 배열 [{x, y}, ...]
* @returns {Object} 검사 결과 {hasSelfIntersection: boolean, intersections: Array}
*/
function checkPolygonSelfIntersection(coordinates) {
if (coordinates.length < 3) {
return {
hasSelfIntersection: false,
intersections: [],
error: '다각형은 최소 3개의 점이 필요합니다.',
}
}
const intersections = []
const edges = []
// 모든 변(edge) 생성
for (let i = 0; i < coordinates.length; i++) {
const start = coordinates[i]
const end = coordinates[(i + 1) % coordinates.length]
edges.push({
start: start,
end: end,
index: i,
})
}
return uniquePolygons
// 모든 변 쌍에 대해 교차 검사
for (let i = 0; i < edges.length; i++) {
for (let j = i + 1; j < edges.length; j++) {
// 인접한 변들은 제외 (끝점을 공유하므로)
if (Math.abs(i - j) === 1 || (i === 0 && j === edges.length - 1)) {
continue
}
const edge1 = edges[i]
const edge2 = edges[j]
if (doSegmentsIntersect(edge1.start, edge1.end, edge2.start, edge2.end)) {
intersections.push({
edge1Index: i,
edge2Index: j,
edge1: {
from: edge1.start,
to: edge1.end,
},
edge2: {
from: edge2.start,
to: edge2.end,
},
})
}
}
}
return intersections.length > 0
}
// 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false