diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index b4bff818..5d1b997f 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -46,6 +46,7 @@ export default function Roof2() { zoomOut, zoom, togglePolygonLine, + handleOuterlinesTest2, } = useMode() useEffect(() => { @@ -135,12 +136,14 @@ export default function Roof2() { if (canvas) { const polygon = new QPolygon( [ - { x: 100, y: 100 }, - { x: 800, y: 100 }, - { x: 800, y: 800 }, - { x: 500, y: 800 }, - { x: 500, y: 400 }, - { x: 100, y: 400 }, + { x: 198.5, y: 735 }, + { x: 698.5, y: 735 }, + { x: 698.5, y: 585 }, + { x: 448.5, y: 585 }, + { x: 448.5, y: 435 }, + { x: 698.5, y: 435 }, + { x: 698.5, y: 235 }, + { x: 198.5, y: 235 }, ], { fill: 'transparent', @@ -155,10 +158,10 @@ export default function Roof2() { canvas?.add(polygon) - addBackgroundInPolygon(polygon) + handleOuterlinesTest2(polygon) - const lines = togglePolygonLine(polygon) - togglePolygonLine(lines[0]) + // const lines = togglePolygonLine(polygon) + // togglePolygonLine(lines[0]) } } diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 43e39fd2..d15c93dd 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -5,6 +5,7 @@ import { getDirectionByPoint, getRoofHeight, getRoofHypotenuse, + sortedPoints, } from '@/util/canvas-util' import { QLine } from '@/components/fabric/QLine' @@ -26,15 +27,14 @@ export default class QPolygon extends fabric.Group { throw new Error('Canvas is required.') } - const polygon = new fabric.Polygon(points, options) + const sortPoints = sortedPoints(points) + const polygon = new fabric.Polygon(sortPoints, options) super([polygon], {}) - this.fontSize = options.fontSize - this.points = points + this.points = sortPoints this.polygon = polygon this.name = options.name - this.#init() this.#addEvent() this.#initLines() diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 67e40b28..c8db2ac6 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -723,7 +723,7 @@ export function useMode() { */ const handleOuterlinesTest = (offset = 71) => { var offsetPoints = [] - + const sortedIndex = getStartIndex(historyLines.current) let tmpArraySorted = rearrangeArray(historyLines.current, sortedIndex) @@ -789,6 +789,76 @@ export function useMode() { roof.drawHelpLine() } + /** + *벽 지붕 외곽선 생성 polygon을 입력받아 만들기 + */ + const handleOuterlinesTest2 = (polygon, offset = 71) => { + const offsetPoints = [] + const sortedIndex = getStartIndex(polygon.lines) + let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex) + + if (tmpArraySorted[0].direction === 'right') { + //시계방향 + tmpArraySorted = tmpArraySorted.reverse() //그럼 배열을 거꾸로 만들어서 무조건 반시계방향으로 배열 보정 + } + + setSortedArray(tmpArraySorted) //recoil에 넣음 + + const points = tmpArraySorted.map((line) => ({ + x: line.x1, + y: line.y1, + })) + + for (let i = 0; i < points.length; i++) { + const prev = points[(i - 1 + points.length) % points.length] + const current = points[i] + const next = points[(i + 1) % points.length] + + // 두 벡터 계산 (prev -> current, current -> next) + const vector1 = { x: current.x - prev.x, y: current.y - prev.y } + const vector2 = { x: next.x - current.x, y: next.y - current.y } + + // 벡터의 길이 계산 + const length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) + const length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y) + + // 벡터를 단위 벡터로 정규화 + const unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 } + const unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 } + + // 법선 벡터 계산 (왼쪽 방향) + const normal1 = { x: -unitVector1.y, y: unitVector1.x } + const normal2 = { x: -unitVector2.y, y: unitVector2.x } + + // 법선 벡터 평균 계산 + const averageNormal = { + x: (normal1.x + normal2.x) / 2, + y: (normal1.y + normal2.y) / 2, + } + + // 평균 법선 벡터를 단위 벡터로 정규화 + const lengthNormal = Math.sqrt( + averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y, + ) + const unitNormal = { + x: averageNormal.x / lengthNormal, + y: averageNormal.y / lengthNormal, + } + + // 오프셋 적용 + const offsetPoint = { + x1: current.x + unitNormal.x * offset, + y1: current.y + unitNormal.y * offset, + } + + offsetPoints.push(offsetPoint) + } + + const roof = makePolygon(offsetPoints) + setRoof(roof) + // roof.drawHelpLine() + } + const togglePolygonLine = (obj) => { const rtnLines = [] if (obj.type === 'QPolygon') { @@ -834,5 +904,6 @@ export function useMode() { zoomOut, zoom, togglePolygonLine, + handleOuterlinesTest2, } } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 51634a00..84563f34 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -155,6 +155,30 @@ export const getStartIndex = (lines) => { return smallestIndex } +/** + * points 배열에서 시작점을 찾는 함수 + * @param points + * @returns {number} + */ +export const getStartIndexPoint = (points) => { + let smallestIndex = 0 + let smallestX1 = points[0].x + let smallestY1 = points[0].y + + for (let i = 1; i < points.length; i++) { + if ( + points[i].x < smallestX1 || + (points[i].x === smallestX1 && points[i].y < smallestY1) + ) { + smallestIndex = i + smallestX1 = points[i].x + smallestY1 = points[i].y + } + } + + return smallestIndex +} + /** * 이 함수는 두 개의 매개변수를 받습니다: array와 index. * array는 재배열할 대상 배열입니다. @@ -309,3 +333,69 @@ export function calculateIntersection(line1, line2) { return { x: intersectX, y: intersectY } } + +/** + * points배열을 입력받아 반시계방향으로 정렬된 points를 반환합니다. + * @param points + */ +export const sortedPoints = (points) => { + const copyPoints = [...points] + //points를 x,y좌표를 기준으로 정렬합니다. + + copyPoints.sort((a, b) => { + if (a.x === b.x) { + return a.y - b.y + } + return a.x - b.x + }) + + // 이때 copyPoints를 순회하며 최초엔 x값을 비교하여 같은 점을 찾는다. 이때 이 점이 2번째 점이 된다. + // 그 다음점은 2번째 점과 y값이 같은 점이 된다. + // 또 그다음 점은 3번째 점과 x값이 같은 점이 된다. + // 이를 반복하여 copyPoints를 재배열한다. + const resultPoints = [copyPoints[0]] + let index = 1 + let currentPoint = { ...copyPoints[0] } + copyPoints.splice(0, 1) + + while (index < points.length) { + if (index === points.length - 1) { + resultPoints.push(copyPoints[0]) + index++ + break + } else if (index % 2 === 0) { + // 짝수번째는 y값이 같은 점을 찾는다. + for (let i = 0; i < copyPoints.length; i++) { + // y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다. + const temp = copyPoints.filter((point) => point.y === currentPoint.y) + // temp중 x값이 가장 큰 값 + const max = temp.reduce((prev, current) => + prev.x >= current.x ? prev : current, + ) + resultPoints.push(max) + currentPoint = max + copyPoints.splice(copyPoints.indexOf(max), 1) + index++ + break + } + } else { + // 홀수번째는 x값이 같은 점을 찾는다. + for (let i = 0; i < copyPoints.length; i++) { + // x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다. + const temp = copyPoints.filter((point) => point.x === currentPoint.x) + // temp중 y값이 가장 큰 값 + const max = temp.reduce((prev, current) => + prev.y >= current.y ? prev : current, + ) + + resultPoints.push(max) + currentPoint = max + copyPoints.splice(copyPoints.indexOf(max), 1) + index++ + break + } + } + } + + return resultPoints +}