From 5102fa2ec08ae9a878340c3b3dcfc571beeb53f0 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 11 Jul 2025 14:09:50 +0900 Subject: [PATCH 1/9] =?UTF-8?q?polygon=20=EC=84=A0=ED=83=9D=EC=9D=B4=20?= =?UTF-8?q?=EC=9E=98=20=EC=95=88=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 153 +++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 25 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 264d62d1..a1a5a169 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -721,8 +721,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { } // Ray casting 알고리즘 - if (((yi > testY) !== (yj > testY)) && - (testX < (xj - xi) * (testY - yi) / (yj - yi) + xi)) { + if (yi > testY !== yj > testY && testX < ((xj - xi) * (testY - yi)) / (yj - yi) + xi) { inside = !inside } } @@ -739,7 +738,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { // 벡터의 외적을 계산하여 점이 선분 위에 있는지 확인 const crossProduct = Math.abs(dxPoint * dySegment - dyPoint * dxSegment) - + if (crossProduct > tolerance) { return false } @@ -747,7 +746,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { // 점이 선분의 범위 내에 있는지 확인 const dotProduct = dxPoint * dxSegment + dyPoint * dySegment const squaredLength = dxSegment * dxSegment + dySegment * dySegment - + return dotProduct >= 0 && dotProduct <= squaredLength }, setCoords: function () { @@ -773,36 +772,140 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { // 먼저 좌표 업데이트 this.setCoords() - // 캔버스 줌과 viewport transform 고려한 좌표 변환 - let localPoint = point - if (this.canvas) { + // 기본 Fabric.js bounding box 체크 먼저 수행 + if (!this.callSuper('containsPoint', point)) { + return false + } + + // 줌 레벨에 관계없이 안정적인 좌표 변환 + const matrix = this.calcTransformMatrix() + const invertedMatrix = fabric.util.invertTransform(matrix) + + // 캔버스 줌 고려 + let testPoint = point + if (this.canvas && this.canvas.viewportTransform) { const vpt = this.canvas.viewportTransform - if (vpt) { - // viewport transform 역변환 - const inverted = fabric.util.invertTransform(vpt) - localPoint = fabric.util.transformPoint(point, inverted) + const invertedVpt = fabric.util.invertTransform(vpt) + testPoint = fabric.util.transformPoint(point, invertedVpt) + } + + // 오브젝트 좌표계로 변환 + const localPoint = fabric.util.transformPoint(testPoint, invertedMatrix) + + // pathOffset 적용 + const pathOffset = this.get('pathOffset') + const finalPoint = { + x: localPoint.x + pathOffset.x, + y: localPoint.y + pathOffset.y, + } + + // 단순하고 안정적인 point-in-polygon 알고리즘 사용 + return this.simplePointInPolygon(finalPoint, this.get('points')) + }, + + simplePointInPolygon: function (point, polygon) { + const x = point.x + const y = point.y + let inside = false + + for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { + const xi = polygon[i].x + const yi = polygon[i].y + const xj = polygon[j].x + const yj = polygon[j].y + + if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) { + inside = !inside } } - // 오브젝트의 transform matrix를 고려한 좌표 변환 - const matrix = this.calcTransformMatrix() - const invertedMatrix = fabric.util.invertTransform(matrix) - const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix) + return inside + }, - // pathOffset을 고려한 최종 좌표 계산 - const pathOffset = this.get('pathOffset') - const finalPoint = { - x: transformedPoint.x + pathOffset.x, - y: transformedPoint.y + pathOffset.y, + isPointInPolygonRobust: function (point, polygon) { + const x = point.x + const y = point.y + let inside = false + + // 부동소수점 정밀도를 고려한 허용 오차 + const epsilon = 1e-10 + + for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { + const xi = polygon[i].x + const yi = polygon[i].y + const xj = polygon[j].x + const yj = polygon[j].y + + // 점이 정점 위에 있는지 확인 (확장된 허용 오차) + if (Math.abs(xi - x) < 0.5 && Math.abs(yi - y) < 0.5) { + return true + } + + // 점이 선분 위에 있는지 확인 + if (this.isPointOnLineSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) { + return true + } + + // Ray casting 알고리즘 - 개선된 버전 + if (Math.abs(yi - yj) > epsilon) { + const minY = Math.min(yi, yj) + const maxY = Math.max(yi, yj) + + if (y > minY && y <= maxY) { + // 교차점 계산 + const intersectionX = xi + ((y - yi) / (yj - yi)) * (xj - xi) + if (intersectionX > x) { + inside = !inside + } + } + } } - if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { - const isInside = this.inPolygonImproved(finalPoint) - this.set('selectable', isInside) - return isInside + return inside + }, + + isPointOnLineSegment: function (point, lineStart, lineEnd) { + const tolerance = 2.0 // 더 큰 허용 오차 + const x = point.x + const y = point.y + const x1 = lineStart.x + const y1 = lineStart.y + const x2 = lineEnd.x + const y2 = lineEnd.y + + // 선분의 길이가 0인 경우 (점) + if (Math.abs(x2 - x1) < 1e-10 && Math.abs(y2 - y1) < 1e-10) { + return Math.abs(x - x1) < tolerance && Math.abs(y - y1) < tolerance + } + + // 점과 선분 사이의 거리 계산 + const A = x - x1 + const B = y - y1 + const C = x2 - x1 + const D = y2 - y1 + + const dot = A * C + B * D + const lenSq = C * C + D * D + const param = dot / lenSq + + let xx, yy + + if (param < 0 || (x1 === x2 && y1 === y2)) { + xx = x1 + yy = y1 + } else if (param > 1) { + xx = x2 + yy = y2 } else { - return this.inPolygonImproved(finalPoint) + xx = x1 + param * C + yy = y1 + param * D } + + const dx = x - xx + const dy = y - yy + const distance = Math.sqrt(dx * dx + dy * dy) + + return distance < tolerance }, inPolygonABType(x, y, polygon) { From 47a8274fd076d99d1b0f81970d2a983b9b4dae0e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 11 Jul 2025 14:34:52 +0900 Subject: [PATCH 2/9] =?UTF-8?q?QPolygon=20=EC=84=A0=ED=83=9D=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 142 ++++-------------------------- 1 file changed, 19 insertions(+), 123 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index a1a5a169..ebf4e625 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -772,140 +772,36 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { // 먼저 좌표 업데이트 this.setCoords() - // 기본 Fabric.js bounding box 체크 먼저 수행 - if (!this.callSuper('containsPoint', point)) { - return false + // 캔버스 줌과 viewport transform 고려한 좌표 변환 + let localPoint = point + if (this.canvas) { + const vpt = this.canvas.viewportTransform + if (vpt) { + // viewport transform 역변환 + const inverted = fabric.util.invertTransform(vpt) + localPoint = fabric.util.transformPoint(point, inverted) + } } - // 줌 레벨에 관계없이 안정적인 좌표 변환 + // 오브젝트의 transform matrix를 고려한 좌표 변환 const matrix = this.calcTransformMatrix() const invertedMatrix = fabric.util.invertTransform(matrix) + const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix) - // 캔버스 줌 고려 - let testPoint = point - if (this.canvas && this.canvas.viewportTransform) { - const vpt = this.canvas.viewportTransform - const invertedVpt = fabric.util.invertTransform(vpt) - testPoint = fabric.util.transformPoint(point, invertedVpt) - } - - // 오브젝트 좌표계로 변환 - const localPoint = fabric.util.transformPoint(testPoint, invertedMatrix) - - // pathOffset 적용 + // pathOffset을 고려한 최종 좌표 계산 const pathOffset = this.get('pathOffset') const finalPoint = { - x: localPoint.x + pathOffset.x, - y: localPoint.y + pathOffset.y, + x: transformedPoint.x + pathOffset.x, + y: transformedPoint.y + pathOffset.y, } - // 단순하고 안정적인 point-in-polygon 알고리즘 사용 - return this.simplePointInPolygon(finalPoint, this.get('points')) - }, - - simplePointInPolygon: function (point, polygon) { - const x = point.x - const y = point.y - let inside = false - - for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { - const xi = polygon[i].x - const yi = polygon[i].y - const xj = polygon[j].x - const yj = polygon[j].y - - if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) { - inside = !inside - } - } - - return inside - }, - - isPointInPolygonRobust: function (point, polygon) { - const x = point.x - const y = point.y - let inside = false - - // 부동소수점 정밀도를 고려한 허용 오차 - const epsilon = 1e-10 - - for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { - const xi = polygon[i].x - const yi = polygon[i].y - const xj = polygon[j].x - const yj = polygon[j].y - - // 점이 정점 위에 있는지 확인 (확장된 허용 오차) - if (Math.abs(xi - x) < 0.5 && Math.abs(yi - y) < 0.5) { - return true - } - - // 점이 선분 위에 있는지 확인 - if (this.isPointOnLineSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) { - return true - } - - // Ray casting 알고리즘 - 개선된 버전 - if (Math.abs(yi - yj) > epsilon) { - const minY = Math.min(yi, yj) - const maxY = Math.max(yi, yj) - - if (y > minY && y <= maxY) { - // 교차점 계산 - const intersectionX = xi + ((y - yi) / (yj - yi)) * (xj - xi) - if (intersectionX > x) { - inside = !inside - } - } - } - } - - return inside - }, - - isPointOnLineSegment: function (point, lineStart, lineEnd) { - const tolerance = 2.0 // 더 큰 허용 오차 - const x = point.x - const y = point.y - const x1 = lineStart.x - const y1 = lineStart.y - const x2 = lineEnd.x - const y2 = lineEnd.y - - // 선분의 길이가 0인 경우 (점) - if (Math.abs(x2 - x1) < 1e-10 && Math.abs(y2 - y1) < 1e-10) { - return Math.abs(x - x1) < tolerance && Math.abs(y - y1) < tolerance - } - - // 점과 선분 사이의 거리 계산 - const A = x - x1 - const B = y - y1 - const C = x2 - x1 - const D = y2 - y1 - - const dot = A * C + B * D - const lenSq = C * C + D * D - const param = dot / lenSq - - let xx, yy - - if (param < 0 || (x1 === x2 && y1 === y2)) { - xx = x1 - yy = y1 - } else if (param > 1) { - xx = x2 - yy = y2 + if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { + const isInside = this.inPolygonImproved(finalPoint) + this.set('selectable', isInside) + return isInside } else { - xx = x1 + param * C - yy = y1 + param * D + return this.inPolygonImproved(finalPoint) } - - const dx = x - xx - const dy = y - yy - const distance = Math.sqrt(dx * dx + dy * dy) - - return distance < tolerance }, inPolygonABType(x, y, polygon) { From 9f7fedab799f3bb0f8864eb784aa074fccd76c08 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 11 Jul 2025 15:59:30 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[1182]=20=EC=B6=94=EA=B0=80=20=EA=B2=AC?= =?UTF-8?q?=EC=A0=81=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20POPUP=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/floor-plan/FloorPlanProvider.js | 2 ++ src/components/estimate/Estimate.jsx | 3 ++- .../estimate/useEstimateController.js | 25 ++++++++++++++++++- src/locales/ja.json | 7 +++--- src/locales/ko.json | 1 + 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/app/floor-plan/FloorPlanProvider.js b/src/app/floor-plan/FloorPlanProvider.js index 1e5c536d..a9595aa5 100644 --- a/src/app/floor-plan/FloorPlanProvider.js +++ b/src/app/floor-plan/FloorPlanProvider.js @@ -21,6 +21,8 @@ const defaultEstimateData = { fileList: [], fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0) priceCd: '', + pricingFlag: false, // 가격 처리 플래그 추가 + } /** diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index 9a3dd74d..2fe098cf 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -507,6 +507,7 @@ export default function Estimate({}) { icon: 'warning', confirmFn: () => { handlePricing(showPriceCd) + setEstimateContextState({ pricingFlag:true }) }, }) } @@ -1437,7 +1438,7 @@ export default function Estimate({}) { onChange={(e) => { //주문분류 setHandlePricingFlag(true) - setEstimateContextState({ estimateType: e.target.value }) + setEstimateContextState({ estimateType: e.target.value, setEstimateContextState }) }} /> diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 9acb79f7..4cbe4521 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -167,13 +167,34 @@ export const useEstimateController = (planNo, flag) => { }) } - //견적서 저장 + const handleEstimateSubmit = async () => { + if(estimateContextState.pricingFlag) { + await handleEstimateSubmitCore(); + }else { + swalFire({ + text: getMessage('estimate.detail.save.pricingFlag'), + type: 'confirm', + icon: 'warning', + confirmFn: async () => { + await handleEstimateSubmitCore(); + + }, + denyFn: () => false + }) + return false + } + } + //견적서 저장 + const handleEstimateSubmitCore = async () => { //0. 필수체크 let flag = true let originFileFlg = false let fileFlg = true let itemFlg = true + + + if (estimateData?.charger === null || estimateData?.charger?.trim().length === 0) { flag = false setIsGlobalLoading(false) @@ -355,6 +376,8 @@ export const useEstimateController = (planNo, flag) => { } const realSave = async (fileList) => { + + setEstimateContextState({pricingFlag: false}) //첨부파일저장끝 let option = [] diff --git a/src/locales/ja.json b/src/locales/ja.json index ed372e18..5cd08828 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -993,10 +993,10 @@ "estimate.detail.docPopup.title": "見積書出力オプションの設定", "estimate.detail.docPopup.explane": "ダウンロードする文書オプションを選択し、[見積書出力]ボタンをクリックします。", "estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル", - "estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "仕切用Excel", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "見積書Excel", "estimate.detail.docPopup.schUnitPriceFlg.excelFlg1": "定価用Excel", - "estimate.detail.docPopup.schUnitPriceFlg.excelFlg2": "見積書", - "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "仕切用PDF", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg2": "旧見積書書式", + "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "見積書PDF", "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1": "定価用PDF", "estimate.detail.docPopup.schDisplayFlg": "見積提出書の表示名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名", @@ -1034,6 +1034,7 @@ "estimate.detail.save.requiredItemId": "製品を選択してください。", "estimate.detail.save.requiredAmount": "数量は0より大きい値を入力してください。", "estimate.detail.save.requiredSalePrice": "単価は0より大きい値を入力してください。", + "estimate.detail.save.pricingFlag": "設定した価格見積が作成されます。このまま進めてよろしいですか?", "estimate.detail.reset.alertMsg": "初期化されました。", "estimate.detail.reset.confirmMsg": "保存した見積情報が初期化され、最近保存された図面情報が反映されます。本当に初期化しますか?", "estimate.detail.lock.alertMsg": "見積もりを[ロック]すると変更できません。
見積もりを修正するには、ロックを解除してください。", diff --git a/src/locales/ko.json b/src/locales/ko.json index c0cd43af..19864ac4 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1034,6 +1034,7 @@ "estimate.detail.save.requiredItemId": "제품을 선택해주세요.", "estimate.detail.save.requiredAmount": "수량은 0보다 큰값을 입력해주세요.", "estimate.detail.save.requiredSalePrice": "단가는 0보다 큰값을 입력해주세요.", + "estimate.detail.save.pricingFlag": "설정한 가격 견적이 작성됩니다. 이대로 진행해도 괜찮으시겠습니까?", "estimate.detail.reset.alertMsg": "초기화 되었습니다.", "estimate.detail.reset.confirmMsg": "수기 변경(저장)한 견적 정보가 초기화되고 최근 저장된 도면정보가 반영됩니다. 정말로 초기화하시겠습니까?", "estimate.detail.lock.alertMsg": "견적서를 [잠금]하면 수정할 수 없습니다.
견적서를 수정하려면 잠금해제를 하십시오.", From b29b76a15846a62a73bd1fb7203f3c3c6ae97364 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 11 Jul 2025 17:02:56 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[1182]=20=EC=B6=94=EA=B0=80=20=EA=B2=AC?= =?UTF-8?q?=EC=A0=81=20=EC=B6=9C=EB=A0=A5=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20POPUP=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/estimate/popup/DocDownOptionPop.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/estimate/popup/DocDownOptionPop.jsx b/src/components/estimate/popup/DocDownOptionPop.jsx index 64f5537c..f534fbda 100644 --- a/src/components/estimate/popup/DocDownOptionPop.jsx +++ b/src/components/estimate/popup/DocDownOptionPop.jsx @@ -130,7 +130,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
- + From dc6b268b7497b3473c412b973e51673deacea886 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 14 Jul 2025 10:35:38 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=EC=BA=A1=EC=B3=90=20=EC=A0=84=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=AD=EB=AA=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/floorPlan/useImgLoader.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hooks/floorPlan/useImgLoader.js b/src/hooks/floorPlan/useImgLoader.js index 12c9cbb2..242206db 100644 --- a/src/hooks/floorPlan/useImgLoader.js +++ b/src/hooks/floorPlan/useImgLoader.js @@ -44,7 +44,7 @@ export function useImgLoader() { */ const handleCanvasToPng = async (type) => { try { - removeMouseLines() + toggleLineEtc(false) canvas.getObjects('image').forEach((obj) => { if (obj.getSrc) { @@ -86,6 +86,8 @@ export function useImgLoader() { }) console.log('🚀 ~ handleCanvasToPng ~ result:', result) + toggleLineEtc(true) + return result } catch (e) { setIsGlobalLoading(false) @@ -94,13 +96,21 @@ export function useImgLoader() { } /** - * 마우스 포인터의 가이드라인을 제거합니다. + * 마우스 포인터 그리드, 임의그리드, 흡착점 등 제거. + * */ - const removeMouseLines = () => { + const toggleLineEtc = (visible = false) => { if (canvas?._objects.length > 0) { const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine') mouseLines.forEach((item) => canvas?.remove(item)) } + + const adsorptionPoints = canvas?._objects.filter((obj) => obj.name === 'adsorptionPoint') + const gridLines = canvas?._objects.filter((obj) => obj.name === 'lineGrid' || obj.name === 'tempGrid' || obj.name === 'dotGrid') + + adsorptionPoints.forEach((item) => item.set({ visible: visible })) + gridLines.forEach((item) => item.set({ visible: visible })) + canvas?.renderAll() } From c58ff87a4c84a4c08153db2a61963c79a0e10ab4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 14 Jul 2025 15:32:22 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=ED=9D=A1=EC=B0=A9=EC=A0=90=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 --- src/hooks/useEvent.js | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index cf1d1951..ff134358 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -16,6 +16,7 @@ import { useTempGrid } from '@/hooks/useTempGrid' import { gridColorState } from '@/store/gridAtom' import { gridDisplaySelector } from '@/store/settingAtom' import { MENU, POLYGON_TYPE } from '@/common/common' +import { useMouse } from '@/hooks/useMouse' export function useEvent() { const canvas = useRecoilValue(canvasState) @@ -35,6 +36,8 @@ export function useEvent() { const textMode = useRecoilValue(textModeState) + const { getIntersectMousePoint } = useMouse() + // 이벤트 초기화 위치 수정 -> useCanvasSetting에서 세팅값 불러오고 나서 초기화 함수 호출 // useEffect(() => { // initEvent() @@ -97,17 +100,16 @@ export function useEvent() { if (zoom < 0.5) zoom = 0.5 setCanvasZoom(Number((zoom * 100).toFixed(0))) - + const { x, y } = getIntersectMousePoint(opt) // 마우스 위치 기준으로 확대/축소 - canvas.zoomToPoint(new fabric.Point(opt.e.offsetX, opt.e.offsetY), zoom) - canvas.calcOffset() - canvas.setViewportTransform(canvas.viewportTransform) - canvas.requestRenderAll() + canvas.zoomToPoint(new fabric.Point(x, y), zoom) canvas.getObjects().forEach((obj) => { obj.setCoords() }) + canvas.renderAll() + // 이벤트의 기본 동작 방지 (스크롤 방지) opt.e.preventDefault() opt.e.stopPropagation() @@ -138,7 +140,17 @@ export function useEvent() { return } - const intersectionPoint = calculateIntersection(line1, line2) + const intersectionPoint = calculateIntersection(line1, line2) // 보조선끼리 만나는 점 + + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 외벽선 + + outerLines.forEach((outerLine) => { + const outerIntersectionPoint = calculateIntersection(outerLine, line1) // 외벽선과 보조선의 교차점 + if (outerIntersectionPoint) { + intersectionPoints.current.push(outerIntersectionPoint) + } + }) + if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) { return } @@ -147,6 +159,7 @@ export function useEvent() { }) let innerLinePoints = [] + let outerLinePoints = [] canvas .getObjects() .filter((obj) => obj.innerLines) @@ -157,12 +170,19 @@ export function useEvent() { }) }) + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((line) => { + outerLinePoints.push({ x: line.x2, y: line.y2 }) + outerLinePoints.push({ x: line.x1, y: line.y1 }) + }) + const adsorptionPoints = [ ...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, ...intersectionPoints.current, ...innerLinePoints, + ...outerLinePoints, ] if (dotLineGridSetting.LINE || canvas.getObjects().filter((obj) => ['lineGrid', 'tempGrid'].includes(obj.name)).length > 1) { @@ -354,15 +374,10 @@ export function useEvent() { canvas.renderAll() } - const defaultKeyboardEvent = (e) => { - if (e.key === 'Escape') { - console.log('defaultKeyboardEvent') - } - } - const addCanvasMouseEventListener = (eventType, handler) => { canvas.off(eventType) canvas.on(eventType, handler) + canvas.on('mouse:move', defaultMouseMoveEvent) // default mouse:move 이벤트는 항상 등록 mouseEventListeners.current.push({ eventType, handler }) } From 8226f6cf3de7e1fa51e84ca99093ddfc5bed46ff Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Jul 2025 09:53:53 +0900 Subject: [PATCH 7/9] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20=ED=9D=A1?= =?UTF-8?q?=EC=B0=A9=20=EA=B4=80=EB=A0=A8=20=EC=9E=91=EC=97=85=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 --- src/components/fabric/QPolygon.js | 33 +++++++---- src/hooks/module/useTrestle.js | 69 ++++++++++++++++++----- src/hooks/object/useObjectBatch.js | 25 ++++---- src/hooks/roofcover/useMovementSetting.js | 15 +++-- src/hooks/useEvent.js | 19 ++++++- 5 files changed, 116 insertions(+), 45 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index ebf4e625..20be0c35 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -701,14 +701,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { inPolygonImproved(point) { const vertices = this.points let inside = false - const testX = point.x - const testY = point.y + const testX = Number(point.x.toFixed(this.toFixed)) + const testY = Number(point.y.toFixed(this.toFixed)) for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) { - const xi = vertices[i].x - const yi = vertices[i].y - const xj = vertices[j].x - const yj = vertices[j].y + const xi = Number(vertices[i].x.toFixed(this.toFixed)) + const yi = Number(vertices[i].y.toFixed(this.toFixed)) + const xj = Number(vertices[j].x.toFixed(this.toFixed)) + const yj = Number(vertices[j].y.toFixed(this.toFixed)) // 점이 정점 위에 있는지 확인 if (Math.abs(xi - testX) < 0.01 && Math.abs(yi - testY) < 0.01) { @@ -720,9 +720,16 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { return true } - // Ray casting 알고리즘 - if (yi > testY !== yj > testY && testX < ((xj - xi) * (testY - yi)) / (yj - yi) + xi) { - inside = !inside + // Ray casting 알고리즘 - 부동소수점 정밀도 개선 + if (yi > testY !== yj > testY) { + const denominator = yj - yi + if (Math.abs(denominator) > 1e-10) { + // 0으로 나누기 방지 + const intersection = ((xj - xi) * (testY - yi)) / denominator + xi + if (testX < intersection) { + inside = !inside + } + } } } @@ -791,13 +798,15 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { // pathOffset을 고려한 최종 좌표 계산 const pathOffset = this.get('pathOffset') const finalPoint = { - x: transformedPoint.x + pathOffset.x, - y: transformedPoint.y + pathOffset.y, + x: Number((transformedPoint.x + pathOffset.x).toFixed(this.toFixed)), + y: Number((transformedPoint.y + pathOffset.y).toFixed(this.toFixed)), } if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { const isInside = this.inPolygonImproved(finalPoint) - this.set('selectable', isInside) + if (!this.selectable) { + this.set('selectable', isInside) + } return isInside } else { return this.inPolygonImproved(finalPoint) diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index 9fa75e67..8e234cf0 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -32,7 +32,10 @@ export const useTrestle = () => { const apply = () => { const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit) if (notAllocationModules.length > 0) { - swalFire({ text: '回路番号が設定されていないモジュールがあります。 番号を設定しなおすか、 パネルを削除してください。', icon: 'error' }) + swalFire({ + text: '回路番号が設定されていないモジュールがあります。 番号を設定しなおすか、 パネルを削除してください。', + icon: 'error', + }) setIsGlobalLoading(false) return } @@ -807,15 +810,12 @@ export const useTrestle = () => { const getAzimuth = (parent) => { const { moduleCompass, surfaceCompass, direction } = parent - if(surfaceCompass) { + if (surfaceCompass) { return -surfaceCompass } let resultAzimuth = moduleCompass - - - switch (direction) { case 'south': { return resultAzimuth @@ -1882,7 +1882,15 @@ export const useTrestle = () => { const { width, height } = { ...module } widthArr.push(width) heightArr.push(height) - centerPoints.push({ x, y, width: Math.floor(width), height: Math.floor(height), index, moduleInfo: module.moduleInfo, module }) + centerPoints.push({ + x, + y, + width: Math.floor(width), + height: Math.floor(height), + index, + moduleInfo: module.moduleInfo, + module, + }) }) //widthArr 중복 제거 1이상 차이가 나지 않으면 같은 너비로 간주 @@ -2055,8 +2063,8 @@ export const useTrestle = () => { /** * 디버깅용 - module.set('fill', originFill) - canvas.renderAll() + module.set('fill', originFill) + canvas.renderAll() */ if (bottomCell) { return @@ -2182,8 +2190,8 @@ export const useTrestle = () => { /** * 디버깅 용 - module.set('fill', originFill) - canvas.renderAll() + module.set('fill', originFill) + canvas.renderAll() */ if (leftTopCnt + rightTopCnt === 2) { @@ -3265,18 +3273,49 @@ export const useTrestle = () => { return result } + function countMatchingCircuitNumbers(arrays) { + let cnt = 0 + + // 모든 고유한 circuitNumber 찾기 + const allCircuitNumbers = new Set() + arrays.forEach((arr) => { + arr.forEach((item) => { + allCircuitNumbers.add(item.circuitNumber) + }) + }) + + // 각 circuitNumber가 몇 개의 배열에 나타나는지 세기 + allCircuitNumbers.forEach((circuitNum) => { + let arrayCount = 0 + + arrays.forEach((arr) => { + const hasCircuitNum = arr.some((item) => item.circuitNumber === circuitNum) + if (hasCircuitNum) { + arrayCount++ + } + }) + + // 2개 이상의 배열에 나타나는 경우에만 카운트 (배열 개수 - 1) + if (arrayCount >= 2) { + cnt += arrayCount - 1 + } + }) + + return cnt + } + // 양단 케이블 구하는 공식 const getTotalConnectCableCnt = () => { let cnt = 0 const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE) + surfaces.forEach((surface) => { const modules = surface.modules - const groups = groupByType(modules) - groups.forEach((group) => { - const result = groupPoints(group, surface) - cnt += result.length - 1 - }) + // 1. 현재 surface의 모듈들을 그룹화 + const groupInSurface = groupPoints(modules, surface) + + cnt += countMatchingCircuitNumbers(groupInSurface) }) const groupByCircuitAndSurfaceCnt = groupByCircuitAndSurface(modules) diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js index 322f2531..accb6866 100644 --- a/src/hooks/object/useObjectBatch.js +++ b/src/hooks/object/useObjectBatch.js @@ -1,18 +1,17 @@ 'use client' -import { useEffect, useRef } from 'react' +import { useEffect } from 'react' import { useMessage } from '@/hooks/useMessage' import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { BATCH_TYPE, INPUT_TYPE, POLYGON_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { + getDegreeByChon, + getTrianglePoints, pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, - triangleToPolygon, - getDegreeByChon, toFixedWithoutRounding, - getTrianglePoints, } from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' @@ -23,6 +22,7 @@ import { fontSelector } from '@/store/fontAtom' import { useRoofFn } from '@/hooks/common/useRoofFn' import { roofDisplaySelector } from '@/store/settingAtom' import { usePopup } from '@/hooks/usePopup' +import { useMouse } from '@/hooks/useMouse' export function useObjectBatch({ isHidden, setIsHidden }) { const { getMessage } = useMessage() @@ -35,6 +35,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const roofDisplay = useRecoilValue(roofDisplaySelector) const { closePopup } = usePopup() + const { getIntersectMousePoint } = useMouse() useEffect(() => { if (canvas) { @@ -59,7 +60,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { console.log('event', e) if (e.target && e.target instanceof fabric.Group) { - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) const objects = e.target._objects // 클릭한 위치에 있는 객체 찾기 @@ -98,7 +99,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { if (selectedType === INPUT_TYPE.FREE) { addCanvasMouseEventListener('mouse:down', (e) => { isDown = true - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { @@ -140,7 +141,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { if (!isDown) return if (selectedSurface) { - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) const width = pointer.x - origX const height = pointer.y - origY @@ -208,7 +209,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === objTempName)) //움직일때 일단 지워가면서 움직임 - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { @@ -220,8 +221,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) { strokeWidth: 1, width: width, height: height, - left: pointer.x - width / 2, - top: pointer.y - height / 2, + left: pointer.x, + top: pointer.y, selectable: true, lockMovementX: true, lockMovementY: true, @@ -329,7 +330,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { @@ -679,7 +680,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { if (!isDown) return canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 - const pointer = canvas.getPointer(e.e) + const pointer = getIntersectMousePoint(e) surfaceShapePolygons.forEach((surface) => { if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 19e44d2e..671ad439 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -8,6 +8,7 @@ import { useSwal } from '@/hooks/useSwal' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' import { calcLinePlaneSize } from '@/util/qpolygon-utils' +import { useMouse } from '@/hooks/useMouse' //동선이동 형 올림 내림 export function useMovementSetting(id) { @@ -19,6 +20,7 @@ export function useMovementSetting(id) { const { initEvent, addCanvasMouseEventListener } = useEvent() const { closePopup } = usePopup() const { getMessage } = useMessage() + const { getIntersectMousePoint } = useMouse() const currentObject = useRecoilValue(currentObjectState) const selectedObject = useRef(null) const buttonType = [ @@ -179,7 +181,7 @@ export function useMovementSetting(id) { FOLLOW_LINE_REF.current = followLine canvas.on('mouse:move', (event) => { - const mousePos = canvas.getPointer(event.e) + const mousePos = getIntersectMousePoint(event) if (followLine.x1 === followLine.x2) { followLine.left = mousePos.x - 2 } else { @@ -211,8 +213,8 @@ export function useMovementSetting(id) { if (!target) return const { top: targetTop, left: targetLeft } = target - const currentX = Big(canvas.getPointer(e.e).x).round(0, Big.roundUp) - const currentY = Big(canvas.getPointer(e.e).y).round(0, Big.roundUp) + const currentX = Big(getIntersectMousePoint(e).x).round(0, Big.roundUp) + const currentY = Big(getIntersectMousePoint(e).y).round(0, Big.roundUp) let value = '' if (target.y1 === target.y2) { value = Big(targetTop).minus(currentY).times(10) @@ -414,7 +416,12 @@ export function useMovementSetting(id) { startPoint: { x: currentLine.x1 + deltaX, y: currentLine.y1 + deltaY }, endPoint: { x: currentLine.x2 + deltaX, y: currentLine.y2 + deltaY }, }) - const currentSize = calcLinePlaneSize({ x1: currentLine.x1, y1: currentLine.y1, x2: currentLine.x2, y2: currentLine.y2 }) + const currentSize = calcLinePlaneSize({ + x1: currentLine.x1, + y1: currentLine.y1, + x2: currentLine.x2, + y2: currentLine.y2, + }) currentLine.attributes.planeSize = currentSize currentLine.attributes.actualSize = currentSize diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index ff134358..3b8924cf 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -92,6 +92,7 @@ export function useEvent() { const wheelEvent = (opt) => { const delta = opt.e.deltaY // 휠 이동 값 (양수면 축소, 음수면 확대) let zoom = canvas.getZoom() // 현재 줌 값 + // console.log('zoom', zoom, 'delta', delta) zoom += delta > 0 ? -0.1 : 0.1 @@ -100,9 +101,9 @@ export function useEvent() { if (zoom < 0.5) zoom = 0.5 setCanvasZoom(Number((zoom * 100).toFixed(0))) - const { x, y } = getIntersectMousePoint(opt) + // 마우스 위치 기준으로 확대/축소 - canvas.zoomToPoint(new fabric.Point(x, y), zoom) + canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom) canvas.getObjects().forEach((obj) => { obj.setCoords() @@ -176,6 +177,8 @@ export function useEvent() { outerLinePoints.push({ x: line.x1, y: line.y1 }) }) + const allAuxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine') + const adsorptionPoints = [ ...getAdsorptionPoints(), ...roofAdsorptionPoints.current, @@ -183,6 +186,18 @@ export function useEvent() { ...intersectionPoints.current, ...innerLinePoints, ...outerLinePoints, + ...allAuxiliaryLines.map((line) => { + return { + x: line.x1, + y: line.y1, + } + }), + ...allAuxiliaryLines.map((line) => { + return { + x: line.x2, + y: line.y2, + } + }), ] if (dotLineGridSetting.LINE || canvas.getObjects().filter((obj) => ['lineGrid', 'tempGrid'].includes(obj.name)).length > 1) { From 10d7a6476a49eff2e8febacc94eb55a4aa5b64cf Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Jul 2025 10:26:52 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=EB=90=9C=20=ED=9D=A1=EC=B0=A9=EC=A0=90=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 --- src/hooks/useEvent.js | 48 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 3b8924cf..8112bb36 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -132,7 +132,7 @@ export function useEvent() { const roofsPoints = roofs.map((roof) => roof.points).flat() roofAdsorptionPoints.current = [...roofsPoints] - const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed) + const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine') const otherAdsorptionPoints = [] auxiliaryLines.forEach((line1) => { @@ -179,7 +179,38 @@ export function useEvent() { const allAuxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine') - const adsorptionPoints = [ + allAuxiliaryLines.forEach((aux) => { + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + + roofs.forEach((roof) => { + //지붕과 보조선이 만나는 지점 + roof.lines.forEach((line) => { + const intersectionPoint = calculateIntersection(aux, line) + if (intersectionPoint) { + intersectionPoints.current.push(intersectionPoint) + } + }) + + //innerLines와 보조선이 만나는 지점 + roof.innerLines.forEach((line) => { + const intersectionPoint = calculateIntersection(aux, line) + if (intersectionPoint) { + intersectionPoints.current.push(intersectionPoint) + } + }) + }) + + // outerLines와의 교점 + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine) => { + const intersectionPoint = calculateIntersection(aux, outerLine) + if (intersectionPoint) { + intersectionPoints.current.push(intersectionPoint) + } + }) + }) + + let adsorptionPoints = [ ...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, @@ -200,6 +231,8 @@ export function useEvent() { }), ] + adsorptionPoints = removeDuplicatePoints(adsorptionPoints) + if (dotLineGridSetting.LINE || canvas.getObjects().filter((obj) => ['lineGrid', 'tempGrid'].includes(obj.name)).length > 1) { const closestLine = getClosestLineGrid(pointer) @@ -451,6 +484,17 @@ export function useEvent() { }) } + const removeDuplicatePoints = (points) => { + const map = new Map() + points.forEach((point) => { + const key = `${point.x},${point.y}` + if (!map.has(key)) { + map.set(key, point) + } + }) + return Array.from(map.values()) + } + return { addDocumentEventListener, addCanvasMouseEventListener, From ea34d469bbd252f6ed7ff769693ed5b69a1ec898 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Jul 2025 11:22:13 +0900 Subject: [PATCH 9/9] =?UTF-8?q?wall=20=EA=B8=B8=EC=9D=B4=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofShapeSetting.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 69defd9d..edf0e7b6 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -241,7 +241,11 @@ export function useRoofShapeSetting(id) { outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') let isValid = outerLines.every((line) => line.attributes?.isFixed) if (!isValid) { - swalFire({ text: getMessage('modal.canvas.setting.roofline.properties.setting.not.setting'), type: 'alert', icon: 'warning' }) + swalFire({ + text: getMessage('modal.canvas.setting.roofline.properties.setting.not.setting'), + type: 'alert', + icon: 'warning', + }) return } const pitch = outerLines.find((line) => line.attributes.type === LINE_TYPE.WALLLINE.SHED)?.attributes.pitch @@ -497,7 +501,13 @@ export function useRoofShapeSetting(id) { canvas.remove(obj) }) - const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction, originX: 'center', originY: 'center' }) + const polygon = addPolygonByLines(outerLines, { + name: POLYGON_TYPE.WALL, + direction, + originX: 'center', + originY: 'center', + }) + polygon.setViewLengthText(false) polygon.lines = [...outerLines] addPitchTextsByOuterLines()