From 82698a5d031117dcdbd9a95fe674551cf3e623a3 Mon Sep 17 00:00:00 2001 From: ysCha Date: Thu, 23 Oct 2025 18:34:11 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=EB=8F=99,=ED=98=84=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=8F=99=EC=9D=BC=EB=9D=BC=EC=9D=B8=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20=EC=83=81=EC=9A=B0=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=8B=9C=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/skeleton-utils.js | 281 ++++++++++++++++++++++++++----------- 1 file changed, 198 insertions(+), 83 deletions(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 34d8153e..1132e9da 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -31,7 +31,8 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { const startPoint = selectLine.startPoint const endPoint = selectLine.endPoint - const oldPoints = canvas?.movePoints?.points ?? roof.points + const orgPoints = roof.points; // orgPoint를 orgPoints로 변경 + const oldPoints = canvas?.skeleton.lastPoints ?? orgPoints // 여기도 변경 const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints); if (oppositeLine) { @@ -40,50 +41,80 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { console.log('No opposite line found'); } - return oldPoints.map((point) => { + + const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) + const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) + + let baseLinePoints = []; + const pointSet = new Set(); +/* + walls.forEach((wall) => { + if (wall.baseLines.length === 0) { + wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id) + } + + // Extract points from each baseLine + wall.baseLines.forEach(line => { + console.log("useSk:::", line.x1, line.y1, line.x2, line.y2); +// 시작점과 끝점을 배열에 추가 + const points = [ + { x: line.x1, y: line.y1 }, + { x: line.x2, y: line.y2 } + ]; + + points.forEach(point => { + const key = `${point.x},${point.y}`; + if (!pointSet.has(key)) { + pointSet.add(key); + baseLinePoints.push(point); + } + }); + }); + + }) + return [...baseLinePoints]; +*/ + + return oldPoints.map((point, index) => { + + console.log('oldPoint:', point); + const originalPoint = orgPoints[index]; // orgPoint를 originalPoint로 변경 + console.log('originalPoint:', originalPoint); const newPoint = { ...point }; - const absMove = Big(moveFlowLine).abs().times(2).div(10); + const absMove = Big(moveFlowLine).times(2).div(10); //console.log('absMove:', absMove); const skeletonLines = canvas.skeletonLines; - console.log('skeleton line:', canvas.skeletonLines); - const changeSkeletonLine = (canvas, oldPoint, newPoint, str) => { - for (const line of canvas.skeletonLines) { - if (str === 'start' && isSamePoint(line.startPoint, oldPoint)) { - // Fabric.js 객체의 set 메서드로 속성 업데이트 - line.set({ - x1: newPoint.x, - y1: newPoint.y, - x2: line.x2 || line.endPoint?.x, - y2: line.y2 || line.endPoint?.y - }); - line.startPoint = newPoint; // 참조 업데이트 - } - else if (str === 'end' && isSamePoint(line.endPoint, oldPoint)) { - line.set({ - x1: line.x1 || line.startPoint?.x, - y1: line.y1 || line.startPoint?.y, - x2: newPoint.x, - y2: newPoint.y - }); - line.endPoint = newPoint; // 참조 업데이트 - } - } - canvas.requestRenderAll(); - console.log('skeleton line:', canvas.skeletonLines); - } + //console.log('skeleton line:', canvas.skeletonLines); + // const changeSkeletonLine = (canvas, oldPoint, newPoint, str) => { + // for (const line of canvas.skeletonLines) { + // if (str === 'start' && isSamePoint(line.startPoint, oldPoint)) { + // // Fabric.js 객체의 set 메서드로 속성 업데이트 + // line.set({ + // x1: newPoint.x, + // y1: newPoint.y, + // x2: line.x2 || line.endPoint?.x, + // y2: line.y2 || line.endPoint?.y + // }); + // line.startPoint = newPoint; // 참조 업데이트 + // } + // else if (str === 'end' && isSamePoint(line.endPoint, oldPoint)) { + // line.set({ + // x1: line.x1 || line.startPoint?.x, + // y1: line.y1 || line.startPoint?.y, + // x2: newPoint.x, + // y2: newPoint.y + // }); + // line.endPoint = newPoint; // 참조 업데이트 + // } + // } + // canvas.requestRenderAll(); + // console.log('skeleton line:', canvas.skeletonLines); + // } - if(moveFlowLine > 0) { - if(moveDirection === 'down'){ - moveDirection = 'up'; - }else if(moveDirection === 'left'){ - moveDirection = 'right'; - } - } - console.log('skeletonBuilder moveDirection:', moveDirection); switch (moveDirection) { @@ -92,14 +123,22 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { for (const line of oppositeLine) { if (line.position === 'left') { if (isSamePoint(newPoint, line.start)) { - newPoint.x = Big(line.start.x).minus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.start, newPoint, 'start') + newPoint.x = Big(line.start.x).plus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { - newPoint.x = Big(line.end.x).minus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.end, newPoint, 'end') + newPoint.x = Big(line.end.x).plus(absMove).toNumber(); } - break + + break; + // } else if (line.position === 'right') { + // if (isSamePoint(newPoint, line.start)) { + // newPoint.x = Big(line.start.x).minus(absMove).toNumber(); + // (newPoint.x < originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용 + // }else if(isSamePoint(newPoint, line.end)) { + // newPoint.x = Big(line.end.x).minus(absMove).toNumber(); + // (newPoint.x < originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용 + // } } + } break; @@ -107,29 +146,46 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { for (const line of oppositeLine) { if (line.position === 'right') { if (isSamePoint(newPoint, line.start)) { - newPoint.x = Big(line.start.x).plus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.start, newPoint, 'start') + newPoint.x = Big(line.start.x).minus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { - newPoint.x = Big(line.end.x).plus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.end, newPoint, 'end') + newPoint.x = Big(line.end.x).minus(absMove).toNumber(); } break + // }else if(line.position === 'left') { + // if (isSamePoint(newPoint, line.start)) { + // newPoint.x = Big(line.start.x).plus(absMove).toNumber(); + // (newPoint.x > originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용 + // } else if (isSamePoint(newPoint, line.end)) { + // newPoint.x= Big(line.end.x).plus(absMove).toNumber(); + // (newPoint.x > originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용 + // } } + } break; case 'up': // Move up: decrease Y (toward top of screen) + for (const line of oppositeLine) { if (line.position === 'top') { if (isSamePoint(newPoint, line.start)) { - newPoint.y = Big(line.start.y).minus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.start, newPoint, 'start') + newPoint.y = Big(line.start.y).plus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { - newPoint.y = Big(line.end.y).minus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.end, newPoint, 'end') + newPoint.y = Big(line.end.y).plus(absMove).toNumber(); } - break + break; + + // }else if(line.position === 'bottom') { + // if(newPoint.y !== originalPoint.y) { + // if (isSamePoint(newPoint, line.start)) { + // newPoint.y = Big(line.start.y).minus(absMove).toNumber(); + // (newPoint.y < originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용 + // }else if(isSamePoint(newPoint, line.end)) { + // newPoint.y = Big(line.end.y).minus(absMove).toNumber(); + // (newPoint.y < originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용 + // } + // } } } @@ -137,23 +193,38 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { case 'down': // Move down: increase Y (toward bottom of screen) for (const line of oppositeLine) { - if (line.position === 'bottom') { - if (isSamePoint(newPoint, line.start)) { - newPoint.y = Big(line.start.y).plus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.start, newPoint, 'start') + if (line.position === 'bottom') { + + if (isSamePoint(newPoint, line.start)) { + newPoint.y = Big(line.start.y).minus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { - newPoint.y = Big(line.end.y).plus(absMove).toNumber(); - //changeSkeletonLine(canvas, line.end, newPoint, 'end') + newPoint.y = Big(line.end.y).minus(absMove).toNumber(); } - break + + // }else if(line.position === 'top') { + // + // if(newPoint.y !== originalPoint.y) { + // + // if (isSamePoint(newPoint, line.start)) { + // newPoint.y = Big(line.start.y).plus(absMove).toNumber(); + // (newPoint.y > originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용 + // } else if (isSamePoint(newPoint, line.end)) { + // newPoint.y = Big(line.end.y).plus(absMove).toNumber(); + // (newPoint.y > originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용 + // } + // } + break; } + } break; } + console.log('newPoint:', newPoint); return newPoint; }) + } /** @@ -168,18 +239,13 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { //처마 let roof = canvas?.getObjects().find((object) => object.id === roofId) - //벽 - const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - // const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) - // if (hasNonParallelLines.length > 0) { - // return - // } const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE] const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD] /** 외벽선 */ + const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) //const skeletonLines = []; @@ -197,22 +263,20 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { let points = roof.points; + + //마루이동 if (moveFlowLine !== 0) { - points = movingRidgeFromSkeleton(roofId, canvas) - const movePoints = { - points: points, - roofId: roofId, - } - canvas.set("movePoints", movePoints) + + points = movingRidgeFromSkeleton(roofId, canvas) } //처마 if(moveUpDown !== 0) { } - + console.log('points:', points); const geoJSONPolygon = toGeoJSON(points) try { @@ -232,7 +296,6 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { canvas.skeletonStates[roofId] = true canvas.skeletonLines = []; canvas.skeletonLines.push(...roof.innerLines) - canvas.set("skeletonLines", canvas.skeletonLines) const cleanSkeleton = { Edges: skeleton.Edges.map(edge => ({ @@ -249,9 +312,12 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { }; canvas.skeleton = []; canvas.skeleton = cleanSkeleton - + canvas.skeleton.lastPoints = points canvas.set("skeleton", cleanSkeleton); + + + canvas.renderAll() } catch (e) { console.error('스켈레톤 생성 중 오류 발생:', e) @@ -397,6 +463,23 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { canvas.add(innerLine); innerLine.bringToFront(); existingLines.add(lineKey); // 추가된 라인을 추적 + }else{ + const coordinateText = new fabric.Text(`(${Math.round(p1.x)}, ${Math.round(p1.y)})`, { + left: p1.x + 5, // 좌표점에서 약간 오른쪽으로 이동 + top: p1.y - 20, // 좌표점에서 약간 위로 이동 + fontSize: 13, + fill: 'red', + fontFamily: 'Arial', + selectable: true, + lockMovementX: false, + lockMovementY: false, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'lengthText' + }) + + canvas?.add(coordinateText) } innerLines.push(innerLine) canvas.renderAll(); @@ -439,7 +522,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { //외벽선 밖으로 나간 선을 정리한다(roof.line의 교점까지 정리한다) // 지붕 경계선과 교차 확인 및 클리핑 const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines); - console.log('clipped line', clippedLine.p1, clippedLine.p2); + //console.log('clipped line', clippedLine.p1, clippedLine.p2); const isOuterLine = isOuterEdge(p1, p2, [edgeResult.Edge]) addRawLine(roof.id, skeletonLines, p1, p2, 'ridge', '#FF0000', 3, pitch, isOuterLine); // } @@ -627,6 +710,7 @@ const preprocessPolygonCoordinates = (initialPoints) => { if (coordinates.length > 1 && coordinates[0][0] === coordinates[coordinates.length - 1][0] && coordinates[0][1] === coordinates[coordinates.length - 1][1]) { coordinates.pop(); } + return coordinates.reverse(); }; @@ -1280,14 +1364,28 @@ function getLinePosition(line, referenceLine) { const refMidX = (referenceLine.start.x + referenceLine.end.x) / 2; const refMidY = (referenceLine.start.y + referenceLine.end.y) / 2; - // Y축 차이가 더 크면 위/아래로 판단 - // Y축 차이가 더 크면 위/아래로 판단 - if (Math.abs(lineMidY - refMidY) > Math.abs(lineMidX - refMidX)) { - return lineMidY > refMidY ? 'bottom' : 'top'; - } - // X축 차이가 더 크면 왼쪽/오른쪽으로 판단 - else { - return lineMidX > refMidX ? 'right' : 'left'; + // 참조선에 대한 벡터를 계산하여 법선 벡터 구하기 + const refVecX = referenceLine.end.x - referenceLine.start.x; + const refVecY = referenceLine.end.y - referenceLine.start.y; + + // 법선 벡터 (참조선에 수직) - 방향을 수정 + const normalX = refVecY; // -refVecY에서 refVecY로 변경 + const normalY = -refVecX; // refVecX에서 -refVecX로 변경 + + // 중점에서 중점으로의 벡터 + const midVecX = lineMidX - refMidX; + const midVecY = lineMidY - refMidY; + + // 내적을 이용해 위치 판단 + const dotProduct = midVecX * normalX + midVecY * normalY; + + // 참조선의 방향에 따라 위치 결정 + if (Math.abs(refVecX) > Math.abs(refVecY)) { + // 수평에 가까운 선분인 경우 + return dotProduct > 0 ? 'top' : 'bottom'; + } else { + // 수직에 가까운 선분인 경우 + return dotProduct > 0 ? 'right' : 'left'; } } @@ -1396,5 +1494,22 @@ function clipLineToRoofBoundary(p1, p2, roofLines) { // 교차점이 없으면 원래 선분 반환 return { p1: originalP1, p2: originalP2 }; } +export const convertBaseLinesToPoints = (baseLines) => { + const points = []; + const pointSet = new Set(); -// 기존 getLineIntersection 함수를 사용하거나, 없으면 아래 구현 사용 + baseLines.forEach((line) => { + [ + { x: line.x1, y: line.y1 }, + { x: line.x2, y: line.y2 } + ].forEach(point => { + const key = `${point.x},${point.y}`; + if (!pointSet.has(key)) { + pointSet.add(key); + points.push(point); + } + }); + }); + + return points; +}; \ No newline at end of file From 0ad18e4f154c4e39ba8ecd34ff5a6162f4ca0e23 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 24 Oct 2025 13:04:56 +0900 Subject: [PATCH 2/6] =?UTF-8?q?roof.moveFlowLine=20=EC=88=98=EC=A0=95,=20l?= =?UTF-8?q?og=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/useMovementSetting.js | 6 ++++-- src/hooks/useContextMenu.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 5e801912..5857707b 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -102,7 +102,7 @@ export function useMovementSetting(id) { /** outerLines 속성처리*/ const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - outerLines.forEach((line) => line.set({ visible: false })) + outerLines.forEach((line) => line.set({ visible: true })) canvas.renderAll() }, [type]) @@ -329,7 +329,9 @@ export function useMovementSetting(id) { const roof = canvas.getObjects().find((obj) => obj.id === roofId) // 현이동, 동이동 추가 - const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? FLOW_LINE_REF.POINTER_INPUT_REF.current.value : 0 + let pointValue = FLOW_LINE_REF.POINTER_INPUT_REF.current.value; + let filledValue = FLOW_LINE_REF.FILLED_INPUT_REF.current.value; + const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? (pointValue===''?filledValue:pointValue) : 0 const moveUpDown = typeRef.current === TYPE.UP_DOWN ? UP_DOWN_REF.POINTER_INPUT_REF.current.value : 0 roof.moveFlowLine = parseInt(moveFlowLine, 10) || 0; roof.moveUpDown = parseInt(moveUpDown, 10) || 0; diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 4580f3ee..46ced154 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -123,7 +123,7 @@ export function useContextMenu() { }, [currentContextMenu]) useEffect(() => { - console.log('currentObject', currentObject) + //console.log('currentObject', currentObject) if (currentObject?.name) { switch (currentObject.name) { case 'triangleDormer': From affef782f35532038bfc2b629850740e0f79904a Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 24 Oct 2025 18:36:38 +0900 Subject: [PATCH 3/6] =?UTF-8?q?roof.moveFlowLine=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useMovementSetting.js | 20 +- src/util/skeleton-utils.js | 338 +++++++++++++++------- 2 files changed, 245 insertions(+), 113 deletions(-) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 5857707b..20485b73 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -102,7 +102,7 @@ export function useMovementSetting(id) { /** outerLines 속성처리*/ const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - outerLines.forEach((line) => line.set({ visible: true })) + outerLines.forEach((line) => line.set({ visible: false })) canvas.renderAll() }, [type]) @@ -321,6 +321,12 @@ export function useMovementSetting(id) { FOLLOW_LINE_REF.current = null canvas.renderAll() } + if (UP_DOWN_REF.current !== null) { + canvas.remove(UP_DOWN_REF.current) + UP_DOWN_REF.current = null + canvas.renderAll() + } + const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target if (!target) return @@ -329,10 +335,14 @@ export function useMovementSetting(id) { const roof = canvas.getObjects().find((obj) => obj.id === roofId) // 현이동, 동이동 추가 - let pointValue = FLOW_LINE_REF.POINTER_INPUT_REF.current.value; - let filledValue = FLOW_LINE_REF.FILLED_INPUT_REF.current.value; - const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? (pointValue===''?filledValue:pointValue) : 0 - const moveUpDown = typeRef.current === TYPE.UP_DOWN ? UP_DOWN_REF.POINTER_INPUT_REF.current.value : 0 + let flPointValue = FLOW_LINE_REF.POINTER_INPUT_REF.current?.value??0; + let flFilledValue = FLOW_LINE_REF.FILLED_INPUT_REF.current?.value??0; + flPointValue = (flFilledValue > 0 || flFilledValue < 0)? flFilledValue : flPointValue; + const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? flPointValue : 0 + let udPointValue = UP_DOWN_REF.POINTER_INPUT_REF.current?.value??0; + let udFilledValue = UP_DOWN_REF.FILLED_INPUT_REF.current?.value??0; + udPointValue = udFilledValue > 0 ? udFilledValue : udPointValue; + const moveUpDown = typeRef.current === TYPE.UP_DOWN ? udPointValue: 0 roof.moveFlowLine = parseInt(moveFlowLine, 10) || 0; roof.moveUpDown = parseInt(moveUpDown, 10) || 0; roof.moveDirect = ""; diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 1132e9da..d7168e74 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -5,6 +5,7 @@ import { QLine } from '@/components/fabric/QLine' import { getDegreeByChon } from '@/util/canvas-util' import Big from 'big.js' import { line } from 'framer-motion/m' +import { QPolygon } from '@/components/fabric/QPolygon' /** * 지붕 폴리곤의 스켈레톤(중심선)을 생성하고 캔버스에 그립니다. @@ -25,61 +26,62 @@ export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => { const movingRidgeFromSkeleton = (roofId, canvas) => { let roof = canvas?.getObjects().find((object) => object.id === roofId) + let moveDirection = roof.moveDirect; let moveFlowLine = roof.moveFlowLine??0; const selectLine = roof.moveSelectLine; const startPoint = selectLine.startPoint const endPoint = selectLine.endPoint - const orgPoints = roof.points; // orgPoint를 orgPoints로 변경 - const oldPoints = canvas?.skeleton.lastPoints ?? orgPoints // 여기도 변경 + const orgRoofPoints = roof.points; // orgPoint를 orgPoints로 변경 + const oldPoints = canvas?.skeleton.lastPoints ?? orgRoofPoints // 여기도 변경 const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints); + const skeletonPolygon = canvas.getObjects().filter((object) => object.skeletonType === 'polygon' && object.parentId === roofId) + const skeletonLines = canvas.getObjects().filter((object) => object.skeletonType === 'line' && object.parentId === roofId) + if (oppositeLine) { console.log('Opposite line found:', oppositeLine); } else { console.log('No opposite line found'); } + let baseLines = canvas.getObjects().filter((object) => object.name === 'baseLine' && object.parentId === roofId) || []; + console.log('baseLines::::', baseLines); + let baseLinePoints = baseLines.map((line) => ({x:line.x1, y:line.y1})); - const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) - let baseLinePoints = []; - const pointSet = new Set(); -/* - walls.forEach((wall) => { - if (wall.baseLines.length === 0) { - wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id) - } - // Extract points from each baseLine - wall.baseLines.forEach(line => { - console.log("useSk:::", line.x1, line.y1, line.x2, line.y2); -// 시작점과 끝점을 배열에 추가 - const points = [ - { x: line.x1, y: line.y1 }, - { x: line.x2, y: line.y2 } - ]; + /* + walls.forEach((wall) => { + if (wall.baseLines.length === 0) { + wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id) + } - points.forEach(point => { - const key = `${point.x},${point.y}`; - if (!pointSet.has(key)) { - pointSet.add(key); - baseLinePoints.push(point); - } + // Extract points from each baseLine + wall.baseLines.forEach(line => { + console.log("useSk:::", line.x1, line.y1, line.x2, line.y2); + // 시작점과 끝점을 배열에 추가 + const points = [ + { x: line.x1, y: line.y1 }, + { x: line.x2, y: line.y2 } + ]; + + points.forEach(point => { + const key = `${point.x},${point.y}`; + if (!pointSet.has(key)) { + pointSet.add(key); + baseLinePoints.push(point); + } + }); }); - }); - }) - return [...baseLinePoints]; -*/ + }) + return [...baseLinePoints]; + */ return oldPoints.map((point, index) => { - console.log('oldPoint:', point); - const originalPoint = orgPoints[index]; // orgPoint를 originalPoint로 변경 - console.log('originalPoint:', originalPoint); const newPoint = { ...point }; const absMove = Big(moveFlowLine).times(2).div(10); //console.log('absMove:', absMove); @@ -170,9 +172,9 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { for (const line of oppositeLine) { if (line.position === 'top') { if (isSamePoint(newPoint, line.start)) { - newPoint.y = Big(line.start.y).plus(absMove).toNumber(); + newPoint.y = Big(line.start.y).minus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { - newPoint.y = Big(line.end.y).plus(absMove).toNumber(); + newPoint.y = Big(line.end.y).minus(absMove).toNumber(); } break; @@ -196,6 +198,8 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { if (line.position === 'bottom') { + console.log('oldPoint:', point); + if (isSamePoint(newPoint, line.start)) { newPoint.y = Big(line.start.y).minus(absMove).toNumber(); } else if (isSamePoint(newPoint, line.end)) { @@ -221,7 +225,9 @@ const movingRidgeFromSkeleton = (roofId, canvas) => { break; } + console.log('newPoint:', newPoint); + //baseline 변경 return newPoint; }) @@ -246,7 +252,16 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { /** 외벽선 */ const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) + //const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0) + + const baseLines = canvas.getObjects().filter((object) => object.name === 'baseLine' && object.parentId === roofId) || []; + const baseLinePoints = baseLines.map((line) => ({x:line.left, y:line.top})); + + const outerLines = canvas.getObjects().filter((object) => object.name === 'outerLinePoint') || []; + const outerLinePoints = outerLines.map((line) => ({x:line.left, y:line.top})) + + const hipLines = canvas.getObjects().filter((object) => object.name === 'hip' && object.parentId === roofId) || []; + const ridgeLines = canvas.getObjects().filter((object) => object.name === 'ridge' && object.parentId === roofId) || []; //const skeletonLines = []; // 1. 지붕 폴리곤 좌표 전처리 @@ -271,6 +286,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { points = movingRidgeFromSkeleton(roofId, canvas) + + } //처마 if(moveUpDown !== 0) { @@ -314,11 +331,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { canvas.skeleton = cleanSkeleton canvas.skeleton.lastPoints = points canvas.set("skeleton", cleanSkeleton); - - - - canvas.renderAll() + console.log('skeleton rendered.', canvas); } catch (e) { console.error('스켈레톤 생성 중 오류 발생:', e) if (canvas.skeletonStates) { @@ -347,23 +361,8 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { // 1. 모든 Edge를 순회하며 기본 스켈레톤 선(용마루)을 수집합니다. skeleton.Edges.forEach((edgeResult, index) => { - // const { Begin, End } = edgeResult.Edge; - // let outerLine = roof.lines.find(line => - // line.attributes.type === 'eaves' && isSameLine(Begin.X, Begin.Y, End.X, End.Y, line) - // ); - // if(!outerLine){ - // - // for (const line of canvas.skeletonLines) { - // if (line.lineName === 'hip' && line.attributes.hipIndex === index) - // { - // outerLine = line; - // break; // Found the matching line, exit the loop - // } - // } - // - // } - // const pitch = outerLine.attributes?.pitch??0 - // console.log("pitch", pitch) + + processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines); }); @@ -431,6 +430,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { const innerLines = []; const existingLines = new Set(); // 이미 추가된 라인을 추적하기 위한 Set + skeletonLines.forEach(line => { const { p1, p2, attributes, lineStyle } = line; @@ -445,6 +445,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { return; // 이미 있는 라인이면 스킵 } + const direction = getLineDirection( + { x: line.p1.x, y: line.p1.y }, + { x: line.p2.x, y: line.p2.y } + ); + const innerLine = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId: roof.id, fontSize: roof.fontSize, @@ -452,10 +457,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { strokeWidth: lineStyle.width, name: (line.attributes.isOuterEdge)?'eaves': attributes.type, attributes: attributes, + direction: direction, isBaseLine: line.attributes.isOuterEdge, lineName: (line.attributes.isOuterEdge)?'outerLine': attributes.type, selectable:(!line.attributes.isOuterEdge), - roofId: roofId + roofId: roofId, }); //skeleton 라인에서 처마선은 삭제 @@ -504,6 +510,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { const { Begin, End } = edgeResult.Edge; let outerLine = roof.lines.find(line => line.attributes.type === 'eaves' && isSameLine(Begin.X, Begin.Y, End.X, End.Y, line) + ); if(!outerLine) { outerLine = findMatchingLine(edgeResult.Polygon, roof, roof.points); @@ -512,6 +519,25 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { let pitch = outerLine?.attributes?.pitch??0 + const convertedPolygon = edgeResult.Polygon?.map(point => ({ + x: typeof point.X === 'number' ? parseFloat(point.X) : 0, + y: typeof point.Y === 'number' ? parseFloat(point.Y) : 0 + })).filter(point => point.x !== 0 || point.y !== 0) || []; + + if (convertedPolygon.length > 0) { + const skeletonPolygon = new QPolygon(convertedPolygon, { + type: POLYGON_TYPE.ROOF, + fill: false, + stroke: 'blue', + strokeWidth: 8, + skeletonType: 'polygon', + polygonName: '', + parentId: roof.id, + }); + //canvas?.add(skeletonPolygon) + //canvas.renderAll() + } + let eavesLines = [] for (let i = 0; i < polygonPoints.length; i++) { const p1 = polygonPoints[i]; @@ -523,8 +549,8 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { // 지붕 경계선과 교차 확인 및 클리핑 const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines); //console.log('clipped line', clippedLine.p1, clippedLine.p2); - const isOuterLine = isOuterEdge(p1, p2, [edgeResult.Edge]) - addRawLine(roof.id, skeletonLines, p1, p2, 'ridge', '#FF0000', 3, pitch, isOuterLine); + const isOuterLine = isOuterEdge(clippedLine.p1, clippedLine.p2, [edgeResult.Edge]) + addRawLine(roof.id, skeletonLines, clippedLine.p1, clippedLine.p2, 'ridge', 'red', 5, pitch, isOuterLine); // } } } @@ -1359,33 +1385,29 @@ function findOppositeLine(edges, startPoint, endPoint, points) { } function getLinePosition(line, referenceLine) { + // 대상선의 중점 const lineMidX = (line.start.x + line.end.x) / 2; const lineMidY = (line.start.y + line.end.y) / 2; + + // 참조선의 중점 const refMidX = (referenceLine.start.x + referenceLine.end.x) / 2; const refMidY = (referenceLine.start.y + referenceLine.end.y) / 2; - - // 참조선에 대한 벡터를 계산하여 법선 벡터 구하기 - const refVecX = referenceLine.end.x - referenceLine.start.x; - const refVecY = referenceLine.end.y - referenceLine.start.y; - // 법선 벡터 (참조선에 수직) - 방향을 수정 - const normalX = refVecY; // -refVecY에서 refVecY로 변경 - const normalY = -refVecX; // refVecX에서 -refVecX로 변경 + // 단순히 좌표 차이로 판단 + const deltaX = lineMidX - refMidX; + const deltaY = lineMidY - refMidY; - // 중점에서 중점으로의 벡터 - const midVecX = lineMidX - refMidX; - const midVecY = lineMidY - refMidY; + // 참조선의 기울기 + const refDeltaX = referenceLine.end.x - referenceLine.start.x; + const refDeltaY = referenceLine.end.y - referenceLine.start.y; - // 내적을 이용해 위치 판단 - const dotProduct = midVecX * normalX + midVecY * normalY; - - // 참조선의 방향에 따라 위치 결정 - if (Math.abs(refVecX) > Math.abs(refVecY)) { - // 수평에 가까운 선분인 경우 - return dotProduct > 0 ? 'top' : 'bottom'; + // 참조선이 더 수평인지 수직인지 판단 + if (Math.abs(refDeltaX) > Math.abs(refDeltaY)) { + // 수평선에 가까운 경우 - Y 좌표로 판단 + return deltaY > 0 ? 'bottom' : 'top'; } else { - // 수직에 가까운 선분인 경우 - return dotProduct > 0 ? 'right' : 'left'; + // 수직선에 가까운 경우 - X 좌표로 판단 + return deltaX > 0 ? 'right' : 'left'; } } @@ -1445,54 +1467,142 @@ function findPolygonsContainingLine(edges, p1, p2) { } /** - * roof.lines와 교차하는 선분(p1, p2)을 찾아 교차점에서 자릅니다. + * roof.lines로 만들어진 다각형 내부에만 선분이 존재하도록 클리핑합니다. * @param {Object} p1 - 선분의 시작점 {x, y} * @param {Object} p2 - 선분의 끝점 {x, y} * @param {Array} roofLines - 지붕 경계선 배열 (QLine 객체의 배열) - * @returns {Object} {p1: {x, y}, p2: {x, y}} - 교차점에서 자른 선분 또는 원래 선분 + * @returns {Object} {p1: {x, y}, p2: {x, y}} - 다각형 내부로 클리핑된 선분 */ function clipLineToRoofBoundary(p1, p2, roofLines) { - if (!roofLines || !roofLines.length) return { p1, p2 }; + if (!roofLines || !roofLines.length) { + return { p1: { ...p1 }, p2: { ...p2 } }; + } - let closestIntersection = null; - let minDistSq = Infinity; - const originalP1 = { ...p1 }; - const originalP2 = { ...p2 }; + // 기본값으로 원본 좌표 설정 + let clippedP1 = { x: p1.x, y: p1.y }; + let clippedP2 = { x: p2.x, y: p2.y }; - // 모든 지붕 경계선과의 교차점을 찾음 + // p1이 다각형 내부에 있는지 확인 + const p1Inside = isPointInsidePolygon(p1, roofLines); + + // p2가 다각형 내부에 있는지 확인 + const p2Inside = isPointInsidePolygon(p2, roofLines); + + console.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside); + + // 두 점 모두 내부에 있으면 그대로 반환 + if (p1Inside && p2Inside) { + return { p1: clippedP1, p2: clippedP2 }; + } + + // 선분과 다각형 경계선의 교차점들을 찾음 + const intersections = []; + for (const line of roofLines) { const lineP1 = { x: line.x1, y: line.y1 }; const lineP2 = { x: line.x2, y: line.y2 }; - const intersection = getLineIntersection( - p1, p2, - lineP1, lineP2 - ); + const intersection = getLineIntersection(p1, p2, lineP1, lineP2); if (intersection) { - // 교차점과 p1 사이의 거리 계산 - const dx = intersection.x - p1.x; - const dy = intersection.y - p1.y; - const distSq = dx * dx + dy * dy; - - // p1에 가장 가까운 교차점 찾기 - if (distSq < minDistSq) { - minDistSq = distSq; - closestIntersection = intersection; + // 교차점이 선분 위에 있는지 확인 + const t = getParameterT(p1, p2, intersection); + if (t >= 0 && t <= 1) { + intersections.push({ + point: intersection, + t: t + }); } } } - // 교차점이 있으면 p2를 가장 가까운 교차점으로 업데이트 - if (closestIntersection) { - return { - p1: originalP1, - p2: closestIntersection - }; + console.log('Found intersections:', intersections.length); + + // 교차점들을 t 값으로 정렬 + intersections.sort((a, b) => a.t - b.t); + + if (!p1Inside && !p2Inside) { + // 두 점 모두 외부에 있는 경우 + if (intersections.length >= 2) { + console.log('Both outside, using intersection points'); + clippedP1.x = intersections[0].point.x; + clippedP1.y = intersections[0].point.y; + clippedP2.x = intersections[1].point.x; + clippedP2.y = intersections[1].point.y; + } else { + console.log('Both outside, no valid intersections - returning original'); + // 교차점이 충분하지 않으면 원본 반환 + return { p1: clippedP1, p2: clippedP2 }; + } + } else if (!p1Inside && p2Inside) { + // p1이 외부, p2가 내부 + if (intersections.length > 0) { + console.log('p1 outside, p2 inside - moving p1 to intersection'); + clippedP1.x = intersections[0].point.x; + clippedP1.y = intersections[0].point.y; + // p2는 이미 내부에 있으므로 원본 유지 + clippedP2.x = p2.x; + clippedP2.y = p2.y; + } + } else if (p1Inside && !p2Inside) { + // p1이 내부, p2가 외부 + if (intersections.length > 0) { + console.log('p1 inside, p2 outside - moving p2 to intersection'); + // p1은 이미 내부에 있으므로 원본 유지 + clippedP1.x = p1.x; + clippedP1.y = p1.y; + clippedP2.x = intersections[0].point.x; + clippedP2.y = intersections[0].point.y; + } } - // 교차점이 없으면 원래 선분 반환 - return { p1: originalP1, p2: originalP2 }; + return { p1: clippedP1, p2: clippedP2 }; +} + +/** + * 점이 다각형 내부에 있는지 확인합니다 (Ray Casting 알고리즘 사용). + * @param {Object} point - 확인할 점 {x, y} + * @param {Array} roofLines - 다각형을 구성하는 선분들 + * @returns {boolean} 점이 다각형 내부에 있으면 true + */ +function isPointInsidePolygon(point, roofLines) { + let inside = false; + const x = point.x; + const y = point.y; + + for (const line of roofLines) { + const x1 = line.x1; + const y1 = line.y1; + const x2 = line.x2; + const y2 = line.y2; + + // Ray casting: 점에서 오른쪽으로 수평선을 그었을 때 다각형 경계와 교차하는 횟수 확인 + if (((y1 > y) !== (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1)) { + inside = !inside; + } + } + + return inside; +} + +/** + * 선분 위의 점에 대한 매개변수 t를 계산합니다. + * p = p1 + t * (p2 - p1)에서 t 값을 구합니다. + * @param {Object} p1 - 선분의 시작점 + * @param {Object} p2 - 선분의 끝점 + * @param {Object} point - 선분 위의 점 + * @returns {number} 매개변수 t (0이면 p1, 1이면 p2) + */ +function getParameterT(p1, p2, point) { + const dx = p2.x - p1.x; + const dy = p2.y - p1.y; + + // x 좌표가 더 큰 변화를 보이면 x로 계산, 아니면 y로 계산 + if (Math.abs(dx) > Math.abs(dy)) { + return dx === 0 ? 0 : (point.x - p1.x) / dx; + } else { + return dy === 0 ? 0 : (point.y - p1.y) / dy; + } } export const convertBaseLinesToPoints = (baseLines) => { const points = []; @@ -1512,4 +1622,16 @@ export const convertBaseLinesToPoints = (baseLines) => { }); return points; -}; \ No newline at end of file +}; + +function getLineDirection(p1, p2) { + const dx = p2.x - p1.x; + const dy = p2.y - p1.y; + const angle = Math.atan2(dy, dx) * 180 / Math.PI; + + // 각도 범위에 따라 방향 반환 + if ((angle >= -45 && angle < 45)) return 'right'; + if ((angle >= 45 && angle < 135)) return 'bottom'; + if ((angle >= 135 || angle < -135)) return 'left'; + return 'top'; // (-135 ~ -45) +} \ No newline at end of file From 2080c8bf20da95cd71a54aeb3b6be9ffd0a42236 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 27 Oct 2025 13:17:56 +0900 Subject: [PATCH 4/6] =?UTF-8?q?target=20=EC=9D=98=20=EC=A2=8C=ED=91=9C?= =?UTF-8?q?=EB=B9=84=EA=B5=90=20=EB=B3=80=EA=B2=BD=20Math.abs(target.y1=20?= =?UTF-8?q?-=20target.y2)=20<=200.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useMovementSetting.js | 103 ++++++++++++---------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 20485b73..d4c9ff9f 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -217,6 +217,8 @@ export function useMovementSetting(id) { } } + let currentCalculatedValue = 0 + const mouseMoveEvent = (e) => { const target = canvas.getActiveObject() if (!target) return @@ -224,61 +226,71 @@ export function useMovementSetting(id) { const { top: targetTop, left: targetLeft } = target 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) { + if (Math.abs(target.y1 - target.y2) < 0.5) { + // 가로라인의 경우 value = Big(targetTop).minus(currentY).times(10).round(0) + console.log('가로라인 계산:', `${targetTop} - ${currentY.toNumber()} = ${value.toNumber()}`) } else { + // 세로라인의 경우 value = Big(targetLeft).minus(currentX).times(10).round(0).neg() + console.log('세로라인 계산:', `-(${targetLeft} - ${currentX.toNumber()}) = ${value.toNumber()}`) } - if (typeRef.current === TYPE.FLOW_LINE) { - FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber() - } else { - UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber() - const midX = Big(target.x1).plus(target.x2).div(2) - const midY = Big(target.y1).plus(target.y2).div(2) - const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId) - let checkPoint - if (target.y1 === target.y2) { - checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() } - if (wall.inPolygon(checkPoint)) { - if (value.s === -1) { - UP_DOWN_REF.UP_RADIO_REF.current.checked = false - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true - } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false - } + + currentCalculatedValue = value.toNumber() + + if (typeRef.current === TYPE.FLOW_LINE) { + FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber() } else { - if (value.s === 1) { - UP_DOWN_REF.UP_RADIO_REF.current.checked = false - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber() + const midX = Big(target.x1).plus(target.x2).div(2) + const midY = Big(target.y1).plus(target.y2).div(2) + const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId) + let checkPoint + if (target.y1 === target.y2) { + checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() } + if (wall.inPolygon(checkPoint)) { + if (value.s === -1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } else { + if (value.s === 1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false - } - } - } else { - checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() } - if (wall.inPolygon(checkPoint)) { - if (value.s === 1) { - UP_DOWN_REF.UP_RADIO_REF.current.checked = false - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true - } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false - } - } else { - if (value.s === -1) { - UP_DOWN_REF.UP_RADIO_REF.current.checked = false - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true - } else { - UP_DOWN_REF.UP_RADIO_REF.current.checked = true - UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() } + if (wall.inPolygon(checkPoint)) { + if (value.s === 1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } else { + if (value.s === -1) { + UP_DOWN_REF.UP_RADIO_REF.current.checked = false + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true + } else { + UP_DOWN_REF.UP_RADIO_REF.current.checked = true + UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false + } + } } } } - } - } + + const mouseDownEvent = (e) => { canvas @@ -287,6 +299,7 @@ export function useMovementSetting(id) { .forEach((obj) => canvas.remove(obj)) canvas.renderAll() + //const target = selectedObject.current const target = selectedObject.current if (!target) return From befa12b00b3674e08bc5767d0744b259f55257f9 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 27 Oct 2025 14:38:25 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=EA=B8=B8=EC=9D=B4=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 46fbb931..1c2e3d65 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -6,7 +6,6 @@ import { calculateAngle, drawGableRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } import * as turf from '@turf/turf' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' -import { drawSkeletonRidgeRoof } from '@/util/skeleton-utils' export const QPolygon = fabric.util.createClass(fabric.Polygon, { type: 'QPolygon', @@ -376,9 +375,27 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const dy = Big(end.y).minus(Big(start.y)) const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber() + const direction = getDirectionByPoint(start, end) + + let left, top + + if (direction === 'bottom') { + left = (start.x + end.x) / 2 - 50 + top = (start.y + end.y) / 2 + } else if (direction === 'top') { + left = (start.x + end.x) / 2 + 30 + top = (start.y + end.y) / 2 + } else if (direction === 'left') { + left = (start.x + end.x) / 2 + top = (start.y + end.y) / 2 - 30 + } else if (direction === 'right') { + left = (start.x + end.x) / 2 + top = (start.y + end.y) / 2 + 30 + } + let midPoint - midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + midPoint = new fabric.Point(left, top) const degree = Big(Math.atan2(dy.toNumber(), dx.toNumber())).times(180).div(Math.PI).toNumber() From 8cd67a92a848d8993be8ed1902768b0531cfadb3 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 28 Oct 2025 16:39:17 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Q.PARTNERS=20=EB=A7=81=ED=81=AC=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/header/Header.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index 8b37b134..561b8e47 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -133,17 +133,21 @@ export default function Header(props) { { id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 2, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, + { id: 4, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' }, + ] : userSession.groupId === '60000' ? [ { id: 0, name: getMessage('site.header.link1'), target: '_blank' }, { id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, + { id: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' }, ] : [ { id: 0, name: getMessage('site.header.link1'), target: '_blank' }, { id: 1, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, + { id: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' }, ], ) onChangeSelect({ id: 0, name: getMessage('site.header.link1') })