From dcff818c5609d120bafd6627b4f533c31ff43abc Mon Sep 17 00:00:00 2001 From: yjnoh Date: Fri, 9 Aug 2024 14:06:12 +0900 Subject: [PATCH] =?UTF-8?q?A,B=20=ED=85=9C=ED=94=8C=EB=A6=BF=20=EB=B0=A9?= =?UTF-8?q?=EC=9C=84=20=EB=8C=80=EC=B6=A9=20=EC=9E=91=EC=97=85=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 20 +- src/hooks/useMode.js | 742 ++++++++++++++++++++++++++++++++++++--- src/store/canvasAtom.js | 6 + 3 files changed, 718 insertions(+), 50 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 6c5f1914..51c2e635 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -5,7 +5,7 @@ import { Button } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasSizeState, fontSizeState, roofMaterialState, sortedPolygonArray, templateTypeState } from '@/store/canvasAtom' +import { canvasSizeState, fontSizeState, roofMaterialState, sortedPolygonArray, templateTypeState, compassState } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection } from '@/util/canvas-util' @@ -35,6 +35,8 @@ export default function Roof2() { const [templateType, setTemplateType] = useRecoilState(templateTypeState) + const [compass, setCompass] = useRecoilState(compassState) + const { mode, setMode, @@ -492,6 +494,10 @@ export default function Roof2() { }) } + const setCompassState = (degree) => { + setCompass(degree) + } + return ( <> {canvas && ( @@ -521,6 +527,18 @@ export default function Roof2() { + + + + diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 05c27007..3d48e63e 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util' -import { useRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import { canvasSizeState, @@ -12,6 +12,7 @@ import { sortedPolygonArray, templateTypeState, wallState, + compassState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' @@ -60,6 +61,7 @@ export function useMode() { const [drewRoofCells, setDrewRoofCells] = useRecoilState(drewRoofCellsState) const [roofStyle, setRoofStyle] = useState(1) //기본 지붕 패턴 const [templateCenterLine, setTemplateCenterLine] = useState([]) + const compass = useRecoilValue(compassState) useEffect(() => { // 이벤트 리스너 추가 @@ -717,6 +719,8 @@ export function useMode() { points.current = [] historyPoints.current = [] historyLines.current = [] + + setSelectedCellRoofArray([]) //셀 그린거 삭제 } const zoomIn = () => { @@ -2545,7 +2549,7 @@ export function useMode() { const line = new QLine([start.x, start.y, end.x, end.y], { stroke: 'blue', - strokeWidth: 1, + strokeWidth: 2, property: 'normal', fontSize: 14, idx: i, @@ -2620,6 +2624,7 @@ export function useMode() { vertCenterLine = reSortQlineArray(vertCenterLine) lines = reSortQlineArray(lines) + setTemplateCenterLine(vertCenterLine) //해당라인에서 만나는점을 계산 vertCenterLine.forEach((vertLine) => { @@ -2777,7 +2782,6 @@ export function useMode() { } else { // 오목한 부분이 세로선일때 아래ㄷ, 위ㄷ //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 - lines.forEach((line, index) => { if (!(index % 2 === 0)) { line.line.set('stroke', 'skyblue') @@ -2824,6 +2828,7 @@ export function useMode() { vertCenterLine = reSortQlineArray(vertCenterLine) lines = reSortQlineArray(lines) + setTemplateCenterLine(vertCenterLine) //해당라인에서 만나는점을 계산 vertCenterLine.forEach((vertLine) => { @@ -2989,6 +2994,8 @@ export function useMode() { } else if (polygon.lines.length === 6) { console.log('6각형') handleTemplateB(params) + } else if (polygon.lines.length === 8) { + handleOuterLineTemplateB8Points(polygon) } setTemplateType(3) @@ -3434,6 +3441,578 @@ export function useMode() { canvas?.renderAll() } + const handleOuterLineTemplateB8Points = (polygon, offsetInputX = 20, offsetInputY = 50) => { + let offsetPoints = [] + + const originalMax = 71 + const transformedMax = 100 + + let lines = [] //내각라인 + let outLines = [] //아웃라인 + let halfLength = 0 //선길이 + let offsetX + let offsetY + + const dashedCenterLineOpt = { + stroke: 'black', + strokeWidth: 1, + property: 'centerLine', + strokeDashArray: [8, 4], + fontSize: 14, + } + + const centerLineOpt = { + stroke: 'blue', + strokeWidth: 2, + property: 'bigHoriCenter', + fontSize: 14, + } + + // 폴리곤의 각 변을 선으로 생성 + for (let i = 0; i < polygon.points.length; i++) { + const start = polygon.points[i] + const end = polygon.points[(i + 1) % polygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로 + + const line = new QLine([start.x, start.y, end.x, end.y], { + stroke: '#A0D468', + strokeWidth: 2, + property: 'normal', + fontSize: 14, + }) + + // 선을 배열에 추가 + lines.push(line) + canvas.add(line) + } + + offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX + + const sortedIndex = getStartIndex(polygon.lines) + let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex) + setSortedArray(tmpArraySorted) //recoil에 넣음 + + const points = tmpArraySorted.map((line) => ({ + x: line.x1, + y: line.y1, + })) + + //좌표 재정렬 + function reSortQlineArray(array) { + let tmpArray = [] + let minX, minY, maxX, maxY + let tmp + array.forEach((arr, index) => { + tmp = arr + if (arr.x2 < arr.x1 || arr.y2 < arr.y1) { + minX = arr.x2 + minY = arr.y2 + maxX = arr.x1 + maxY = arr.y1 + tmp['x1'] = minX + tmp['y1'] = minY + tmp['x2'] = maxX + tmp['y2'] = maxY + tmp.line['x1'] = minX + tmp.line['y1'] = minY + tmp.line['x2'] = maxX + tmp.line['y2'] = maxY + } + tmpArray.push(tmp) + }) + + return tmpArray + } + + // 오목한 부분 인덱스 찾기 + const concaveIndicesObj = findConcavePointIndices(points) //오목한 부분을 제외한 인덱스 + let concavePointIndices = concaveIndicesObj.concavePointIndices + + const concaveLine = { + index: concavePointIndices[0], + line: lines[concavePointIndices[0]], + } + + for (let i = 0; i < points.length; i++) { + let prev = points[(i - 1 + points.length) % points.length] + let current = points[i] + let next = points[(i + 1) % points.length] + + // 두 벡터 계산 (prev -> current, current -> next) + let vector1 = { x: current.x - prev.x, y: current.y - prev.y } + let vector2 = { x: next.x - current.x, y: next.y - current.y } + + // 벡터의 길이 계산 + let length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) + let length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y) + + // 벡터를 단위 벡터로 정규화 + let unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 } + let unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 } + + // 법선 벡터 계산 (왼쪽 방향) + let normal1 = { x: -unitVector1.y, y: unitVector1.x } + let normal2 = { x: -unitVector2.y, y: unitVector2.x } + + // 법선 벡터 평균 계산 + let averageNormal = { + x: (normal1.x + normal2.x) / 2, + y: (normal1.y + normal2.y) / 2, + } + + // 평균 법선 벡터를 단위 벡터로 정규화 + let lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y) + let unitNormal = { + x: averageNormal.x / lengthNormal, + y: averageNormal.y / lengthNormal, + } + + offsetX = (offsetInputX / transformedMax) * originalMax * 2 + offsetY = (offsetInputY / transformedMax) * originalMax * 2 + + // 오프셋 적용 + let offsetPoint = { + x1: current.x + unitNormal.x * offsetX, + y1: current.y + unitNormal.y * offsetY, + } + + offsetPoints.push(offsetPoint) + } + + const outlinePolygon = makePolygon(offsetPoints, false) + outlinePolygon.setViewLengthText(false) + + // 아웃라인 폴리곤의 각 변을 선으로 생성 + for (let i = 0; i < outlinePolygon.points.length; i++) { + const start = outlinePolygon.points[i] + const end = outlinePolygon.points[(i + 1) % outlinePolygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로 + + const line = new QLine([start.x, start.y, end.x, end.y], { + stroke: 'blue', + strokeWidth: 2, + property: 'normal', + fontSize: 14, + idx: i, + }) + + // 선을 배열에 추가 + outLines.push(line) + canvas.add(line) + } + + canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제 + + let parallelLinesIdx = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기 + if (parallelLinesIdx >= outLines.length) { + parallelLinesIdx = parallelLinesIdx - outLines.length + } + + let vertCenterLine = [] + let halfHoriCenterLinePoint = [] //카라바선의 2분할의 1번 배열 + let horiCenterLine = [] + let shorVertCenterLine = [] + + let edgeIndexArray = [] + + if (concavePointIndices[0] % 2 === 0) { + //concave가 짝수면 좌우로 그려진 ㄷ자 + //케라바 색을 바꾼다 + lines.forEach((line, index) => { + if (index % 2 === 0) { + line.line.set('stroke', 'skyblue') + if (concavePointIndices[0] !== index) { + edgeIndexArray.push(index) + } + } + }) + + outLines = reSortQlineArray(outLines) + edgeIndexArray.forEach((idx, index) => { + //가로라인이 케라바 라인임 + if (concavePointIndices[0] !== idx) { + //오목이가 아니면 반으로 갈라서 계산 + + //카라바 선의 2분할 치수를 그림 + let halfLength = outLines[idx].length / 2 + let centerLine1 = new QLine([outLines[idx].x1, outLines[idx].y1, outLines[idx].x1, outLines[idx].y1 + halfLength], centerLineOpt) + canvas.add(centerLine1) + + let centerLine2 = new QLine([outLines[idx].x1, centerLine1.y2, outLines[idx].x2, centerLine1.y2 + halfLength], centerLineOpt) + canvas.add(centerLine2) + canvas.remove(outLines[idx]) //기존 라인 삭제 + + halfHoriCenterLinePoint.push({ + index: idx, + x1: centerLine1.x1, + y1: centerLine1.y1, + x2: centerLine1.x2, + y2: centerLine1.y2, + }) //각 카라바 라인의 1번이 마지막점을 잡아서 센터선으로 설정 + } + }) + + // //각 센터 라인을 그림 + halfHoriCenterLinePoint.forEach((centerPoint) => { + let tmpX2 = parallelLinesIdx !== centerPoint.index ? concaveLine.line.x2 : outLines[concavePointIndices[0]].x1 //평행선에서 내려오는 선은 아웃라인에 닿아야한다 + + let line = new QLine([centerPoint.x2, centerPoint.y2, tmpX2, centerPoint.y2], centerLineOpt) + canvas.add(line) + + line['arrayIndex'] = centerPoint.index //커스텀으로 기존 index를 넣어줌 + vertCenterLine.push(line) + }) + + vertCenterLine = reSortQlineArray(vertCenterLine) + lines = reSortQlineArray(lines) + setTemplateCenterLine(vertCenterLine) + + //해당라인에서 만나는점을 계산 + vertCenterLine.forEach((vertLine) => { + if (parallelLinesIdx !== vertLine.arrayIndex) { + //평행선을 제외한 애들만 네모를 연결 + let nearLine + let nearOutline + if (vertLine.arrayIndex > concaveLine.index) { + //센터에 인덱스가 오목점 보다 크면 다음 작으면 앞에꺼 + nearLine = lines[concaveLine.index + 1] + nearOutline = outLines[concaveLine.index + 1] + } else { + nearLine = lines[concaveLine.index - 1] + nearOutline = outLines[concaveLine.index - 1] + } + + let nearLineY = nearLine.y1 + if (parallelLinesIdx < concaveLine.index) { + //오목점 위치가 평행선보다 크면 위쪽으로 오목 + nearLineY = nearLine.y2 + } + + //기존에 있는 라인에서 연장해서 새로 그림 + let centerExtendHoriLine = new QLine([vertLine.x1, nearOutline.line.y1, vertLine.x2, nearOutline.line.y2], centerLineOpt) + canvas.add(centerExtendHoriLine) + canvas.remove(nearOutline) + outLines.splice(nearOutline.idx, 1, centerExtendHoriLine) //아웃라인에 데이터를 다시 넣는다 + + //가로형에선 기본으로 ㄷ자 형태로 한다 + let centerExtendLine = new QLine([vertLine.line.x1, vertLine.line.y1, centerExtendHoriLine.x1, centerExtendHoriLine.y1], centerLineOpt) + + //오목이가 배열에 반보다 작으면 역 ㄷ자 여서 변경 + if (concavePointIndices[0] < outLines.length / 2) { + centerExtendLine = new QLine([vertLine.line.x2, vertLine.line.y2, centerExtendHoriLine.x2, centerExtendHoriLine.y2], centerLineOpt) + } + + canvas.add(centerExtendLine) //새로그리고 + + let betweenCenterLine = (vertLine.line.x1 + vertLine.line.x2) / 2 + let centerDashLine = new QLine([betweenCenterLine, centerExtendLine.y1, betweenCenterLine, centerExtendLine.y2], dashedCenterLineOpt) + + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + shorVertCenterLine.push(vertLine) //마지막에 가운데 선을 긋기 위해 담음 + } else { + let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) //평행선 + + let dashCenterExtendLineLength = longDashLine.y2 - longDashLine.y1 //y반개 길이 + let betweenCenterLine = (vertLine.line.x1 + vertLine.line.x2) / 2 //y의 길이 + let totalLength = ((longDashLine.y2 - longDashLine.y1) * 2) / dashCenterExtendLineLength //2개로 나눔 + + //반 쪼개서 그린다 + for (let i = 0; i < totalLength; i++) { + //2번에 나눠서 + let startY = i === 0 ? longDashLine.y1 : longDashLine.y1 + dashCenterExtendLineLength //시작 하는 y의 좌표 + //x값은 고정이임 //TODO: 지붕 각도 계산법에 의해 재계산해야함 + let centerDashLine = new QLine([betweenCenterLine, startY, betweenCenterLine, startY + dashCenterExtendLineLength], dashedCenterLineOpt) + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + } + } + }) + + //마지막에 오목한 외곽선을 연장한다 + const tmpLastOutLine = outLines[concavePointIndices[0]] + const lastOutLine = new QLine([tmpLastOutLine.x1, shorVertCenterLine[0].y1, tmpLastOutLine.x1, shorVertCenterLine[1].y1], centerLineOpt) + canvas.add(lastOutLine) + canvas.remove(tmpLastOutLine) + + //폴리곤 패턴을 그리기 위해 작성 + let tmpVertCenterLine = outLines.filter((x, index) => index % 2 !== 0) //세로만 찾음 + tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) + tmpVertCenterLine.sort((a, b) => a.y1 - b.y1) + tmpVertCenterLine.push(lastOutLine) + + let roofPatternPolygonArray = [] + let tmpArray = [] + let tmpBigArray = [] + + const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 + + for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { + //-1인건 마지막은 오목한 선이라 돌 필요 없음 + //라인 하나에 두점씩 나온다 + let firstPointObj = {} + let secondPointObj = {} + + let x1 = tmpVertCenterLine[i].x1 + let y1 = tmpVertCenterLine[i].y1 + let x2 = tmpVertCenterLine[i].x2 + let y2 = tmpVertCenterLine[i].y2 + + if (i === 2 || i === 4) { + //작은 네모들 + tmpArray = [] + const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 + const nextLine = tmpVertCenterLine[i + 1] + + //내 앞뒤 라인 + const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 + const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 + const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 + const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 + + firstPointObj = { x: tmpX1, y: tmpY1 } + secondPointObj = { x: tmpX2, y: tmpY2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + + //현재 내 선 + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + roofPatternPolygonArray.push(tmpArray) + } else { + //큰 육각 + if (i === 1 || i === 5) { + // 큰 폴리곤은 가운데 선으로 되야됨 + if (outLines.length / 2 > concavePointIndices[0]) { + x2 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y2 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 + } else { + //오목이가 배열 전체보다 크면 오른쪽 오목이 + x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y1 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 + } + } + + if (i === 5) { + //5번일때는 앞에 3번에 선이 필요하다 + let prevX1 = tmpVertCenterLine[i - 2].x1 + let prevY1 = tmpVertCenterLine[i - 2].y1 + let prevX2 = tmpVertCenterLine[i - 2].x2 + let prevY2 = tmpVertCenterLine[i - 2].y2 + firstPointObj = { x: prevX1, y: prevY1 } + secondPointObj = { x: prevX2, y: prevY2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + } + + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + + if (i === 3 || i === 6) { + roofPatternPolygonArray.push(tmpBigArray) + tmpBigArray = [] + } + } + } + + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) + } else { + // 오목한 부분이 세로선일때 아래ㄷ, 위ㄷ + //라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정 + lines.forEach((line, index) => { + if (!(index % 2 === 0)) { + line.line.set('stroke', 'skyblue') + } + }) + outLines = reSortQlineArray(outLines) + outLines.forEach((outline, index) => { + if (!(index % 2 === 0)) { + //세로라인이 케라바 라인임 + + if (concavePointIndices[0] !== index) { + //오목이가 아니면 반으로 갈라서 계산 + + //카라바 선의 2분할 치수를 그림 + let halfLength = outline.length / 2 + let centerLine1 = new QLine([outline.x1, outline.y1, outline.x1 + halfLength, outline.y1], centerLineOpt) + canvas.add(centerLine1) + + let centerLine2 = new QLine([centerLine1.x2, outline.y1, centerLine1.x2 + halfLength, outline.y1], centerLineOpt) + canvas.add(centerLine2) + canvas.remove(outline) //기존 라인 삭제 + + halfHoriCenterLinePoint.push({ + index: index, + x1: centerLine1.x1, + y1: centerLine1.y1, + x2: centerLine1.x2, + y2: centerLine1.y2, + }) //각 카라바 라인의 1번이 마지막점을 잡아서 센터선으로 설정 + } + } + }) + + //각 센터 라인을 그림 + halfHoriCenterLinePoint.forEach((centerPoint) => { + let tmpY2 = parallelLinesIdx !== centerPoint.index ? concaveLine.line.y1 : outLines[concavePointIndices[0]].y2 //평행선에서 내려오는 선은 아웃라인에 닿아야한다 + + let line = new QLine([centerPoint.x2, centerPoint.y1, centerPoint.x2, tmpY2], centerLineOpt) + canvas.add(line) + + line['arrayIndex'] = centerPoint.index //커스텀으로 기존 index를 넣어줌 + vertCenterLine.push(line) + }) + + vertCenterLine = reSortQlineArray(vertCenterLine) + lines = reSortQlineArray(lines) + setTemplateCenterLine(vertCenterLine) + + //해당라인에서 만나는점을 계산 + vertCenterLine.forEach((vertLine) => { + if (parallelLinesIdx !== vertLine.arrayIndex) { + //평행선을 제외한 애들만 네모를 연결 + let nearLine + let nearOutline + if (vertLine.arrayIndex > concaveLine.index) { + //센터에 인덱스가 오목점 보다 크면 다음 작으면 앞에꺼 + nearLine = lines[concaveLine.index + 1] + nearOutline = outLines[concaveLine.index + 1] + } else { + nearLine = lines[concaveLine.index - 1] + nearOutline = outLines[concaveLine.index - 1] + } + + let nearLineY = nearLine.y1 + if (parallelLinesIdx < concaveLine.index) { + //오목점 위치가 평행선보다 크면 위쪽으로 오목 + nearLineY = nearLine.y2 + } + + let centerExtendLine = new QLine([vertLine.line.x1, nearLineY, nearOutline.x1, nearLineY], centerLineOpt) + canvas.add(centerExtendLine) //새로그리고 + + //기존에 있는 라인에서 연장해서 새로 그림 + let centerExtendHoriLine = new QLine([nearOutline.line.x1, vertLine.y1, nearOutline.line.x2, vertLine.line.y2], centerLineOpt) + canvas.add(centerExtendHoriLine) + canvas.remove(nearOutline) + outLines.splice(nearOutline.idx, 1, centerExtendHoriLine) //아웃라인에 데이터를 다시 넣는다 + + let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + let centerDashLine = new QLine([vertLine.line.x1, betweenCenterLine, nearOutline.x1, betweenCenterLine], dashedCenterLineOpt) + + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + shorVertCenterLine.push(vertLine) //마지막에 가운데 선을 긋기 위해 담음 + } else { + let longDashLine = halfHoriCenterLinePoint.find((obj) => obj.index === parallelLinesIdx) + + let dashCenterExtendLineLength = longDashLine.x2 - longDashLine.x1 + let betweenCenterLine = (vertLine.line.y1 + vertLine.line.y2) / 2 + let totalLength = ((longDashLine.x2 - longDashLine.x1) * 2) / dashCenterExtendLineLength + + //반 쪼개서 그린다 + for (let i = 0; i < totalLength; i++) { + let startX = i === 0 ? longDashLine.x1 : longDashLine.x1 + dashCenterExtendLineLength + let centerDashLine = new QLine([startX, betweenCenterLine, startX + dashCenterExtendLineLength, betweenCenterLine], dashedCenterLineOpt) + canvas.add(centerDashLine) + horiCenterLine.push(centerDashLine) + } + } + }) + + //마지막에 오목한 외곽선을 연장한다 + const tmpLastOutLine = outLines[concavePointIndices[0]] + const lastOutLine = new QLine([shorVertCenterLine[0].x1, tmpLastOutLine.y1, shorVertCenterLine[1].x1, tmpLastOutLine.y2], centerLineOpt) + canvas.add(lastOutLine) + canvas.remove(tmpLastOutLine) + + let tmpVertCenterLine = outLines.filter((x, index) => index % 2 === 0) //세로만 찾음 + tmpVertCenterLine = tmpVertCenterLine.concat(vertCenterLine) + tmpVertCenterLine.sort((a, b) => a.x1 - b.x1) + tmpVertCenterLine.push(lastOutLine) + + let roofPatternPolygonArray = [] + let tmpArray = [] + let tmpBigArray = [] + + const lastCenterLine = tmpVertCenterLine[tmpVertCenterLine.length - 1] //마지막 센터라인을 정의 + + for (let i = 0; i < tmpVertCenterLine.length - 1; i++) { + //-1인건 마지막은 오목한 선이라 돌 필요 없음 + //라인 하나에 두점씩 나온다 + let firstPointObj = {} + let secondPointObj = {} + + let x1 = tmpVertCenterLine[i].x1 + let y1 = tmpVertCenterLine[i].y1 + let x2 = tmpVertCenterLine[i].x2 + let y2 = tmpVertCenterLine[i].y2 + + if (i === 2 || i === 4) { + tmpArray = [] + const prevLine = tmpVertCenterLine[i - 1] //뒤에서 앞라인을 찾는다 + const nextLine = tmpVertCenterLine[i + 1] + + //내 앞뒤 라인 + const tmpX1 = i === 2 ? prevLine.x1 : nextLine.x1 + const tmpY1 = i === 2 ? prevLine.y1 : nextLine.y1 + const tmpX2 = i === 2 ? prevLine.x2 : nextLine.x2 + const tmpY2 = i === 2 ? prevLine.y2 : nextLine.y2 + + firstPointObj = { x: tmpX1, y: tmpY1 } + secondPointObj = { x: tmpX2, y: tmpY2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + + //현재 내 선 + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpArray.push(firstPointObj) + tmpArray.push(secondPointObj) + roofPatternPolygonArray.push(tmpArray) + } else { + if (i === 1 || i === 5) { + // 큰 폴리곤은 가운데 선으로 되야됨 + if (outLines.length / 2 < concavePointIndices[0]) { + //오목이가 배열 전체보다 크면 위쪽 방향 + x2 = i === 1 ? lastCenterLine.x2 : lastCenterLine.x1 + y2 = i === 1 ? lastCenterLine.y2 : lastCenterLine.y1 + } else { + x1 = i === 1 ? lastCenterLine.x1 : lastCenterLine.x2 + y1 = i === 1 ? lastCenterLine.y1 : lastCenterLine.y2 + } + } + + if (i === 5) { + //5번일때는 앞에 3번에 선이 필요하다 + let prevX1 = tmpVertCenterLine[i - 2].x1 + let prevY1 = tmpVertCenterLine[i - 2].y1 + let prevX2 = tmpVertCenterLine[i - 2].x2 + let prevY2 = tmpVertCenterLine[i - 2].y2 + firstPointObj = { x: prevX1, y: prevY1 } + secondPointObj = { x: prevX2, y: prevY2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + } + + firstPointObj = { x: x1, y: y1 } + secondPointObj = { x: x2, y: y2 } + tmpBigArray.push(firstPointObj) + tmpBigArray.push(secondPointObj) + + if (i === 3 || i === 6) { + roofPatternPolygonArray.push(tmpBigArray) + tmpBigArray = [] + } + } + } + setRoofPolygonPattern({ roofPatternPolygonArray, lines }) + } + canvas?.renderAll() + } + /** * 세로 방샹 라인의 좌표 순서를 위에서 아래로 변경 * @param {array} arr @@ -3620,59 +4199,25 @@ export function useMode() { * 가대 생성 로직 */ const makeRoofTrestle = () => { + if (compass === undefined) { + alert('방위를 먼저 선택 해주세요.') + return + } + if (Object.keys(roofPolygonPattern).length === 0 && roofPolygonPattern.constructor === Object) { alert('객체가 비어있습니다.') return } const polygons = roofPolygonArray //리코일에 있는 패턴그린 폴리곤가져옴 - let selectedAreaArray = [] - - const defualtStrokeStyle = { - stroke: 'red', - strokeDashArray: [9, 5], - strokeWidth: 0.3, - } - - const selectedStrokeStyle = { - stroke: 'red', - strokeWidth: 3, - } /** * 지붕가대 생성 후 가대 선택 이벤트를 추가하는 로직 * @param polygon */ - function toggleSelection(polygon) { - if (polygon.strokeWidth === defualtStrokeStyle.strokeWidth) { - //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄 - polygon.set({ - stroke: selectedStrokeStyle.stroke, - strokeWidth: selectedStrokeStyle.strokeWidth, - strokeDashArray: [0], - }) - canvas.discardActiveObject() // 객체의 활성 상태 해제 - selectedAreaArray.push(polygon) - } else { - //선택후 재선택하면 선택안됨으로 변경 - polygon.set({ - stroke: defualtStrokeStyle.stroke, - strokeWidth: defualtStrokeStyle.strokeWidth, - strokeDashArray: defualtStrokeStyle.strokeDashArray, - }) - canvas.discardActiveObject() // 객체의 활성 상태 해제 - - //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함 - const removeIndex = polygon.idx - const removeArrayIndex = selectedAreaArray.findIndex((x) => x.idx === removeIndex) - selectedAreaArray.splice(removeArrayIndex, 1) - } - canvas?.renderAll() - } - const pattern = getRoofPattern(roofStyle, 'cell') //셀모드 배경색을 칠한다 - polygons.sort((a, b) => a.lines.length > b.lines.length) //무조건 잴 긴거 정렬 + polygons.sort((a, b) => b.points.length - a.points.length) //무조건 잴 긴거 정렬 // 외각선을 안쪽으로 그려 가대선을 그린다. polygons.forEach((polygon, index) => { @@ -3680,6 +4225,8 @@ export function useMode() { let referenceDirection = 'none' //상단 기준 값 let parallelPoint = -1 trestlePolygon.setViewLengthText(false) //얘는 set으로 안먹는다... + trestlePolygon.bringForward() + trestlePolygon.set({ stroke: 'red', strokeDashArray: [9, 5], @@ -3689,12 +4236,12 @@ export function useMode() { lockRotation: true, lockScalingX: true, lockScalingY: true, - bringToFront: true, idx: polygon.customIndex, //가대 폴리곤의 임시 인덱스를 넣어줌 name: 'trestlePolygon', }) if (polygon.points.length > 4) { + //6, 8각 //4각 이상일때만 한다 const concave = findConcavePointIndices(polygon.points) //오목한 부분기준으로 시작점을 찾으려 계산 parallelPoint = parseInt(concave.concavePointIndices[0] + 3) % polygon.points.length //시작점을 찾기 위해 적용 @@ -3757,11 +4304,107 @@ export function useMode() { toggleSelection(trestlePolygon) }) polygon.set({ fill: pattern }) + trestlePolygon.bringForward() }) + canvas?.renderAll() + setMode(Mode.DEFAULT) //default 모드로 변경 + } + + /** + * 가대 영역 선택 시 이벤트 + * @param {} polygon + */ + const toggleSelection = (polygon) => { + const selectedAreaArray = selectedCellRoofArray + + const defualtStrokeStyle = { + stroke: 'red', + strokeDashArray: [9, 5], + strokeWidth: 0.3, + } + + const wallDirection = polygon.wallDirection + + // //A템플릿 타입 + // if (templateType === 2) { + // if (compass === 90) { + // if (wallDirection === 'right') { + // alert('선택할 수 없는 방향입니다.') + // return + // } + // } else if (compass === 270) { + // if (wallDirection === 'left') { + // alert('선택할 수 없는 방향입니다.') + // return + // } + // } + // } else if (templateType === 3) { + // if (compass === 0) { + // if (wallDirection === 'top') { + // alert('선택할 수 없는 방향입니다.') + // return + // } + // } else if (compass === 180) { + // if (wallDirection === 'bottom') { + // alert('선택할 수 없는 방향입니다.') + // return + // } + // } + // } + + const invalidDirections = { + 2: { + 90: 'right', + 270: 'left', + }, + 3: { + 0: 'top', + 180: 'bottom', + }, + } + + if (invalidDirections[templateType] && invalidDirections[templateType][compass] === wallDirection) { + alert('선택할 수 없는 방향입니다.') + canvas.discardActiveObject() // 객체의 활성 상태 해제 + return + } + + const selectedStrokeStyle = { + stroke: 'red', + strokeWidth: 3, + } + if (polygon.strokeWidth === defualtStrokeStyle.strokeWidth) { + //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄 + polygon.set({ + stroke: selectedStrokeStyle.stroke, + strokeWidth: selectedStrokeStyle.strokeWidth, + strokeDashArray: [0], + }) + canvas.discardActiveObject() // 객체의 활성 상태 해제 + + //중복으로 들어가는걸 방지하기 위한 코드 + const isExist = selectedAreaArray.some((x) => x.idx === polygon.idx) + if (!isExist) { + selectedAreaArray.push(polygon) + } + } else { + //선택후 재선택하면 선택안됨으로 변경 + polygon.set({ + stroke: defualtStrokeStyle.stroke, + strokeWidth: defualtStrokeStyle.strokeWidth, + strokeDashArray: defualtStrokeStyle.strokeDashArray, + }) + canvas.discardActiveObject() // 객체의 활성 상태 해제 + + //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함 + const removeIndex = polygon.idx + const removeArrayIndex = selectedAreaArray.findIndex((x) => x.idx === removeIndex) + selectedAreaArray.splice(removeArrayIndex, 1) + } + setSelectedCellRoofArray(selectedAreaArray) canvas?.renderAll() - setMode(Mode.DEFAULT) //default 모드로 변경 } /** @@ -3769,8 +4412,9 @@ export function useMode() { */ const makeRoofFillCells = () => { const drawCellsArray = [] + const selectedCellRoofs = [...selectedCellRoofArray] - if (selectedCellRoofArray.length === 0) { + if (selectedCellRoofs.length === 0) { //배열에 선택된 가대 셀이 없으면 리턴 alert('선택된 영역이 없습니다.') setMode(Mode.DEFAULT) //default 모드로 변경 @@ -3797,9 +4441,7 @@ export function useMode() { ;[cellSize.width, cellSize.height] = [cellSize.height, cellSize.width] } - selectedCellRoofArray.forEach((polygon, index) => { - //오목한 부분의 반대 꼭지점 //없는 애들도 있어서 -1 - + selectedCellRoofs.forEach((polygon, index) => { const drawCells = polygon.fillCellABType({ width: cellSize.width, height: cellSize.height, @@ -3809,6 +4451,8 @@ export function useMode() { startIndex: polygon.startIndex, }) drawCellsArray.push({ roofIndex: polygon.customIndex, drawCells: drawCells }) + + // toggleSelection(polygon) //선택 후 셀그리면 지우려고 했는데 방위 땜에 삭제 }) setDrewRoofCells(drawCellsArray) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index c607796c..fa4d2770 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -67,3 +67,9 @@ export const roofMaterialState = atom({ default: { width: 20, height: 10, rafter: 0, roofStyle: 2 }, dangerouslyAllowMutability: true, }) + +export const compassState = atom({ + key: 'compass', + default: undefined, + dangerouslyAllowMutability: true, +})