배치면 방향 수정
This commit is contained in:
parent
85d9aca6d3
commit
437d552b3a
@ -159,6 +159,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||||
isDrawing = false
|
isDrawing = false
|
||||||
|
|
||||||
|
const { xInversion, yInversion } = surfaceRefs
|
||||||
canvas?.remove(obj)
|
canvas?.remove(obj)
|
||||||
|
|
||||||
//각도 추가
|
//각도 추가
|
||||||
@ -179,6 +180,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//회전, flip등이 먹은 기준으로 새로생성
|
//회전, flip등이 먹은 기준으로 새로생성
|
||||||
|
// const batchSurface = addPolygon(reorderedPoints, {
|
||||||
const batchSurface = addPolygon(obj.getCurrentPoints(), {
|
const batchSurface = addPolygon(obj.getCurrentPoints(), {
|
||||||
fill: 'transparent',
|
fill: 'transparent',
|
||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
@ -208,6 +210,38 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
// const popupId = uuidv4()
|
// const popupId = uuidv4()
|
||||||
// addPopup(popupId, 2, <PlacementSurfaceLineProperty roof={batchSurface} id={popupId} setIsHidden={setIsHidden} />)
|
// addPopup(popupId, 2, <PlacementSurfaceLineProperty roof={batchSurface} id={popupId} setIsHidden={setIsHidden} />)
|
||||||
|
|
||||||
|
// console.log('xInversion', xInversion) //상하반전
|
||||||
|
// console.log('yInversion', yInversion) //좌우반전
|
||||||
|
|
||||||
|
//좌우 반전일때 방향 변경
|
||||||
|
if (yInversion) {
|
||||||
|
batchSurface.lines.forEach((line) => {
|
||||||
|
const { x1, y1, x2, y2 } = line
|
||||||
|
if (line.x1 > line.x2) {
|
||||||
|
if (line.direction === 'left') {
|
||||||
|
line.direction = 'right'
|
||||||
|
line.x1 = x2
|
||||||
|
line.x2 = x1
|
||||||
|
} else if (line.direction === 'right') {
|
||||||
|
line.direction = 'left'
|
||||||
|
line.x1 = x2
|
||||||
|
line.x2 = x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.y2 > line.y1) {
|
||||||
|
if (line.direction === 'bottom') {
|
||||||
|
line.direction = 'top'
|
||||||
|
line.y1 = y2
|
||||||
|
line.y2 = y1
|
||||||
|
} else if (line.direction === 'top') {
|
||||||
|
line.direction = 'bottom'
|
||||||
|
line.y1 = y2
|
||||||
|
line.y2 = y1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
changeSurfaceLineType(batchSurface)
|
changeSurfaceLineType(batchSurface)
|
||||||
|
|
||||||
if (setIsHidden) setIsHidden(false)
|
if (setIsHidden) setIsHidden(false)
|
||||||
@ -491,18 +525,18 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
}
|
}
|
||||||
case 10: {
|
case 10: {
|
||||||
points = [
|
points = [
|
||||||
{ x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
|
|
||||||
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
|
|
||||||
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 },
|
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 },
|
||||||
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 },
|
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
|
||||||
{
|
{ x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
|
||||||
x: pointer.x + length1 / 2 - length1 + length2,
|
|
||||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
x: pointer.x + length1 / 2 - length1 + length2 + length3,
|
x: pointer.x + length1 / 2 - length1 + length2 + length3,
|
||||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
x: pointer.x + length1 / 2 - length1 + length2,
|
||||||
|
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||||
|
},
|
||||||
|
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 },
|
||||||
]
|
]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1095,10 +1129,10 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
if (line[coord1] === line[coord2]) {
|
if (line[coord1] === line[coord2]) {
|
||||||
if (line.direction === evaesDirection) {
|
if (line.direction === evaesDirection) {
|
||||||
line.attributes.type = LINE_TYPE.WALLLINE.EAVES
|
line.attributes.type = LINE_TYPE.WALLLINE.EAVES
|
||||||
line.stroke = 'rgb(47, 0, 255)'
|
line.stroke = 'rgb(1, 1, 1)'
|
||||||
} else if (line.direction === ridgeDirection) {
|
} else if (line.direction === ridgeDirection) {
|
||||||
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
||||||
line.stroke = 'rgb(44, 255, 2)'
|
line.stroke = 'rgb(9, 9, 9)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1118,20 +1152,21 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
: a
|
: a
|
||||||
})
|
})
|
||||||
|
|
||||||
if (
|
// if (
|
||||||
(polygon.direction === 'south' && maxLineSorted.direction === 'left') ||
|
// (polygon.direction === 'south' && maxLineSorted.direction === 'left') ||
|
||||||
(polygon.direction === 'north' && maxLineSorted.direction === 'right') ||
|
// (polygon.direction === 'north' && maxLineSorted.direction === 'right')
|
||||||
(polygon.direction === 'east' && maxLineSorted.direction === 'bottom') ||
|
// // ||
|
||||||
(polygon.direction === 'west' && maxLineSorted.direction === 'top')
|
// // (polygon.direction === 'east' && maxLineSorted.direction === 'bottom') ||
|
||||||
) {
|
// // (polygon.direction === 'west' && maxLineSorted.direction === 'top')
|
||||||
polygon.lines.forEach((line) => {
|
// ) {
|
||||||
if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
|
// polygon.lines.forEach((line) => {
|
||||||
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
// if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
|
||||||
} else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) {
|
// line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
||||||
line.attributes.type = LINE_TYPE.WALLLINE.EAVES
|
// } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) {
|
||||||
}
|
// line.attributes.type = LINE_TYPE.WALLLINE.EAVES
|
||||||
})
|
// }
|
||||||
}
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
if (maxLine.length === 1) {
|
if (maxLine.length === 1) {
|
||||||
const maxLineCoord = polygon.lines.reduce((a, b) => {
|
const maxLineCoord = polygon.lines.reduce((a, b) => {
|
||||||
@ -1141,31 +1176,226 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
: a
|
: a
|
||||||
})
|
})
|
||||||
|
|
||||||
const isRealEavesLine = polygon.lines.find((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
const isRealEavesLine = polygon.lines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
if (isRealEavesLine) {
|
if (isRealEavesLine.length > 0) {
|
||||||
if (polygon.direction === 'south' || polygon.direction === 'north') {
|
isRealEavesLine.forEach((line) => {
|
||||||
const targetCoord =
|
if (polygon.direction === 'south' || polygon.direction === 'north') {
|
||||||
polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2)
|
const targetCoord =
|
||||||
const realLineCoord =
|
polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2)
|
||||||
polygon.direction === 'south' ? Math.max(isRealEavesLine.y1, isRealEavesLine.y2) : Math.min(isRealEavesLine.y1, isRealEavesLine.y2)
|
const realLineCoord = polygon.direction === 'south' ? Math.max(line.y1, line.y2) : Math.min(line.y1, line.y2)
|
||||||
|
|
||||||
if (targetCoord !== realLineCoord) {
|
if (targetCoord !== realLineCoord) {
|
||||||
isRealEavesLine.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
||||||
}
|
line.stroke = 'rgb(9, 9, 9)'
|
||||||
} else if (polygon.direction === 'east' || polygon.direction === 'west') {
|
}
|
||||||
const targetCoord = polygon.direction === 'east' ? Math.max(maxLineCoord.x1, maxLineCoord.x2) : Math.min(maxLineCoord.x1, maxLineCoord.x2)
|
} else if (polygon.direction === 'east' || polygon.direction === 'west') {
|
||||||
const realLineCoord =
|
const targetCoord =
|
||||||
polygon.direction === 'east' ? Math.max(isRealEavesLine.x1, isRealEavesLine.x2) : Math.min(isRealEavesLine.x1, isRealEavesLine.x2)
|
polygon.direction === 'east' ? Math.max(maxLineCoord.x1, maxLineCoord.x2) : Math.min(maxLineCoord.x1, maxLineCoord.x2)
|
||||||
|
const realLineCoord = polygon.direction === 'east' ? Math.max(line.x1, line.x2) : Math.min(line.x1, line.x2)
|
||||||
|
|
||||||
if (targetCoord !== realLineCoord) {
|
if (targetCoord !== realLineCoord) {
|
||||||
isRealEavesLine.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
|
||||||
|
line.stroke = 'rgb(9, 9, 9)'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findCentroid(points) {
|
||||||
|
let sumX = 0,
|
||||||
|
sumY = 0
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
sumX += points[i].x
|
||||||
|
sumY += points[i].y
|
||||||
|
}
|
||||||
|
return { x: sumX / points.length, y: sumY / points.length }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 도형의 포인트를 왼쪽부터 반시계 방향으로 정렬하는 함수
|
||||||
|
/**
|
||||||
|
* 다각형의 점들을 시계 반대 방향으로 정렬하는 함수
|
||||||
|
* @param {Array} points - {x, y} 좌표 객체 배열
|
||||||
|
* @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
|
||||||
|
* @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
|
||||||
|
*/
|
||||||
|
function orderPointsCounterClockwise(points, startPoint = null) {
|
||||||
|
if (points.length <= 3) {
|
||||||
|
return points // 점이 3개 이하면 이미 다각형의 모든 점이므로 그대로 반환
|
||||||
|
}
|
||||||
|
|
||||||
|
// 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
|
||||||
|
let start = startPoint
|
||||||
|
if (!start) {
|
||||||
|
start = points[0]
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
if (points[i].x < start.x || (points[i].x === start.x && points[i].y < start.y)) {
|
||||||
|
start = points[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 다각형의 중심점 계산
|
||||||
|
let centerX = 0,
|
||||||
|
centerY = 0
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
centerX += points[i].x
|
||||||
|
centerY += points[i].y
|
||||||
|
}
|
||||||
|
centerX /= points.length
|
||||||
|
centerY /= points.length
|
||||||
|
|
||||||
|
// 시작점에서 시계 반대 방향으로 각도 계산
|
||||||
|
let angles = []
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
// 시작점은 제외
|
||||||
|
if (points[i] === start) continue
|
||||||
|
|
||||||
|
// 시작점을 기준으로 각 점의 각도 계산
|
||||||
|
let angle = Math.atan2(points[i].y - start.y, points[i].x - start.x)
|
||||||
|
|
||||||
|
// 각도가 음수면 2π를 더해 0~2π 범위로 변환
|
||||||
|
if (angle < 0) angle += 2 * Math.PI
|
||||||
|
|
||||||
|
angles.push({
|
||||||
|
point: points[i],
|
||||||
|
angle: angle,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 각도에 따라 정렬 (시계 반대 방향)
|
||||||
|
angles.sort((a, b) => a.angle - b.angle)
|
||||||
|
|
||||||
|
// 정렬된 배열 생성 (시작점을 첫 번째로)
|
||||||
|
let orderedPoints = [start]
|
||||||
|
for (let i = 0; i < angles.length; i++) {
|
||||||
|
orderedPoints.push(angles[i].point)
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 점에서 시작하여 시계 반대 방향으로 다음 점을 찾는 함수
|
||||||
|
* @param {Object} currentPoint - 현재 점 {x, y}
|
||||||
|
* @param {Array} points - 모든 점들의 배열
|
||||||
|
* @param {Array} visited - 방문한 점들의 인덱스 배열
|
||||||
|
* @param {Object} prevVector - 이전 벡터 방향 (첫 호출에서는 null)
|
||||||
|
* @returns {Object} 다음 점의 인덱스와 객체
|
||||||
|
*/
|
||||||
|
function findNextCounterClockwisePoint(currentPoint, points, visited, prevVector = null) {
|
||||||
|
let minAngle = Infinity
|
||||||
|
let nextIndex = -1
|
||||||
|
|
||||||
|
// 이전 벡터가 없으면 (첫 점인 경우) 아래쪽을 향하는 벡터 사용
|
||||||
|
if (!prevVector) {
|
||||||
|
prevVector = { x: 0, y: -1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
// 이미 방문했거나 현재 점이면 건너뜀
|
||||||
|
if (visited.includes(i) || (points[i].x === currentPoint.x && points[i].y === currentPoint.y)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 현재 점에서 다음 후보 점으로의 벡터
|
||||||
|
let vector = {
|
||||||
|
x: points[i].x - currentPoint.x,
|
||||||
|
y: points[i].y - currentPoint.y,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 벡터의 크기
|
||||||
|
let magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y)
|
||||||
|
|
||||||
|
// 단위 벡터로 정규화
|
||||||
|
vector.x /= magnitude
|
||||||
|
vector.y /= magnitude
|
||||||
|
|
||||||
|
// 이전 벡터와 현재 벡터 사이의 각도 계산 (내적 사용)
|
||||||
|
let dotProduct = prevVector.x * vector.x + prevVector.y * vector.y
|
||||||
|
let crossProduct = prevVector.x * vector.y - prevVector.y * vector.x
|
||||||
|
|
||||||
|
// 각도 계산 (atan2 사용)
|
||||||
|
let angle = Math.atan2(crossProduct, dotProduct)
|
||||||
|
|
||||||
|
// 시계 반대 방향으로 가장 작은 각도를 가진 점 찾기
|
||||||
|
// 각도가 음수면 2π를 더해 0~2π 범위로 변환
|
||||||
|
if (angle < 0) angle += 2 * Math.PI
|
||||||
|
|
||||||
|
if (angle < minAngle) {
|
||||||
|
minAngle = angle
|
||||||
|
nextIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextIndex !== -1 ? { index: nextIndex, point: points[nextIndex] } : null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 다각형의 점들을 시계 반대 방향으로 추적하는 함수
|
||||||
|
* @param {Array} points - {x, y} 좌표 객체 배열
|
||||||
|
* @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
|
||||||
|
* @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
|
||||||
|
*/
|
||||||
|
function tracePolygonCounterClockwise(points, startPoint = null) {
|
||||||
|
if (points.length <= 3) {
|
||||||
|
return orderPointsCounterClockwise(points, startPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
|
||||||
|
let startIndex = 0
|
||||||
|
if (!startPoint) {
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
if (points[i].x < points[startIndex].x || (points[i].x === points[startIndex].x && points[i].y < points[startIndex].y)) {
|
||||||
|
startIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startPoint = points[startIndex]
|
||||||
|
} else {
|
||||||
|
// 시작점이 제공된 경우 해당 점의 인덱스 찾기
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
if (points[i].x === startPoint.x && points[i].y === startPoint.y) {
|
||||||
|
startIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 결과 배열 초기화
|
||||||
|
let orderedPoints = [startPoint]
|
||||||
|
let visited = [startIndex]
|
||||||
|
|
||||||
|
let currentPoint = startPoint
|
||||||
|
let prevVector = null
|
||||||
|
|
||||||
|
// 모든 점을 방문할 때까지 반복
|
||||||
|
while (visited.length < points.length) {
|
||||||
|
let next = findNextCounterClockwisePoint(currentPoint, points, visited, prevVector)
|
||||||
|
|
||||||
|
if (!next) break // 더 이상 찾을 점이 없으면 종료
|
||||||
|
|
||||||
|
orderedPoints.push(next.point)
|
||||||
|
visited.push(next.index)
|
||||||
|
|
||||||
|
// 이전 벡터 업데이트 (현재 점에서 다음 점으로의 벡터)
|
||||||
|
prevVector = {
|
||||||
|
x: next.point.x - currentPoint.x,
|
||||||
|
y: next.point.y - currentPoint.y,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 벡터 정규화
|
||||||
|
let magnitude = Math.sqrt(prevVector.x * prevVector.x + prevVector.y * prevVector.y)
|
||||||
|
prevVector.x /= magnitude
|
||||||
|
prevVector.y /= magnitude
|
||||||
|
|
||||||
|
currentPoint = next.point
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedPoints
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
applySurfaceShape,
|
applySurfaceShape,
|
||||||
deleteAllSurfacesAndObjects,
|
deleteAllSurfacesAndObjects,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user