From 7e0e4643e219107a90ed3a896bec52fbe2edc307 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 11 Jul 2025 11:23:33 +0900 Subject: [PATCH] =?UTF-8?q?QPolygon=20containsPoint=20=EA=B3=84=EC=82=B0?= =?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/components/fabric/QPolygon.js | 87 ++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index c94bb316..264d62d1 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -387,7 +387,15 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.canvas = canvas }, fillCellABType( - cell = { width: 50, height: 100, padding: 5, wallDirection: 'left', referenceDirection: 'none', startIndex: -1, isCellCenter: false }, + cell = { + width: 50, + height: 100, + padding: 5, + wallDirection: 'left', + referenceDirection: 'none', + startIndex: -1, + isCellCenter: false, + }, ) { const points = this.points @@ -689,6 +697,59 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { return intersects % 2 === 1 }, + + inPolygonImproved(point) { + const vertices = this.points + let inside = false + const testX = point.x + const testY = point.y + + for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) { + const xi = vertices[i].x + const yi = vertices[i].y + const xj = vertices[j].x + const yj = vertices[j].y + + // 점이 정점 위에 있는지 확인 + if (Math.abs(xi - testX) < 0.01 && Math.abs(yi - testY) < 0.01) { + return true + } + + // 점이 선분 위에 있는지 확인 + if (this.isPointOnSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) { + return true + } + + // Ray casting 알고리즘 + if (((yi > testY) !== (yj > testY)) && + (testX < (xj - xi) * (testY - yi) / (yj - yi) + xi)) { + inside = !inside + } + } + + return inside + }, + + isPointOnSegment(point, segStart, segEnd) { + const tolerance = 0.1 + const dxSegment = segEnd.x - segStart.x + const dySegment = segEnd.y - segStart.y + const dxPoint = point.x - segStart.x + const dyPoint = point.y - segStart.y + + // 벡터의 외적을 계산하여 점이 선분 위에 있는지 확인 + const crossProduct = Math.abs(dxPoint * dySegment - dyPoint * dxSegment) + + if (crossProduct > tolerance) { + return false + } + + // 점이 선분의 범위 내에 있는지 확인 + const dotProduct = dxPoint * dxSegment + dyPoint * dySegment + const squaredLength = dxSegment * dxSegment + dySegment * dySegment + + return dotProduct >= 0 && dotProduct <= squaredLength + }, setCoords: function () { // 부모 클래스의 setCoords 호출 this.callSuper('setCoords') @@ -699,10 +760,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { delete this.oCoords delete this.aCoords delete this.__corner - + // 다시 부모 setCoords 호출 this.callSuper('setCoords') - + // 한 번 더 강제로 bounding rect 재계산 this._clearCache && this._clearCache() } @@ -711,7 +772,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { containsPoint: function (point) { // 먼저 좌표 업데이트 this.setCoords() - + // 캔버스 줌과 viewport transform 고려한 좌표 변환 let localPoint = point if (this.canvas) { @@ -722,13 +783,25 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { localPoint = fabric.util.transformPoint(point, inverted) } } - + + // 오브젝트의 transform matrix를 고려한 좌표 변환 + const matrix = this.calcTransformMatrix() + const invertedMatrix = fabric.util.invertTransform(matrix) + const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix) + + // pathOffset을 고려한 최종 좌표 계산 + const pathOffset = this.get('pathOffset') + const finalPoint = { + x: transformedPoint.x + pathOffset.x, + y: transformedPoint.y + pathOffset.y, + } + if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { - const isInside = this.inPolygon(localPoint) + const isInside = this.inPolygonImproved(finalPoint) this.set('selectable', isInside) return isInside } else { - return this.callSuper('containsPoint', localPoint) + return this.inPolygonImproved(finalPoint) } },