fix: transform screen coordinates to SVG space for isPointInFill()

CRITICAL FIX: isPointInFill() requires points in SVG coordinate space,
not screen coordinates. Use getScreenCTM().inverse() to transform
screen coordinates before hit testing.

The previous commit broke region detection because it was passing
screen pixel coordinates directly to isPointInFill(), which expects
SVG viewBox coordinates.

Now properly:
1. Get SVG's screen transformation matrix (getScreenCTM)
2. Invert it to get screen→SVG transform
3. Transform each test point before calling isPointInFill()
4. Fallback to bounding box detection if CTM unavailable

This fixes the "zoom box is totally gone" issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-24 19:37:18 -06:00
parent e255ce2c6f
commit 4c933be48a
1 changed files with 31 additions and 14 deletions

View File

@ -180,22 +180,39 @@ export function useRegionDetection(options: UseRegionDetectionOptions): UseRegio
let overlaps = false
let cursorInRegion = false
// Create SVG point for reuse
const svgPoint = svgElement.createSVGPoint()
// Get the transformation matrix to convert screen coordinates to SVG coordinates
const screenCTM = svgElement.getScreenCTM()
if (!screenCTM) {
// Fallback to bounding box if we can't get coordinate transform
overlaps = true
cursorInRegion =
cursorClientX >= regionLeft &&
cursorClientX <= regionRight &&
cursorClientY >= regionTop &&
cursorClientY <= regionBottom
} else {
const inverseMatrix = screenCTM.inverse()
// Check if cursor point is inside the actual region path
svgPoint.x = cursorClientX
svgPoint.y = cursorClientY
cursorInRegion = regionPath.isPointInFill(svgPoint)
// Create SVG point for reuse
let svgPoint = svgElement.createSVGPoint()
// Sample points in detection box to check for overlap
for (let i = 0; i < samplesPerSide && !overlaps; i++) {
for (let j = 0; j < samplesPerSide && !overlaps; j++) {
svgPoint.x = boxLeft + i * sampleStep
svgPoint.y = boxTop + j * sampleStep
if (regionPath.isPointInFill(svgPoint)) {
overlaps = true
break
// Check if cursor point is inside the actual region path
svgPoint.x = cursorClientX
svgPoint.y = cursorClientY
svgPoint = svgPoint.matrixTransform(inverseMatrix)
cursorInRegion = regionPath.isPointInFill(svgPoint)
// Sample points in detection box to check for overlap
for (let i = 0; i < samplesPerSide && !overlaps; i++) {
for (let j = 0; j < samplesPerSide && !overlaps; j++) {
svgPoint = svgElement.createSVGPoint()
svgPoint.x = boxLeft + i * sampleStep
svgPoint.y = boxTop + j * sampleStep
svgPoint = svgPoint.matrixTransform(inverseMatrix)
if (regionPath.isPointInFill(svgPoint)) {
overlaps = true
break
}
}
}
}