From f1d976521dbe62b198176774da51102979366596 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 24 Mar 2025 10:55:53 +0900 Subject: [PATCH 1/7] =?UTF-8?q?#=20932=20-=20pcs=20=ED=95=9C=EA=B5=AD?= =?UTF-8?q?=EC=96=B4=20=EA=B8=B0=EC=9E=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/circuitTrestle/step/StepUp.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx index d776476b..0e8ad7b6 100644 --- a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx @@ -573,7 +573,7 @@ export default function StepUp(props) { value={seletedMainOption} sourceKey="code" targetKey="code" - showKey="name" + showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`} onChange={(e) => setSeletedMainOption(e)} /> )} @@ -586,7 +586,7 @@ export default function StepUp(props) { value={seletedSubOption} sourceKey="code" targetKey="code" - showKey="name" + showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`} onChange={(e) => setSeletedSubOption(e)} /> )} From 1ba9853a3229e90769ca4cfa5389744acba459f9 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 24 Mar 2025 11:06:35 +0900 Subject: [PATCH 2/7] =?UTF-8?q?#935=20=EC=A0=84=EB=B0=98/=EA=B8=80?= =?UTF-8?q?=EC=9E=90=ED=91=9C=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/module/useTrestle.js | 6 ++++-- src/locales/ja.json | 5 +++-- src/locales/ko.json | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index 48ad5913..be795814 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -10,6 +10,7 @@ import { useSwal } from '@/hooks/useSwal' import { useContext } from 'react' import { QcastContext } from '@/app/QcastProvider' import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle' +import { useMessage } from '@/hooks/useMessage' // 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주 const MODULE_MARGIN = 10 @@ -26,6 +27,7 @@ export const useTrestle = () => { const { getSelectedPcsItemList } = useCircuitTrestle() const { resetCircuits } = useCircuitTrestle() + const { getMessage } = useMessage() const apply = () => { const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit) @@ -131,9 +133,9 @@ export const useTrestle = () => { surface.isChidory = isChidory if (plvrYn === 'N' && isChidory) { - swalFire({ text: '치조불가공법입니다.', icon: 'error' }) + swalFire({ text: getMessage('chidory.can.not.install'), icon: 'error' }) clear() - throw new Error('치조불가공법입니다.') + throw new Error(getMessage('chidory.can.not.install')) } surface.set({ isChidory: isChidory }) diff --git a/src/locales/ja.json b/src/locales/ja.json index abc9b5cf..3bbc5344 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -128,7 +128,7 @@ "modal.module.basic.setting.pitch.module.row.margin": "上下間隔", "modal.module.basic.setting.pitch.module.column.amount": "列数", "modal.module.basic.setting.pitch.module.column.margin": "左右間隔", - "modal.module.basic.setting.prev": "移転", + "modal.module.basic.setting.prev": "前に戻る", "modal.module.basic.setting.passivity.placement": "手動配置", "modal.module.basic.setting.auto.placement": "設定値に自動配置", "plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定", @@ -1040,5 +1040,6 @@ "modal.placement.initial.setting.plan.drawing.only.number": "(※数字は[半角]入力のみ可能です。)", "wall.line.not.found": "外壁がありません", "roof.line.not.found": "屋根形状がありません", - "roof.material.can.not.delete": "割り当てられた配置面があります。" + "roof.material.can.not.delete": "割り当てられた配置面があります。", + "chidory.can.not.install" : "千鳥配置できない工法です。" } diff --git a/src/locales/ko.json b/src/locales/ko.json index 3ea284e2..f7dca490 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1040,5 +1040,6 @@ "modal.placement.initial.setting.plan.drawing.only.number": "(※ 숫자는 [반각]입력만 가능합니다.)", "wall.line.not.found": "외벽선이 없습니다.", "roof.line.not.found": "지붕형상이 없습니다.", - "roof.material.can.not.delete": "할당된 배치면이 있습니다." + "roof.material.can.not.delete": "할당된 배치면이 있습니다.", + "chidory.can.not.install" : "치조 불가 공법입니다." } From b458b4e8531762d03f70a6f564e6e5663cd8b7c3 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Mon, 24 Mar 2025 11:26:26 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EC=BA=94=EB=B2=84=EC=8A=A4=20=ED=99=95?= =?UTF-8?q?=EC=8B=9C=EB=8C=80=20=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useEvent.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index d3d270f6..2e5d23b0 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -79,6 +79,9 @@ export function useEvent() { // 마우스 위치 기준으로 확대/축소 canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom) + canvas.requestRenderAll() + canvas.calcOffset() + // 이벤트의 기본 동작 방지 (스크롤 방지) opt.e.preventDefault() opt.e.stopPropagation() From 85d9aca6d39fe7dffc5dc45acf08a550715b9278 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 24 Mar 2025 14:50:06 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=EB=B3=91=EB=A0=AC=EC=88=98=200=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index 618a65d2..a836044e 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -479,7 +479,7 @@ export default function CircuitTrestleSetting({ id }) { console.log(stepUpListData) stepUpListData[0].pcsItemList.map((item, index) => { return item.serQtyList - .filter((serQty) => serQty.selected) + .filter((serQty) => serQty.selected && serQty.paralQty > 0) .forEach((serQty) => { pcs.push({ pcsMkrCd: item.pcsMkrCd, From 437d552b3ab67d01a335499f0e8fd08c508ad4e1 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Tue, 25 Mar 2025 11:05:54 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=A9=B4=20=EB=B0=A9?= =?UTF-8?q?=ED=96=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/surface/useSurfaceShapeBatch.js | 310 +++++++++++++++++++--- 1 file changed, 270 insertions(+), 40 deletions(-) diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index 296632a7..0de86ecc 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -159,6 +159,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false + const { xInversion, yInversion } = surfaceRefs canvas?.remove(obj) //각도 추가 @@ -179,6 +180,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { } //회전, flip등이 먹은 기준으로 새로생성 + // const batchSurface = addPolygon(reorderedPoints, { const batchSurface = addPolygon(obj.getCurrentPoints(), { fill: 'transparent', stroke: 'red', @@ -208,6 +210,38 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { // const popupId = uuidv4() // addPopup(popupId, 2, ) + // 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) if (setIsHidden) setIsHidden(false) @@ -491,18 +525,18 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { } case 10: { 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 + length2, y: pointer.y + length4 / 2 - length5 }, - { - x: pointer.x + length1 / 2 - length1 + length2, - y: pointer.y + length4 / 2 - length5 - (length4 - 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 + length3, 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 } @@ -1095,10 +1129,10 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { if (line[coord1] === line[coord2]) { if (line.direction === evaesDirection) { line.attributes.type = LINE_TYPE.WALLLINE.EAVES - line.stroke = 'rgb(47, 0, 255)' + line.stroke = 'rgb(1, 1, 1)' } else if (line.direction === ridgeDirection) { 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 }) - if ( - (polygon.direction === 'south' && maxLineSorted.direction === 'left') || - (polygon.direction === 'north' && maxLineSorted.direction === 'right') || - (polygon.direction === 'east' && maxLineSorted.direction === 'bottom') || - (polygon.direction === 'west' && maxLineSorted.direction === 'top') - ) { - polygon.lines.forEach((line) => { - if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) { - line.attributes.type = LINE_TYPE.SUBLINE.RIDGE - } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) { - line.attributes.type = LINE_TYPE.WALLLINE.EAVES - } - }) - } + // if ( + // (polygon.direction === 'south' && maxLineSorted.direction === 'left') || + // (polygon.direction === 'north' && maxLineSorted.direction === 'right') + // // || + // // (polygon.direction === 'east' && maxLineSorted.direction === 'bottom') || + // // (polygon.direction === 'west' && maxLineSorted.direction === 'top') + // ) { + // polygon.lines.forEach((line) => { + // if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + // line.attributes.type = LINE_TYPE.SUBLINE.RIDGE + // } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) { + // line.attributes.type = LINE_TYPE.WALLLINE.EAVES + // } + // }) + // } if (maxLine.length === 1) { const maxLineCoord = polygon.lines.reduce((a, b) => { @@ -1141,31 +1176,226 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { : a }) - const isRealEavesLine = polygon.lines.find((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES) - if (isRealEavesLine) { - if (polygon.direction === 'south' || polygon.direction === 'north') { - const targetCoord = - polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2) - const realLineCoord = - polygon.direction === 'south' ? Math.max(isRealEavesLine.y1, isRealEavesLine.y2) : Math.min(isRealEavesLine.y1, isRealEavesLine.y2) + const isRealEavesLine = polygon.lines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES) + if (isRealEavesLine.length > 0) { + isRealEavesLine.forEach((line) => { + if (polygon.direction === 'south' || polygon.direction === 'north') { + const targetCoord = + polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2) + const realLineCoord = polygon.direction === 'south' ? Math.max(line.y1, line.y2) : Math.min(line.y1, line.y2) - if (targetCoord !== realLineCoord) { - isRealEavesLine.attributes.type = LINE_TYPE.SUBLINE.RIDGE - } - } 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) - const realLineCoord = - polygon.direction === 'east' ? Math.max(isRealEavesLine.x1, isRealEavesLine.x2) : Math.min(isRealEavesLine.x1, isRealEavesLine.x2) + if (targetCoord !== realLineCoord) { + 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) + const realLineCoord = polygon.direction === 'east' ? Math.max(line.x1, line.x2) : Math.min(line.x1, line.x2) - if (targetCoord !== realLineCoord) { - isRealEavesLine.attributes.type = LINE_TYPE.SUBLINE.RIDGE + if (targetCoord !== realLineCoord) { + 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 { applySurfaceShape, deleteAllSurfacesAndObjects, From 75549b66b57a7eed52800e32d9170fdefb6074e6 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 25 Mar 2025 11:10:56 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=8B=9C=20canvas=20=EC=A0=80=EC=9E=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/placementShape/PlacementShapeSetting.jsx | 5 ++++- src/hooks/module/useTrestle.js | 1 - src/hooks/roofcover/useRoofAllocationSetting.js | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 25ca72b8..e2e499b6 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -19,6 +19,7 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' import { usePolygon } from '@/hooks/usePolygon' import { canvasState } from '@/store/canvasAtom' import { useRoofFn } from '@/hooks/common/useRoofFn' +import { usePlan } from '@/hooks/usePlan' /** * 지붕 레이아웃 @@ -45,6 +46,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla const { setSurfaceShapePattern } = useRoofFn() const canvas = useRecoilValue(canvasState) const roofDisplay = useRecoilValue(roofDisplaySelector) + const { saveCanvas } = usePlan() const roofRef = { roofCd: useRef(null), @@ -205,7 +207,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla /** * 배치면초기설정 저장 버튼 클릭 */ - const handleSaveBtn = () => { + const handleSaveBtn = async () => { const roofInfo = { ...currentRoof, planNo: basicSetting.planNo, @@ -254,6 +256,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla /* 저장 후 화면 닫기 */ closePopup(id) + await saveCanvas(false) } return ( diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index be795814..a584876c 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -60,7 +60,6 @@ export const useTrestle = () => { } const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction if (!construction) { - swalFire({ text: 'construction 존재안함', icon: 'error' }) return } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index cfc73b81..2b9c9494 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -27,6 +27,7 @@ import { moduleSelectionDataState } from '@/store/selectedModuleOptions' import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController' import { outerLinePointsState } from '@/store/outerLineAtom' import { QcastContext } from '@/app/QcastProvider' +import { usePlan } from '@/hooks/usePlan' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) @@ -52,6 +53,7 @@ export function useRoofAllocationSetting(id) { const { swalFire } = useSwal() const { setIsGlobalLoading } = useContext(QcastContext) const { setSurfaceShapePattern } = useRoofFn() + const { saveCanvas } = usePlan() const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState) const resetPoints = useResetRecoilState(outerLinePointsState) @@ -225,6 +227,7 @@ export function useRoofAllocationSetting(id) { await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => { swalFire({ text: getMessage(res.returnMessage) }) setIsGlobalLoading(false) + saveCanvas(false) }) //Recoil 설정 From 592275c0de8b5d8790a4d1a88ad093f50b32071f Mon Sep 17 00:00:00 2001 From: yjnoh Date: Tue, 25 Mar 2025 14:49:07 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=A9=B4=20=EB=8F=84?= =?UTF-8?q?=ED=98=95=20=EC=9E=AC=EB=B0=B0=EC=B9=98=20=EB=B0=8F=20=EC=B2=98?= =?UTF-8?q?=EB=A7=88,=EC=9A=A9=EB=A7=88=EB=A3=A8,=EC=BC=80=EB=9D=BC?= =?UTF-8?q?=EB=B0=94=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 1 + src/components/fabric/QPolygon.js | 4 + src/hooks/surface/useSurfaceShapeBatch.js | 114 ++++++++-------------- 3 files changed, 47 insertions(+), 72 deletions(-) diff --git a/src/common/common.js b/src/common/common.js index d82d43f0..abda5acd 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -203,6 +203,7 @@ export const SAVE_KEY = [ 'fontWeight', 'dormerAttributes', 'toFixed', + 'isSortedPoints', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 396ae6af..a471b5be 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -45,8 +45,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { options.sort = options.sort ?? true options.parentId = options.parentId ?? null + this.isSortedPoints = false + if (!options.sort && points.length <= 8) { points = sortedPointLessEightPoint(points) + this.isSortedPoints = true } else { let isDiagonal = false points.forEach((point, i) => { @@ -62,6 +65,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { if (!isDiagonal) { points = sortedPoints(points) + this.isSortedPoints = true } } diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index 0de86ecc..75da1422 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -199,6 +199,8 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { pitch: globalPitch, surfaceId: surfaceId, direction: direction, + isXInversion: xInversion, + isYInversion: yInversion, }) canvas.setActiveObject(batchSurface) setSurfaceShapePattern(batchSurface, roofDisplay.column) @@ -213,35 +215,6 @@ export function useSurfaceShapeBatch({ isHidden, 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) if (setIsHidden) setIsHidden(false) @@ -650,27 +623,27 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { } case 14: { points = [ - { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 }, - { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 }, - { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, - { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 }, - { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 }, + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 }, { x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, y: pointer.y + length4 / 2 - length4 + length5, }, + { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, ] break } case 15: { points = [ - { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 }, { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, - { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) }, - { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, + { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, + { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) }, ] break } @@ -678,28 +651,28 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { case 16: { points = [ { - x: pointer.x - length1 / 2, - y: pointer.y + length3 / 2, + x: pointer.x - length1 / 2 + (length1 - length2) / 2, + y: pointer.y + length3 / 2 - (length3 - length4) - length4, }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2, y: pointer.y + length3 / 2 - (length3 - length4), }, { - x: pointer.x - length1 / 2 + (length1 - length2) / 2, - y: pointer.y + length3 / 2 - (length3 - length4) - length4, + x: pointer.x - length1 / 2, + y: pointer.y + length3 / 2, }, { - x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, - y: pointer.y + length3 / 2 - (length3 - length4) - length4, + x: pointer.x - length1 / 2 + length1, + y: pointer.y + length3 / 2, }, { x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4, }, { - x: pointer.x - length1 / 2 + length1, - y: pointer.y + length3 / 2, + x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, + y: pointer.y + length3 / 2 - (length3 - length4) - length4, }, ] break @@ -710,25 +683,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 points = [ - { - x: pointer.x - length1 / 2 + length1, - y: pointer.y + length3 / 2, - }, { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2, }, { - x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)), - y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), + x: pointer.x - length1 / 2 + length1, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), }, { x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2, y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), }, { - x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)), - y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), + x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), }, ] break @@ -1111,7 +1084,10 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { * @param { } polygon */ + //폴리곤, 상하반전, 좌우반전 const changeSurfaceLineType = (polygon) => { + const { isXInversion, isYInversion } = polygon //상하반전, 좌우반전 + polygon.lines.forEach((line) => { line.attributes.type = LINE_TYPE.WALLLINE.GABLE }) @@ -1129,10 +1105,8 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { if (line[coord1] === line[coord2]) { if (line.direction === evaesDirection) { line.attributes.type = LINE_TYPE.WALLLINE.EAVES - line.stroke = 'rgb(1, 1, 1)' } else if (line.direction === ridgeDirection) { line.attributes.type = LINE_TYPE.SUBLINE.RIDGE - line.stroke = 'rgb(9, 9, 9)' } } }) @@ -1152,21 +1126,19 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { : a }) - // if ( - // (polygon.direction === 'south' && maxLineSorted.direction === 'left') || - // (polygon.direction === 'north' && maxLineSorted.direction === 'right') - // // || - // // (polygon.direction === 'east' && maxLineSorted.direction === 'bottom') || - // // (polygon.direction === 'west' && maxLineSorted.direction === 'top') - // ) { - // polygon.lines.forEach((line) => { - // if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) { - // line.attributes.type = LINE_TYPE.SUBLINE.RIDGE - // } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) { - // line.attributes.type = LINE_TYPE.WALLLINE.EAVES - // } - // }) - // } + //정렬된 폴리곤이 아니면(대각선이 존재하는 폴리곤일때) + if (!polygon.isSortedPoints) { + //좌우 반전을 했으면 반대로 정의함 + if (isYInversion) { + polygon.lines.forEach((line) => { + if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + line.attributes.type = LINE_TYPE.SUBLINE.RIDGE + } else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) { + line.attributes.type = LINE_TYPE.WALLLINE.EAVES + } + }) + } + } if (maxLine.length === 1) { const maxLineCoord = polygon.lines.reduce((a, b) => { @@ -1186,7 +1158,6 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { if (targetCoord !== realLineCoord) { line.attributes.type = LINE_TYPE.SUBLINE.RIDGE - line.stroke = 'rgb(9, 9, 9)' } } else if (polygon.direction === 'east' || polygon.direction === 'west') { const targetCoord = @@ -1195,7 +1166,6 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { if (targetCoord !== realLineCoord) { line.attributes.type = LINE_TYPE.SUBLINE.RIDGE - line.stroke = 'rgb(9, 9, 9)' } } })