polygon 선택이 잘 안되는 현상 수정중

This commit is contained in:
hyojun.choi 2025-07-11 14:09:50 +09:00
parent 7e0e4643e2
commit 5102fa2ec0

View File

@ -721,8 +721,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}
// Ray casting 알고리즘
if (((yi > testY) !== (yj > testY)) &&
(testX < (xj - xi) * (testY - yi) / (yj - yi) + xi)) {
if (yi > testY !== yj > testY && testX < ((xj - xi) * (testY - yi)) / (yj - yi) + xi) {
inside = !inside
}
}
@ -773,36 +772,140 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = point
if (this.canvas) {
const vpt = this.canvas.viewportTransform
if (vpt) {
// viewport transform 역변환
const inverted = fabric.util.invertTransform(vpt)
localPoint = fabric.util.transformPoint(point, inverted)
}
// 기본 Fabric.js bounding box 체크 먼저 수행
if (!this.callSuper('containsPoint', point)) {
return false
}
// 오브젝트의 transform matrix를 고려한 좌표 변환
// 줌 레벨에 관계없이 안정적인 좌표 변환
const matrix = this.calcTransformMatrix()
const invertedMatrix = fabric.util.invertTransform(matrix)
const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix)
// pathOffset을 고려한 최종 좌표 계산
// 캔버스 줌 고려
let testPoint = point
if (this.canvas && this.canvas.viewportTransform) {
const vpt = this.canvas.viewportTransform
const invertedVpt = fabric.util.invertTransform(vpt)
testPoint = fabric.util.transformPoint(point, invertedVpt)
}
// 오브젝트 좌표계로 변환
const localPoint = fabric.util.transformPoint(testPoint, invertedMatrix)
// pathOffset 적용
const pathOffset = this.get('pathOffset')
const finalPoint = {
x: transformedPoint.x + pathOffset.x,
y: transformedPoint.y + pathOffset.y,
x: localPoint.x + pathOffset.x,
y: localPoint.y + pathOffset.y,
}
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygonImproved(finalPoint)
this.set('selectable', isInside)
return isInside
} else {
return this.inPolygonImproved(finalPoint)
// 단순하고 안정적인 point-in-polygon 알고리즘 사용
return this.simplePointInPolygon(finalPoint, this.get('points'))
},
simplePointInPolygon: function (point, polygon) {
const x = point.x
const y = point.y
let inside = false
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i].x
const yi = polygon[i].y
const xj = polygon[j].x
const yj = polygon[j].y
if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
inside = !inside
}
}
return inside
},
isPointInPolygonRobust: function (point, polygon) {
const x = point.x
const y = point.y
let inside = false
// 부동소수점 정밀도를 고려한 허용 오차
const epsilon = 1e-10
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i].x
const yi = polygon[i].y
const xj = polygon[j].x
const yj = polygon[j].y
// 점이 정점 위에 있는지 확인 (확장된 허용 오차)
if (Math.abs(xi - x) < 0.5 && Math.abs(yi - y) < 0.5) {
return true
}
// 점이 선분 위에 있는지 확인
if (this.isPointOnLineSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) {
return true
}
// Ray casting 알고리즘 - 개선된 버전
if (Math.abs(yi - yj) > epsilon) {
const minY = Math.min(yi, yj)
const maxY = Math.max(yi, yj)
if (y > minY && y <= maxY) {
// 교차점 계산
const intersectionX = xi + ((y - yi) / (yj - yi)) * (xj - xi)
if (intersectionX > x) {
inside = !inside
}
}
}
}
return inside
},
isPointOnLineSegment: function (point, lineStart, lineEnd) {
const tolerance = 2.0 // 더 큰 허용 오차
const x = point.x
const y = point.y
const x1 = lineStart.x
const y1 = lineStart.y
const x2 = lineEnd.x
const y2 = lineEnd.y
// 선분의 길이가 0인 경우 (점)
if (Math.abs(x2 - x1) < 1e-10 && Math.abs(y2 - y1) < 1e-10) {
return Math.abs(x - x1) < tolerance && Math.abs(y - y1) < tolerance
}
// 점과 선분 사이의 거리 계산
const A = x - x1
const B = y - y1
const C = x2 - x1
const D = y2 - y1
const dot = A * C + B * D
const lenSq = C * C + D * D
const param = dot / lenSq
let xx, yy
if (param < 0 || (x1 === x2 && y1 === y2)) {
xx = x1
yy = y1
} else if (param > 1) {
xx = x2
yy = y2
} else {
xx = x1 + param * C
yy = y1 + param * D
}
const dx = x - xx
const dy = y - yy
const distance = Math.sqrt(dx * dx + dy * dy)
return distance < tolerance
},
inPolygonABType(x, y, polygon) {