From a45bba3a7bc0e2dfad959d7c5d363be81dc310ab Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 14:26:21 +0900 Subject: [PATCH 01/15] =?UTF-8?q?=EC=98=A4=EC=B0=A8=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 82 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 9a5f39f8..43a78eae 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -822,10 +822,10 @@ export const usePolygon = () => { line.endPoint = endPoint }) - polygonLines.forEach((line) => { + /*polygonLines.forEach((line) => { line.set({ strokeWidth: 10 }) canvas.add(line) - }) + })*/ canvas.renderAll() polygonLines.forEach((line) => { @@ -1148,31 +1148,47 @@ export const usePolygon = () => { } const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => { + // ==== Utility functions ==== + + function isSamePoint(p1, p2, epsilon = 1) { + return Math.abs(p1.x - p2.x) <= epsilon && Math.abs(p1.y - p2.y) <= epsilon + } + + function normalizePoint(p, epsilon = 1) { + return { + x: Math.round(p.x / epsilon) * epsilon, + y: Math.round(p.y / epsilon) * epsilon, + } + } + + function pointToKey(p, epsilon = 1) { + const norm = normalizePoint(p, epsilon) + return `${norm.x},${norm.y}` + } + // 거리 계산 function calcDistance(p1, p2) { return Math.hypot(p2.x - p1.x, p2.y - p1.y) } - // 바로 연결 체크 - function isDirectlyConnected(start, end, graph) { - const startKey = `${start.x},${start.y}` - if (!graph[startKey]) return false - return graph[startKey].some((neighbor) => neighbor.point.x === end.x && neighbor.point.y === end.y) - } + // ==== Direct edge check ==== - // Dijkstra 최단 경로 - function findShortestPath(start, end, graph) { - const startKey = `${start.x},${start.y}` - const endKey = `${end.x},${end.y}` + function isDirectlyConnected(start, end, graph, epsilon = 1) { + const startKey = pointToKey(start, epsilon) + return (graph[startKey] || []).some((neighbor) => isSamePoint(neighbor.point, end, epsilon)) + } + // ==== Dijkstra pathfinding ==== + + function findShortestPath(start, end, graph, epsilon = 1) { + const startKey = pointToKey(start, epsilon) + const endKey = pointToKey(end, epsilon) const distances = {} const previous = {} const visited = new Set() const queue = [{ key: startKey, dist: 0 }] - for (const key in graph) { - distances[key] = Infinity - } + for (const key in graph) distances[key] = Infinity distances[startKey] = 0 while (queue.length > 0) { @@ -1181,8 +1197,8 @@ export const usePolygon = () => { if (visited.has(key)) continue visited.add(key) - for (const neighbor of graph[key]) { - const neighborKey = `${neighbor.point.x},${neighbor.point.y}` + for (const neighbor of graph[key] || []) { + const neighborKey = pointToKey(neighbor.point, epsilon) const alt = distances[key] + neighbor.distance if (alt < distances[neighborKey]) { distances[neighborKey] = alt @@ -1192,35 +1208,37 @@ export const usePolygon = () => { } } - // 경로 복구 const path = [] let currentKey = endKey - if (!previous[currentKey]) { - return null // 경로 없음 - } + if (!previous[currentKey]) return null while (currentKey !== startKey) { const [x, y] = currentKey.split(',').map(Number) path.unshift({ x, y }) currentKey = previous[currentKey] } - path.unshift(start) + + const [sx, sy] = startKey.split(',').map(Number) + path.unshift({ x: sx, y: sy }) return path } // 최종 함수 - function getPath(start, end, graph) { - if (isDirectlyConnected(start, end, graph)) { + function getPath(start, end, graph, epsilon = 1) { + if (isDirectlyConnected(start, end, graph, epsilon)) { + console.log('직선 연결 있음. 무시.') return null - } else { - const path = findShortestPath(start, end, graph) - if (!path || path.length < 3) { - return null - } - return path } + + const path = findShortestPath(start, end, graph, epsilon) + if (!path || path.length < 3) { + console.log('경로 존재하나 3개 미만 좌표. 무시.') + return null + } + + return path } const roofs = [] @@ -1235,8 +1253,8 @@ export const usePolygon = () => { }) for (const [p1, p2] of edges) { - const key1 = `${p1.x},${p1.y}` - const key2 = `${p2.x},${p2.y}` + const key1 = pointToKey(p1) + const key2 = pointToKey(p2) const distance = calcDistance(p1, p2) if (!graph[key1]) graph[key1] = [] From 9eda4aa834cf597cc2d51ce6ba1de66476ef5f75 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 17:48:38 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EC=95=88?= =?UTF-8?q?=EB=82=98=EB=88=A0=EC=A7=80=EB=8A=94=20=ED=98=84=EC=83=81=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/hooks/usePolygon.js | 9 +++++++-- src/util/canvas-util.js | 15 ++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 43a78eae..93659a08 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -754,7 +754,7 @@ export const usePolygon = () => { } const splitPolygonWithLines = (polygon) => { - polygon.set({ visible: false }) + // polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. @@ -934,8 +934,9 @@ export const usePolygon = () => { }) //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. + newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) - + const originPolygonLines = [...polygonLines] polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] @@ -1045,6 +1046,10 @@ export const usePolygon = () => { }) }) + if (startLines.length === 0) { + startLines = originPolygonLines + } + // 나눠서 중복 제거된 roof return const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 1146afa7..9eb6e0e5 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -515,26 +515,31 @@ export const sortedPointLessEightPoint = (points) => { * @param line * @param point * @returns {boolean} + * @param epsilon */ // 직선의 방정식. // 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다. -export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }) { - /*const a = line.y2 - line.y1 +export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { + const a = y2 - y1 + const b = x1 - x2 + const c = x2 * y1 - x1 * y2 + return Math.abs(a * x + b * y + c) < 1000 + /*/!*const a = line.y2 - line.y1 const b = line.x1 - line.x2 const c = line.x2 * line.y1 - line.x1 * line.y2 const result = Math.abs(a * point.x + b * point.y + c) / 100 // 점이 선 위에 있는지 확인 - return result <= 10*/ + return result <= 10*!/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 5) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) - return withinXRange && withinYRange + return withinXRange && withinYRange*/ } /** * 점과 가까운 line 찾기 From 9f88880ac2a93c25edff47632a9f070e070ec29d Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 29 Apr 2025 17:50:36 +0900 Subject: [PATCH 03/15] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=82=B4=EC=9A=A9?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 93659a08..8fc150e7 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -754,7 +754,7 @@ export const usePolygon = () => { } const splitPolygonWithLines = (polygon) => { - // polygon.set({ visible: false }) + polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. From 8412462a8b7ef229fa834e3780573477e4882040 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:08:58 +0900 Subject: [PATCH 04/15] =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=9C=84=EC=97=90?= =?UTF-8?q?=20=EC=9E=88=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=88=98=EC=A0=95=20=EC=A7=80=EB=B6=95=EC=9E=AC=20?= =?UTF-8?q?=ED=95=A0=EB=8B=B9=20=EC=8B=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/common/useRoofFn.js | 250 +++++++++++++++++----------------- src/hooks/usePolygon.js | 4 +- src/util/canvas-util.js | 8 +- 3 files changed, 133 insertions(+), 129 deletions(-) diff --git a/src/hooks/common/useRoofFn.js b/src/hooks/common/useRoofFn.js index c49128e7..38246273 100644 --- a/src/hooks/common/useRoofFn.js +++ b/src/hooks/common/useRoofFn.js @@ -23,102 +23,65 @@ export function useRoofFn() { //면형상 선택 클릭시 지붕 패턴 입히기 function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) { - if (!polygon) { - return - } - if (polygon.points.length < 3) { - return - } - if (isForceChange && !isDisplay) { - /*if (polygon.roofMaterial) { + try { + if (!polygon) { + return + } + if (polygon.points.length < 3) { + return + } + if (isForceChange && !isDisplay) { + /*if (polygon.roofMaterial) { polygon.roofMaterial = null }*/ - } - if (!roofMaterial) { - roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial - } - - const ratio = window.devicePixelRatio || 1 - const layout = roofMaterial.layout - - let width = (roofMaterial.width || 226) / 10 - let height = (roofMaterial.length || 158) / 10 - const index = roofMaterial.index ?? 0 - let roofStyle = 2 - const inputPatternSize = { width: width, height: height } //임시 사이즈 - const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 - - if (polygon.direction === 'east' || polygon.direction === 'west') { - //세로형이면 width height를 바꿈 - ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] - } - - // 패턴 소스를 위한 임시 캔버스 생성 - const patternSourceCanvas = document.createElement('canvas') - patternSourceCanvas.width = polygon.width * ratio - patternSourceCanvas.height = polygon.height * ratio - const ctx = patternSourceCanvas.getContext('2d') - let offset = roofStyle === 1 ? 0 : patternSize.width / 2 - - const rows = Math.floor(patternSourceCanvas.height / patternSize.height) - const cols = Math.floor(patternSourceCanvas.width / patternSize.width) - - ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index] - ctx.lineWidth = 2 - ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' - - if (trestleMode) { - ctx.strokeStyle = 'black' - ctx.lineWidth = 0.2 - ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' - } else { - ctx.fillStyle = 'rgba(255, 255, 255, 1)' - } - - if (polygon.direction === 'east' || polygon.direction === 'west') { - offset = roofStyle === 1 ? 0 : patternSize.height / 2 - for (let col = 0; col <= cols; col++) { - const x = col * patternSize.width - const yStart = 0 - const yEnd = patternSourceCanvas.height - ctx.beginPath() - ctx.moveTo(x, yStart) // 선 시작점 - ctx.lineTo(x, yEnd) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) - } - - for (let row = 0; row <= rows; row++) { - const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height - const xStart = col * patternSize.width - const xEnd = xStart + patternSize.width - ctx.beginPath() - ctx.moveTo(xStart, y) // 선 시작점 - ctx.lineTo(xEnd, y) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) - } - } } - } else { - for (let row = 0; row <= rows; row++) { - const y = row * patternSize.height + if (!roofMaterial) { + roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial + } - ctx.beginPath() - ctx.moveTo(0, y) // 선 시작점 - ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 - ctx.stroke() - if (mode === 'allPainted' || trestleMode) { - ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) - } + const ratio = window.devicePixelRatio || 1 + const layout = roofMaterial.layout + let width = (roofMaterial.width || 226) / 10 + let height = (roofMaterial.length || 158) / 10 + const index = roofMaterial.index ?? 0 + let roofStyle = 2 + const inputPatternSize = { width: width, height: height } //임시 사이즈 + const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 + + if (polygon.direction === 'east' || polygon.direction === 'west') { + //세로형이면 width height를 바꿈 + ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] + } + + // 패턴 소스를 위한 임시 캔버스 생성 + const patternSourceCanvas = document.createElement('canvas') + patternSourceCanvas.width = polygon.width * ratio + patternSourceCanvas.height = polygon.height * ratio + const ctx = patternSourceCanvas.getContext('2d') + let offset = roofStyle === 1 ? 0 : patternSize.width / 2 + + const rows = Math.floor(patternSourceCanvas.height / patternSize.height) + const cols = Math.floor(patternSourceCanvas.width / patternSize.width) + + ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index] + ctx.lineWidth = 2 + ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' + + if (trestleMode) { + ctx.strokeStyle = 'black' + ctx.lineWidth = 0.2 + ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' + } else { + ctx.fillStyle = 'rgba(255, 255, 255, 1)' + } + + if (polygon.direction === 'east' || polygon.direction === 'west') { + offset = roofStyle === 1 ? 0 : patternSize.height / 2 for (let col = 0; col <= cols; col++) { - const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width - const yStart = row * patternSize.height - const yEnd = yStart + patternSize.height - + const x = col * patternSize.width + const yStart = 0 + const yEnd = patternSourceCanvas.height ctx.beginPath() ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 @@ -126,49 +89,90 @@ export function useRoofFn() { if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } + + for (let row = 0; row <= rows; row++) { + const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height + const xStart = col * patternSize.width + const xEnd = xStart + patternSize.width + ctx.beginPath() + ctx.moveTo(xStart, y) // 선 시작점 + ctx.lineTo(xEnd, y) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) + } + } + } + } else { + for (let row = 0; row <= rows; row++) { + const y = row * patternSize.height + + ctx.beginPath() + ctx.moveTo(0, y) // 선 시작점 + ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) + } + + for (let col = 0; col <= cols; col++) { + const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width + const yStart = row * patternSize.height + const yEnd = yStart + patternSize.height + + ctx.beginPath() + ctx.moveTo(x, yStart) // 선 시작점 + ctx.lineTo(x, yEnd) // 선 끝점 + ctx.stroke() + if (mode === 'allPainted' || trestleMode) { + ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) + } + } } } - } - const hachingPatternSourceCanvas = document.createElement('canvas') + const hachingPatternSourceCanvas = document.createElement('canvas') - if (mode === 'lineHatch') { - hachingPatternSourceCanvas.width = polygon.width * ratio - hachingPatternSourceCanvas.height = polygon.height * ratio + if (mode === 'lineHatch') { + hachingPatternSourceCanvas.width = polygon.width * ratio + hachingPatternSourceCanvas.height = polygon.height * ratio - const ctx1 = hachingPatternSourceCanvas.getContext('2d') + const ctx1 = hachingPatternSourceCanvas.getContext('2d') - const gap = 10 + const gap = 10 - ctx1.strokeStyle = 'green' // 선 색상 - ctx1.lineWidth = 0.3 // 선 두께 + ctx1.strokeStyle = 'green' // 선 색상 + ctx1.lineWidth = 0.3 // 선 두께 - for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) { - ctx1.beginPath() - ctx1.moveTo(x, 0) // 선 시작점 - ctx1.lineTo(0, x) // 선 끝점 - ctx1.stroke() + for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) { + ctx1.beginPath() + ctx1.moveTo(x, 0) // 선 시작점 + ctx1.lineTo(0, x) // 선 끝점 + ctx1.stroke() + } } + + const combinedPatternCanvas = document.createElement('canvas') + combinedPatternCanvas.width = polygon.width * ratio + combinedPatternCanvas.height = polygon.height * ratio + const combinedCtx = combinedPatternCanvas.getContext('2d') + // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘 + combinedCtx.drawImage(patternSourceCanvas, 0, 0) + combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0) + + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: combinedPatternCanvas, + repeat: 'repeat', + }) + + polygon.set('fill', null) + polygon.set('fill', pattern) + polygon.roofMaterial = roofMaterial + polygon.canvas?.renderAll() + } catch (e) { + console.log(e) } - - const combinedPatternCanvas = document.createElement('canvas') - combinedPatternCanvas.width = polygon.width * ratio - combinedPatternCanvas.height = polygon.height * ratio - const combinedCtx = combinedPatternCanvas.getContext('2d') - // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘 - combinedCtx.drawImage(patternSourceCanvas, 0, 0) - combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0) - - // 패턴 생성 - const pattern = new fabric.Pattern({ - source: combinedPatternCanvas, - repeat: 'repeat', - }) - - polygon.set('fill', null) - polygon.set('fill', pattern) - polygon.roofMaterial = roofMaterial - polygon.canvas?.renderAll() } function removeRoofMaterial(roof = currentObject) { diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 8fc150e7..49472b08 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1234,13 +1234,13 @@ export const usePolygon = () => { function getPath(start, end, graph, epsilon = 1) { if (isDirectlyConnected(start, end, graph, epsilon)) { console.log('직선 연결 있음. 무시.') - return null + return [] } const path = findShortestPath(start, end, graph, epsilon) if (!path || path.length < 3) { console.log('경로 존재하나 3개 미만 좌표. 무시.') - return null + return [] } return path diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 9eb6e0e5..74c5c2a4 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -520,17 +520,17 @@ export const sortedPointLessEightPoint = (points) => { // 직선의 방정식. // 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다. export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { - const a = y2 - y1 + /*const a = y2 - y1 const b = x1 - x2 const c = x2 * y1 - x1 * y2 - return Math.abs(a * x + b * y + c) < 1000 + return Math.abs(a * x + b * y + c) < 1000*/ /*/!*const a = line.y2 - line.y1 const b = line.x1 - line.x2 const c = line.x2 * line.y1 - line.x1 * line.y2 const result = Math.abs(a * point.x + b * point.y + c) / 100 // 점이 선 위에 있는지 확인 - return result <= 10*!/ + return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 @@ -539,7 +539,7 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) - return withinXRange && withinYRange*/ + return withinXRange && withinYRange } /** * 점과 가까운 line 찾기 From adc0c41a672ca990d3c30e3cbbe3abb25f7566b4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:18:48 +0900 Subject: [PATCH 05/15] =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useLine.js | 8 ++++---- src/hooks/usePolygon.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index b447d4f0..bdbd5d6f 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -38,12 +38,12 @@ export const useLine = () => { line.set({ visible: false, }) - canvas - ?.getObjects() - .find((obj) => obj.parentId === line.id) - .set({ + const obj = canvas?.getObjects().find((obj) => obj.parentId === line.id) + if (obj) { + obj.set({ visible: false, }) + } canvas?.renderAll() } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 49472b08..89f1b40b 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1110,7 +1110,7 @@ export const usePolygon = () => { defense = 'north' break } - pitch = polygon.lines[index].attributes?.pitch ?? 0 + pitch = polygon.lines[index]?.attributes?.pitch ?? 0 const roof = new QPolygon(roofPoint, { fontSize: polygon.fontSize, From 937f2eb3feadaa95ad1742ddb84c9db352ed61c6 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 13:26:05 +0900 Subject: [PATCH 06/15] =?UTF-8?q?x=EA=B0=80=20=EC=A0=84=EB=B6=80=20?= =?UTF-8?q?=EA=B0=99=EA=B1=B0=EB=82=98,=20y=EA=B0=80=20=EC=A0=84=EB=B6=80?= =?UTF-8?q?=20=EA=B0=99=EC=9D=80=EA=B2=BD=EC=9A=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 9fbfc6a3..131e9232 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -303,7 +303,13 @@ export function removeDuplicatePolygons(polygons) { } }) - return uniquePolygons + // x가 전부 같거나, y가 전부 같은 경우 제거 + return uniquePolygons.filter((polygon) => { + const xValues = polygon.map((point) => point.x) + const yValues = polygon.map((point) => point.y) + + return !(xValues.every((x) => x === xValues[0]) || yValues.every((y) => y === yValues[0])) + }) } export const isSamePoint = (a, b) => { From 902d13b2739e2a757fe50a0de58d46b4ae3f362a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 16:17:03 +0900 Subject: [PATCH 07/15] =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EB=B2=94=EC=9C=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 74c5c2a4..6eef894e 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -533,7 +533,7 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 10) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 500) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) From a7eb2f7598212932adca274bf433d193ae9439e1 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 30 Apr 2025 18:05:25 +0900 Subject: [PATCH 08/15] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 70 ++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 89f1b40b..a2c8c9aa 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,7 +1,14 @@ import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine, toFixedWithoutRounding } from '@/util/canvas-util' +import { + distanceBetweenPoints, + findAndRemoveClosestPoint, + getDegreeByChon, + getDegreeInOrientation, + isPointOnLine, + toFixedWithoutRounding, +} from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' @@ -755,7 +762,7 @@ export const usePolygon = () => { const splitPolygonWithLines = (polygon) => { polygon.set({ visible: false }) - let innerLines = [...polygon.innerLines] + let innerLines = [...polygon.innerLines].filter((line) => line.visible) /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다. if (!innerLines || innerLines.length === 0) { @@ -825,8 +832,8 @@ export const usePolygon = () => { /*polygonLines.forEach((line) => { line.set({ strokeWidth: 10 }) canvas.add(line) - })*/ - canvas.renderAll() + }) + canvas.renderAll()*/ polygonLines.forEach((line) => { /*const originStroke = line.stroke @@ -838,12 +845,14 @@ export const usePolygon = () => { innerLine.set({ stroke: 'red' }) canvas.renderAll()*/ if (isPointOnLine(line, innerLine.startPoint)) { + canvas.renderAll() if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) { return } intersections.push(innerLine.startPoint) } if (isPointOnLine(line, innerLine.endPoint)) { + canvas.renderAll() if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) { return } @@ -852,13 +861,14 @@ export const usePolygon = () => { /*innerLine.set({ stroke: originInnerStroke }) canvas.renderAll()*/ }) - line.set({ intersections }) + /*line.set({ intersections }) - /*line.set({ stroke: originStroke }) + line.set({ stroke: originStroke }) canvas.renderAll()*/ }) - const divideLines = polygonLines.filter((line) => line.intersections.length > 0) + const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) + let newLines = [] divideLines.forEach((line) => { @@ -873,12 +883,14 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) const newLine2 = new QLine(newLinePoint2, { stroke: 'blue', strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine1.attributes = { ...line.attributes, @@ -907,6 +919,7 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine.attributes = { ...line.attributes, @@ -923,6 +936,7 @@ export const usePolygon = () => { strokeWidth: 3, fontSize: polygon.fontSize, attributes: line.attributes, + name: 'newLine', }) newLine.attributes = { ...line.attributes, @@ -936,7 +950,7 @@ export const usePolygon = () => { //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) - const originPolygonLines = [...polygonLines] + polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] @@ -1005,6 +1019,13 @@ export const usePolygon = () => { } }) + /*innerLines.forEach((line) => { + const startPoint = line.startPoint + const endPoint = line.endPoint + /!*canvas.add(new fabric.Circle({ left: startPoint.x, top: startPoint.y + 10, radius: 5, fill: 'red' })) + canvas.add(new fabric.Circle({ left: endPoint.x, top: endPoint.y - 10, radius: 5, fill: 'blue' }))*!/ + })*/ + /** * 왼쪽 상단을 startPoint로 전부 변경 */ @@ -1031,27 +1052,18 @@ export const usePolygon = () => { line.endPoint = endPoint }) - // polygonLines에서 시작점 혹은 끝점이 innerLines와 연결된 line만 가져온다. - let startLines = polygonLines.filter((line) => { - const startPoint = line.startPoint - const endPoint = line.endPoint - - return innerLines.some((innerLine) => { - return ( - isSamePoint(innerLine.startPoint, startPoint) || - isSamePoint(innerLine.endPoint, startPoint) || - isSamePoint(innerLine.startPoint, endPoint) || - isSamePoint(innerLine.endPoint, endPoint) - ) - }) + //allLines에서 중복을 제거한다. + allLines = allLines.filter((line, index, self) => { + return ( + index === + self.findIndex((l) => { + return isSamePoint(l.startPoint, line.startPoint) && isSamePoint(l.endPoint, line.endPoint) + }) + ) }) - if (startLines.length === 0) { - startLines = originPolygonLines - } - // 나눠서 중복 제거된 roof return - const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints) + const newRoofs = getSplitRoofsPoints(allLines) newRoofs.forEach((roofPoint, index) => { let defense, pitch @@ -1152,11 +1164,11 @@ export const usePolygon = () => { }) } - const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => { + const getSplitRoofsPoints = (allLines) => { // ==== Utility functions ==== function isSamePoint(p1, p2, epsilon = 1) { - return Math.abs(p1.x - p2.x) <= epsilon && Math.abs(p1.y - p2.y) <= epsilon + return Math.abs(p1.x - p2.x) <= 2 && Math.abs(p1.y - p2.y) <= 2 } function normalizePoint(p, epsilon = 1) { @@ -1248,7 +1260,7 @@ export const usePolygon = () => { const roofs = [] - startLines.forEach((line) => { + allLines.forEach((line) => { // 그래프 생성 const graph = {} const edges = allLines From 92c33c9c11c88b4eeed96a1db77e0664e0fe08dd Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 10:46:57 +0900 Subject: [PATCH 09/15] =?UTF-8?q?polygonLines=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index a2c8c9aa..7b14b906 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -949,7 +949,8 @@ export const usePolygon = () => { //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) - polygonLines = polygonLines.filter((line) => line.intersections?.length === 0) + + polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) polygonLines = [...polygonLines, ...newLines] From 381c639d187280c5ca891ac79ffce6883e801a80 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 11:21:26 +0900 Subject: [PATCH 10/15] =?UTF-8?q?roofPoints=20valid=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 131e9232..b1cf5ae1 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -294,7 +294,7 @@ function arePolygonsEqual(polygon1, polygon2) { } export function removeDuplicatePolygons(polygons) { - const uniquePolygons = [] + let uniquePolygons = [] polygons.forEach((polygon) => { const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon)) @@ -304,12 +304,32 @@ export function removeDuplicatePolygons(polygons) { }) // x가 전부 같거나, y가 전부 같은 경우 제거 - return uniquePolygons.filter((polygon) => { - const xValues = polygon.map((point) => point.x) - const yValues = polygon.map((point) => point.y) - - return !(xValues.every((x) => x === xValues[0]) || yValues.every((y) => y === yValues[0])) + uniquePolygons = uniquePolygons.filter((polygon) => { + return isValidPoints(polygon) }) + + return uniquePolygons +} + +// 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. +// 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. +const isValidPoints = (points) => { + for (let i = 1; i < points.length - 1; i++) { + const prev = points[i - 1] + const curr = points[i] + const next = points[i + 1] + + // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 + if (curr.x === prev.x && curr.x === next.x) { + return false + } + + // 현재와 이전의 y가 같다면 다음의 y는 달라야 함 + if (curr.y === prev.y && curr.y === next.y) { + return false + } + } + return true } export const isSamePoint = (a, b) => { From 8da2ab08202f6fce4073243bb2169bc387ff68c7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 13:31:00 +0900 Subject: [PATCH 11/15] =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EB=B2=94=EC=9C=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 5 ++--- src/util/canvas-util.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 7b14b906..62130acd 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -861,14 +861,13 @@ export const usePolygon = () => { /*innerLine.set({ stroke: originInnerStroke }) canvas.renderAll()*/ }) - /*line.set({ intersections }) + line.set({ intersections }) - line.set({ stroke: originStroke }) + /*line.set({ stroke: originStroke }) canvas.renderAll()*/ }) const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) - let newLines = [] divideLines.forEach((line) => { diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 6eef894e..0f0b69dc 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -533,11 +533,11 @@ export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) { return result <= 10*!*/ // 직선 방정식 만족 여부 확인 const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) - if (Math.abs(crossProduct) > 500) return false // 작은 오차 허용 + if (Math.abs(crossProduct) > 1000) return false // 작은 오차 허용 // 점이 선분의 범위 내에 있는지 확인 - const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2) - const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2) + const withinXRange = Math.abs(Math.min(x1, x2) - x) <= 2 || 2 <= Math.abs(Math.max(x1, x2) - x) + const withinYRange = Math.abs(Math.min(y1, y2) - y) <= 2 || 2 <= Math.abs(Math.max(y1, y2) - y) return withinXRange && withinYRange } From 78258fc9c10e9a58695843112851a0a3917061d5 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 7 May 2025 15:05:44 +0900 Subject: [PATCH 12/15] =?UTF-8?q?roof=20=EB=82=98=EB=88=8C=EB=95=8C=20poin?= =?UTF-8?q?t=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index b1cf5ae1..d9923616 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -296,6 +296,7 @@ function arePolygonsEqual(polygon1, polygon2) { export function removeDuplicatePolygons(polygons) { let uniquePolygons = [] + // x가 전부 같거나, y가 전부 같은 경우 제거 polygons.forEach((polygon) => { const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon)) if (!isDuplicate) { @@ -303,7 +304,6 @@ export function removeDuplicatePolygons(polygons) { } }) - // x가 전부 같거나, y가 전부 같은 경우 제거 uniquePolygons = uniquePolygons.filter((polygon) => { return isValidPoints(polygon) }) @@ -314,18 +314,24 @@ export function removeDuplicatePolygons(polygons) { // 현재 point의 x와 이전 포인트의 x와 같을경우, 다음 포인트의 x와 달라야 함. // 현재 point의 y와 이전 포인트의 y와 같을경우, 다음 포인트의 y와 달라야 함. const isValidPoints = (points) => { - for (let i = 1; i < points.length - 1; i++) { - const prev = points[i - 1] - const curr = points[i] - const next = points[i + 1] + for (let i = 1; i < points.length; i++) { + let prev = points[i - 1] + let curr = points[i] + let next = points[i + 1] + + if (i === points.length - 1) { + prev = points[i - 1] + curr = points[i] + next = points[0] + } // 현재와 이전의 x가 같다면 다음의 x는 달라야 함 - if (curr.x === prev.x && curr.x === next.x) { + if (Math.abs(curr.x - prev.x) < 1 && Math.abs(curr.x - next.x) < 1) { return false } // 현재와 이전의 y가 같다면 다음의 y는 달라야 함 - if (curr.y === prev.y && curr.y === next.y) { + if (Math.abs(curr.y - prev.y) < 1 && Math.abs(curr.y - next.y) < 1) { return false } } From 14764d295c27f0934ffc8eaa671a373a76fd3de7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 09:59:09 +0900 Subject: [PATCH 13/15] =?UTF-8?q?polygonLines=EC=A4=91=20intersection=20?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=EA=B0=80=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 42 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 62130acd..4d59d292 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -869,14 +869,15 @@ export const usePolygon = () => { const divideLines = polygonLines.filter((line) => line.intersections?.length > 0) let newLines = [] - - divideLines.forEach((line) => { + polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) + for (let i = divideLines.length - 1; i >= 0; i--) { + const line = divideLines[i] const { intersections, startPoint, endPoint } = line if (intersections.length === 1) { - // 한 점만 교차하는 경우 const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y] const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2] + const newLine1 = new QLine(newLinePoint1, { stroke: 'blue', strokeWidth: 3, @@ -891,28 +892,27 @@ export const usePolygon = () => { attributes: line.attributes, name: 'newLine', }) + newLine1.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10, } newLine2.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10, } - newLines.push(newLine1) - newLines.push(newLine2) - } else { - // 두 점 이상 교차하는 경우 - //1. intersections중에 startPoint와 가장 가까운 점을 찾는다. - //2. 가장 가까운 점을 기준으로 line을 나눈다. + newLines.push(newLine1, newLine2) + divideLines.splice(i, 1) // 기존 line 제거 + } else { let currentPoint = startPoint while (intersections.length !== 0) { const minDistancePoint = findAndRemoveClosestPoint(currentPoint, intersections) const newLinePoint = [currentPoint.x, currentPoint.y, minDistancePoint.x, minDistancePoint.y] + const newLine = new QLine(newLinePoint, { stroke: 'blue', strokeWidth: 3, @@ -920,11 +920,13 @@ export const usePolygon = () => { attributes: line.attributes, name: 'newLine', }) + newLine.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, } + newLines.push(newLine) currentPoint = minDistancePoint } @@ -939,18 +941,18 @@ export const usePolygon = () => { }) newLine.attributes = { ...line.attributes, - planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, - actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, + actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10, } + newLines.push(newLine) + divideLines.splice(i, 1) // 기존 line 제거 } - }) + } //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1)) - polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0) - polygonLines = [...polygonLines, ...newLines] let allLines = [...polygonLines, ...innerLines] From bb6a796b9fd8a170d08711aad83b0030ba2d2d63 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 13:44:31 +0900 Subject: [PATCH 14/15] =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=9E=AC=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=EC=8B=9C=20=EC=B6=94=EA=B0=80=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=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/useAuxiliaryDrawing.js | 10 ++++++++-- src/hooks/roofcover/useRoofAllocationSetting.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 6023bce8..1510825b 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -849,8 +849,14 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) { if ( lineHistory.current.some( (history) => - JSON.stringify(history.startPoint) === JSON.stringify(line.startPoint) && - JSON.stringify(history.endPoint) === JSON.stringify(line.endPoint), + (Math.abs(history.startPoint.x - line.startPoint.x) < 2 && + Math.abs(history.startPoint.y - line.startPoint.y) < 2 && + Math.abs(history.endPoint.x - line.endPoint.x) < 2 && + Math.abs(history.endPoint.y - line.endPoint.y) < 2) || + (Math.abs(history.startPoint.x - line.endPoint.x) < 2 && + Math.abs(history.startPoint.y - line.endPoint.y) < 2 && + Math.abs(history.endPoint.x - line.startPoint.x) < 2 && + Math.abs(history.endPoint.y - line.startPoint.y) < 2), ) ) { canvas.remove(line) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index fb7d2114..00c17c1d 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -412,7 +412,7 @@ export function useRoofAllocationSetting(id) { }) /** 외곽선 삭제 */ - const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine') + const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine' || obj.name === 'pitchText') removeTargets.forEach((obj) => { canvas.remove(obj) }) From dc743cee8fc167c94c4d4a0a6fbde648e1237752 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 8 May 2025 14:21:44 +0900 Subject: [PATCH 15/15] =?UTF-8?q?getRoofMaterialList=20=ED=95=9C=EB=B2=88?= =?UTF-8?q?=EB=A7=8C=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8F=84=EB=A1=9D=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/floor-plan/CanvasFrame.jsx | 29 ++++++++++++++++++++++- src/hooks/option/useCanvasSetting.js | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index ddce32a6..b43fd559 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -12,7 +12,7 @@ import { usePlan } from '@/hooks/usePlan' import { useContextMenu } from '@/hooks/useContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { currentMenuState } from '@/store/canvasAtom' -import { totalDisplaySelector } from '@/store/settingAtom' +import { roofMaterialsAtom, totalDisplaySelector } from '@/store/settingAtom' import { MENU, POLYGON_TYPE } from '@/common/common' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { QcastContext } from '@/app/QcastProvider' @@ -30,8 +30,35 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useEvent } from '@/hooks/useEvent' import { compasDegAtom } from '@/store/orientationAtom' +import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import { useMasterController } from '@/hooks/common/useMasterController' export default function CanvasFrame() { + const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) + const { getRoofMaterialList } = useMasterController() + useEffect(async () => { + if (roofMaterials.length !== 0) { + return + } + const { data } = await getRoofMaterialList() + + const roofLists = data.map((item, idx) => ({ + ...item, + id: item.roofMatlCd, + name: item.roofMatlNm, + selected: idx === 0, + index: idx, + nameJp: item.roofMatlNmJp, + length: item.lenBase && parseInt(item.lenBase), + width: item.widBase && parseInt(item.widBase), + raft: item.raftBase && parseInt(item.raftBase), + layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL, + hajebichi: item.roofPchBase && parseInt(item.roofPchBase), + pitch: item.pitch ? parseInt(item.pitch) : 4, + angle: item.angle ? parseInt(item.angle) : 21.8, + })) + setRoofMaterials(roofLists) + }, []) const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 76f42075..2688502a 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -148,7 +148,7 @@ export function useCanvasSetting(executeEffect = true) { } /** 초 1회만 실행하도록 처리 */ - addRoofMaterials() + //addRoofMaterials() }, []) /**