From f77794ceeddd1124a899ed8f731fa1418b763ff8 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 18 Mar 2026 10:54:24 +0900 Subject: [PATCH] =?UTF-8?q?1525=20=20=EB=A9=B4=ED=98=95=EC=83=81=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EC=B9=98=EC=88=98=20=ED=91=9C=EA=B8=B0=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/surface/useSurfaceShapeBatch.js | 96 +++++++++++++++++++++-- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index 27a6f737..7d47b02e 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -9,14 +9,14 @@ import { globalPitchState, } from '@/store/canvasAtom' import { LINE_TYPE, MENU, POLYGON_TYPE } from '@/common/common' -import { getIntersectionPoint } from '@/util/canvas-util' +import { getIntersectionPoint, getDegreeByChon } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' import { useSwal } from '@/hooks/useSwal' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { usePopup } from '@/hooks/usePopup' -import { roofDisplaySelector } from '@/store/settingAtom' +import { roofDisplaySelector, basicSettingState } from '@/store/settingAtom' import { usePolygon } from '@/hooks/usePolygon' import { fontSelector } from '@/store/fontAtom' import { slopeSelector } from '@/store/commonAtom' @@ -30,10 +30,12 @@ import { useText } from '@/hooks/useText' import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting' import { v4 as uuidv4 } from 'uuid' import { useState } from 'react' +import { fabric } from 'fabric' export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { const { getMessage } = useMessage() const { drawDirectionArrow, addPolygon, addLengthText, setPolygonLinesActualSize } = usePolygon() + const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const resetOuterLinePoints = useResetRecoilState(outerLinePointsState) const resetPlacementShapeDrawingPoints = useResetRecoilState(placementShapeDrawingPointsState) @@ -92,9 +94,76 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { let obj = null let points = [] + // azimuth로부터 direction 결정 + let direction = 'south' + if (azimuth === 'left') direction = 'west' + else if (azimuth === 'right') direction = 'east' + else if (azimuth === 'up') direction = 'north' + + // after:render 콜백 - 매 렌더링 후 temp 수치 텍스트를 ctx에 직접 그림 + let tempLengthData = null // { transformedPoints, direction } + const afterRenderHandler = () => { + if (!tempLengthData) return + const ctx = canvas?.getContext('2d') + if (!ctx) return + + const { tPoints, dir } = tempLengthData + const vpt = canvas.viewportTransform + const zoom = vpt[0] + const oX = vpt[4] + const oY = vpt[5] + + ctx.save() + ctx.font = `bold ${(lengthTextFont.fontSize.value || 14) * zoom}px sans-serif` + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + + tPoints.forEach((start, i) => { + const end = tPoints[(i + 1) % tPoints.length] + const dx = end.x - start.x + const dy = end.y - start.y + const planeSize = Math.round(Math.sqrt(dx * dx + dy * dy) * 10) + + let actualSize = planeSize + const lineDir = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 'right' : 'left') : (dy > 0 ? 'bottom' : 'top') + if (+roofSizeSet === 1 && globalPitch > 0) { + const pitchDeg = getDegreeByChon(globalPitch) + const isV = Math.abs(start.x - end.x) < 1 + const isH = Math.abs(start.y - end.y) < 1 + const isD = !isH && !isV + if (dir === 'south' || dir === 'north') { + if (isV) actualSize = Math.round(planeSize / Math.cos(pitchDeg * Math.PI / 180)) + else if (isD) { + const h = Math.abs(dy) * 10 * Math.tan(pitchDeg * Math.PI / 180) + actualSize = Math.round(Math.sqrt(h * h + planeSize * planeSize)) + } + } else if (dir === 'west' || dir === 'east') { + if (isH) actualSize = Math.round(planeSize / Math.cos(pitchDeg * Math.PI / 180)) + else if (isD) { + const h = Math.abs(dx) * 10 * Math.tan(pitchDeg * Math.PI / 180) + actualSize = Math.round(Math.sqrt(h * h + planeSize * planeSize)) + } + } + } + + const displayValue = (+roofSizeSet === 1 ? actualSize : planeSize).toString() + let tx = (start.x + end.x) / 2 + let ty = (start.y + end.y) / 2 + if (lineDir === 'right') ty += 15 + else if (lineDir === 'left') ty -= 15 + else if (lineDir === 'top') tx += 15 + else tx -= 25 + + ctx.fillText(displayValue, tx * zoom + oX, ty * zoom + oY) + }) + ctx.restore() + } + //일단 팝업을 가린다 if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) { + canvas?.on('after:render', afterRenderHandler) addCanvasMouseEventListener('mouse:move', (e) => { if (!isDrawing) { return @@ -125,7 +194,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { pitch: globalPitch, } - obj = new QPolygon(points, options) + obj = new fabric.Polygon(points, options) let imageRotate = 0 if (xInversion && !yInversion) { @@ -159,12 +228,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { obj.setCoords() //좌표 변경 적용 canvas?.add(obj) + + // 회전/반전이 적용된 실제 좌표를 계산하여 after:render에서 그릴 데이터 설정 + const matrix = obj.calcTransformMatrix() + const tPoints = obj.get('points').map((p) => { + const pt = new fabric.Point(p.x - obj.pathOffset.x, p.y - obj.pathOffset.y) + return fabric.util.transformPoint(pt, matrix) + }) + tempLengthData = { tPoints, dir: direction } + canvas?.renderAll() }) addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false + // after:render 핸들러 해제 및 temp 수치 데이터 초기화 + tempLengthData = null + canvas?.off('after:render', afterRenderHandler) + const { xInversion, yInversion } = surfaceRefs canvas?.remove(obj) @@ -186,8 +268,12 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { } //회전, flip등이 먹은 기준으로 새로생성 - // const batchSurface = addPolygon(reorderedPoints, { - const batchSurface = addPolygon(obj.getCurrentPoints(), { + const objMatrix = obj.calcTransformMatrix() + const finalPoints = obj.get('points').map((p) => { + const pt = new fabric.Point(p.x - obj.pathOffset.x, p.y - obj.pathOffset.y) + return fabric.util.transformPoint(pt, objMatrix) + }) + const batchSurface = addPolygon(finalPoints, { fill: 'transparent', stroke: 'red', strokeWidth: 3, -- 2.47.2