From e10e0d5b582dda1f4b19d0c074c171fab4af2c49 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 30 Jul 2024 17:55:30 +0900 Subject: [PATCH 1/3] =?UTF-8?q?sortedPoints=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 | 128 +++++++--------------------------------- 1 file changed, 21 insertions(+), 107 deletions(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index e7bdb373..d7a740ef 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -292,56 +292,6 @@ export const getDirectionByPoint = (a, b) => { } } -/** - * 두 선분의 교차점을 찾는 함수입니다. 이 함수는 두 선분이 실제로 교차하는 지점 내에서 교차하는지 확인합니다. - * @param {Object} line1 첫 번째 선분, {x1, y1, x2, y2} 형태의 객체 - * @param {Object} line2 두 번째 선분, {x1, y1, x2, y2} 형태의 객체 - * @returns {{x: number, y: number}|null} 교차점의 좌표를 반환하거나, 교차점이 없으면 null을 반환합니다. - */ -export function calculateIntersection(line1, line2) { - const { x1: x1_1, y1: y1_1, x2: x2_1, y2: y2_1 } = line1 - const { x1: x1_2, y1: y1_2, x2: x2_2, y2: y2_2 } = line2 - - const denominator = (x1_1 - x2_1) * (y1_2 - y2_2) - (y1_1 - y2_1) * (x1_2 - x2_2) - if (denominator === 0) return null // 선분이 평행하거나 일치하는 경우 - - const t = ((x1_1 - x1_2) * (y1_2 - y2_2) - (y1_1 - y1_2) * (x1_2 - x2_2)) / denominator - const u = -((x1_1 - x2_1) * (y1_1 - y1_2) - (y1_1 - y2_1) * (x1_1 - x1_2)) / denominator - - // t와 u가 모두 0과 1 사이에 있을 때, 선분 내에서 교차 - if (t >= 0 && t <= 1 && u >= 0 && u <= 1) { - const intersectionX = x1_1 + t * (x2_1 - x1_1) - const intersectionY = y1_1 + t * (y2_1 - y1_1) - - // Determine the min and max for line1 and line2 for both x and y - const line1MinX = Math.min(line1.x1, line1.x2) - const line1MaxX = Math.max(line1.x1, line1.x2) - const line2MinX = Math.min(line2.x1, line2.x2) - const line2MaxX = Math.max(line2.x1, line2.x2) - - const line1MinY = Math.min(line1.y1, line1.y2) - const line1MaxY = Math.max(line1.y1, line1.y2) - const line2MinY = Math.min(line2.y1, line2.y2) - const line2MaxY = Math.max(line2.y1, line2.y2) - - // 교차점이 선분의 범위 내에 있는지 확인 - if ( - intersectionX >= line1MinX && - intersectionX <= line1MaxX && - intersectionX >= line2MinX && - intersectionX <= line2MaxX && - intersectionY >= line1MinY && - intersectionY <= line1MaxY && - intersectionY >= line2MinY && - intersectionY <= line2MaxY - ) { - return { x: Math.round(intersectionX), y: Math.round(intersectionY) } - } - } - - return null // 교차점이 선분의 범위 내에 없음 -} - export const findIntersection1 = (line1, line2) => { const { x1, y1, x2, y2 } = line1 // 첫 번째 선의 두 점 @@ -372,7 +322,7 @@ export const findIntersection1 = (line1, line2) => { return { x, y } } -export const calculateIntersection2 = (line1, line2) => { +export const calculateIntersection = (line1, line2) => { const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2]) if (!result) { @@ -425,68 +375,32 @@ export function findOrthogonalPoint(line1, line2) { */ export const sortedPoints = (points) => { const copyPoints = [...points] - //points를 x,y좌표를 기준으로 정렬합니다. - copyPoints.sort((a, b) => { - if (a.x === b.x) { - return a.y - b.y - } - return a.x - b.x + copyPoints.forEach((point) => { + point.x1 = point.x + point.y1 = point.y + const nextPoint = copyPoints[(copyPoints.indexOf(point) + 1) % copyPoints.length] + point.x2 = nextPoint.x + point.y2 = nextPoint.y }) - // 이때 copyPoints를 순회하며 최초엔 x값을 비교하여 같은 점을 찾는다. 이때 이 점이 2번째 점이 된다. - // 그 다음점은 2번째 점과 y값이 같은 점이 된다. - // 또 그다음 점은 3번째 점과 x값이 같은 점이 된다. - // 이를 반복하여 copyPoints를 재배열한다. - const resultPoints = [copyPoints[0]] - let index = 1 - let currentPoint = { ...copyPoints[0] } - copyPoints.splice(0, 1) - while (index < points.length) { - if (index === points.length - 1) { - resultPoints.push(copyPoints[0]) - index++ - break - } else if (index % 2 === 0) { - // 짝수번째는 y값이 같은 점을 찾는다. - for (let i = 0; i < copyPoints.length; i++) { - // y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다. - let temp = copyPoints.filter((point) => point.y === currentPoint.y) - if (temp.length === 0) { - // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다. - temp = Array.of(findClosestPointByY(currentPoint, copyPoints)) - } - // temp중 x값이 가장 가까운 값 + // copyPoint에서 x1, y1 값을 기준으로 정렬 후 첫번째 값 + const startIndex = getStartIndex(copyPoints) - const min = temp.reduce((prev, current) => (Math.abs(current.x - currentPoint.x) <= Math.abs(prev.x - currentPoint.x) ? current : prev)) + const resultPoints = [copyPoints[startIndex]] - resultPoints.push(min) - currentPoint = min - copyPoints.splice(copyPoints.indexOf(min), 1) - index++ - break - } - } else { - // 홀수번째는 x값이 같은 점을 찾는다. - for (let i = 0; i < copyPoints.length; i++) { - // x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다. - let temp = copyPoints.filter((point) => point.x === currentPoint.x) - if (temp.length === 0) { - // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다. + let currentPoint = copyPoints[startIndex] - temp = Array.of(findClosestPointByX(currentPoint, copyPoints)) - } - // temp중 y값이 가장 가까운 값 - const min = temp.reduce((prev, current) => (Math.abs(current.y - currentPoint.y) <= Math.abs(prev.y - currentPoint.y) ? current : prev)) + copyPoints.forEach((point, index) => { + if (index === startIndex) return - resultPoints.push(min) - currentPoint = min - copyPoints.splice(copyPoints.indexOf(min), 1) - index++ - break - } - } - } - return resultPoints + const nextPoint = copyPoints.find((p) => p.x1 === currentPoint.x2 && p.y1 === currentPoint.y2) + resultPoints.push(nextPoint) + currentPoint = nextPoint + }) + + return resultPoints.map((point) => { + return { x: point.x, y: point.y } + }) } /** From 60eab96d7959a89680eca1e7cbf68b50d07ccaeb Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 30 Jul 2024 19:55:39 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EC=B2=AB=EB=B2=88=EC=A7=B8=20=EC=9D=B8?= =?UTF-8?q?=EB=8D=B1=EC=8A=A4=EC=9D=98=20=EB=B0=A9=ED=96=A5=EC=9D=B4=20rig?= =?UTF-8?q?ht=20=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/canvas-util.js | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index d7a740ef..c8aeafe2 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -385,18 +385,37 @@ export const sortedPoints = (points) => { // copyPoint에서 x1, y1 값을 기준으로 정렬 후 첫번째 값 const startIndex = getStartIndex(copyPoints) + const startDirection = getDirectionByPoint( + { x: copyPoints[startIndex].x1, y: copyPoints[startIndex].y1 }, + { x: copyPoints[startIndex].x2, y: copyPoints[startIndex].y2 }, + ) const resultPoints = [copyPoints[startIndex]] let currentPoint = copyPoints[startIndex] - copyPoints.forEach((point, index) => { - if (index === startIndex) return + switch (startDirection) { + case 'right': { + copyPoints.forEach((point, index) => { + if (index === startIndex) return - const nextPoint = copyPoints.find((p) => p.x1 === currentPoint.x2 && p.y1 === currentPoint.y2) - resultPoints.push(nextPoint) - currentPoint = nextPoint - }) + const nextPoint = copyPoints.find((p) => p.x2 === currentPoint.x && p.y2 === currentPoint.y) + resultPoints.push(nextPoint) + currentPoint = nextPoint + }) + break + } + case 'bottom': { + copyPoints.forEach((point, index) => { + if (index === startIndex) return + + const nextPoint = copyPoints.find((p) => p.x1 === currentPoint.x2 && p.y1 === currentPoint.y2) + resultPoints.push(nextPoint) + currentPoint = nextPoint + }) + break + } + } return resultPoints.map((point) => { return { x: point.x, y: point.y } From 4e39987ed491eaa6a2c310a31a3f6f163b6c679e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 30 Jul 2024 21:33:58 +0900 Subject: [PATCH 3/3] =?UTF-8?q?QPolygon=20=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 | 72 ++++++++++++++++++------------- src/util/canvas-util.js | 2 +- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 699c7182..b5978bfb 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -126,16 +126,20 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { points.forEach((start, i) => { const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id && obj.idx === i) - if (thisText) { - thisText.set({ left: (start.x + points[(i + 1) % points.length].x) / 2, top: (start.y + points[(i + 1) % points.length].y) / 2 }) - return - } - const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y const length = Math.sqrt(dx * dx + dy * dy) + if (thisText) { + thisText.set({ + left: (start.x + points[(i + 1) % points.length].x) / 2, + top: (start.y + points[(i + 1) % points.length].y) / 2, + text: length.toFixed(0), + }) + return + } + const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) // Create new text object if it doesn't exist @@ -168,38 +172,46 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.canvas = canvas }, fillCell(cell = { width: 50, height: 100, padding: 10 }) { - const points = this.getCurrentPoints() - let bounds + const points = this.points + const minX = Math.min(...points.map((p) => p.x)) + const maxX = Math.max(...points.map((p) => p.x)) + const minY = Math.min(...points.map((p) => p.y)) + const maxY = Math.max(...points.map((p) => p.y)) - try { - bounds = fabric.util.makeBoundingBoxFromPoints(points) - } catch (error) { - alert('다각형의 꼭지점이 4개 이상이어야 합니다.') - return - } + const boundingBoxWidth = maxX - minX + const boundingBoxHeight = maxY - minY - for (let x = bounds.left; x < bounds.left + bounds.width; x += cell.width + cell.padding) { - for (let y = bounds.top; y < bounds.top + bounds.height; y += cell.height + cell.padding) { - const rect = new fabric.Rect({ - left: x, - top: y, - width: cell.width, - height: cell.height, - fill: 'transparent', - stroke: 'black', - selectable: false, - }) + const rectWidth = cell.width + const rectHeight = cell.height + + const cols = Math.floor((boundingBoxWidth + cell.padding) / (rectWidth + cell.padding)) + const rows = Math.floor((boundingBoxHeight + cell.padding) / (rectHeight + cell.padding)) + + for (let i = 0; i < cols; i++) { + for (let j = 0; j < rows; j++) { + const rectLeft = minX + i * (rectWidth + cell.padding) + const rectTop = minY + j * (rectHeight + cell.padding) const rectPoints = [ - new fabric.Point(rect.left, rect.top), - new fabric.Point(rect.left + rect.width, rect.top), - new fabric.Point(rect.left, rect.top + rect.height), - new fabric.Point(rect.left + rect.width, rect.top + rect.height), + { x: rectLeft, y: rectTop }, + { x: rectLeft + rectWidth, y: rectTop }, + { x: rectLeft, y: rectTop + rectHeight }, + { x: rectLeft + rectWidth, y: rectTop + rectHeight }, ] - const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding) + const allPointsInside = rectPoints.every((point) => this.inPolygon(point)) + + if (allPointsInside) { + const rect = new fabric.Rect({ + left: rectLeft, + top: rectTop, + width: rectWidth, + height: rectHeight, + stroke: 'black', // or any other color + fill: 'transparent', + selectable: false, + }) - if (isInside) { this.canvas.add(rect) } } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index c8aeafe2..75bf9439 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -383,7 +383,7 @@ export const sortedPoints = (points) => { point.y2 = nextPoint.y }) - // copyPoint에서 x1, y1 값을 기준으로 정렬 후 첫번째 값 + // copyPoint에서 x1, y1 값을 기준으로 시작 인덱스 const startIndex = getStartIndex(copyPoints) const startDirection = getDirectionByPoint( { x: copyPoints[startIndex].x1, y: copyPoints[startIndex].y1 },