QPolygon containsPoint 계산 수정

This commit is contained in:
hyojun.choi 2025-07-11 11:23:33 +09:00
parent 849ba0e0d5
commit 7e0e4643e2

View File

@ -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)
}
},