phetsims / scenery

Scenery is an HTML5 scene graph.
MIT License
53 stars 12 forks source link

Brave browser on mac and windows fails with "Uncaught Error: Matrix could not be inverted, determinant === 0" when running some published sims (but erratic whether fails on load) #1579

Open samreid opened 1 year ago

samreid commented 1 year ago

@kathy-phet reported that a user saw Projectile Motion fail on Brave browser on mac. It was initially fine on her windows testing (but then the failure appeared at a later time), so its erratic also. Some devs were able to reproduce some brave errors.

Here is the stack trace:

Uncaught Error: Matrix could not be inverted, determinant === 0
    at Matrix3.invert (projectile-motion_all.html:10365:19)
    at Transform3.getInverse (projectile-motion_all.html:12749:22)
    at Transform3.inversePosition2 (projectile-motion_all.html:12965:19)
    at Object.scanBounds (projectile-motion_all.html:39186:89)
    at scan (projectile-motion_all.html:39242:33)
    at Object.canvasAccurateBounds (projectile-motion_all.html:39299:25)
    at Object.accurateCanvasBounds (projectile-motion_all.html:53481:19)
    at Text.updateSelfBounds (projectile-motion_all.html:54136:33)
    at Text.validateSelfBounds (projectile-motion_all.html:26109:40)
    at Text.validateBounds (projectile-motion_all.html:26133:33)
samreid commented 1 year ago

This code in scanBounds is returning an infinite bounds result:

    // based on pixel boundaries. for minBounds, the inner edge of the dirty pixel. for maxBounds, the outer edge of the adjacent non-dirty pixel
    // results in a spread of 2 for the identity transform (or any translated form)
    const extraSpread = resolution / 16; // is Chrome antialiasing really like this? dear god... TODO!!!
    return {
      minBounds: new Bounds2(
        ( minX < 1 || minX >= resolution - 1 ) ? Number.POSITIVE_INFINITY : transform.inversePosition2( p( minX + 1 + extraSpread, 0 ) ).x,
        ( minY < 1 || minY >= resolution - 1 ) ? Number.POSITIVE_INFINITY : transform.inversePosition2( p( 0, minY + 1 + extraSpread ) ).y,
        ( maxX < 1 || maxX >= resolution - 1 ) ? Number.NEGATIVE_INFINITY : transform.inversePosition2( p( maxX - extraSpread, 0 ) ).x,
        ( maxY < 1 || maxY >= resolution - 1 ) ? Number.NEGATIVE_INFINITY : transform.inversePosition2( p( 0, maxY - extraSpread ) ).y
      ),
      maxBounds: new Bounds2(
        ( minX < 1 || minX >= resolution - 1 ) ? Number.NEGATIVE_INFINITY : transform.inversePosition2( p( minX - 1 - extraSpread, 0 ) ).x,
        ( minY < 1 || minY >= resolution - 1 ) ? Number.NEGATIVE_INFINITY : transform.inversePosition2( p( 0, minY - 1 - extraSpread ) ).y,
        ( maxX < 1 || maxX >= resolution - 1 ) ? Number.POSITIVE_INFINITY : transform.inversePosition2( p( maxX + 2 + extraSpread, 0 ) ).x,
        ( maxY < 1 || maxY >= resolution - 1 ) ? Number.POSITIVE_INFINITY : transform.inversePosition2( p( 0, maxY + 2 + extraSpread ) ).y
      )
    };
  },

In the error case, maxX = 127 and resolution - 1 = 127.

jonathanolson commented 12 months ago

Theory: There might be something privacy-conscious to prevent fingerprinting (around fonts?) that could mess up getImageData() on a Canvas with text on it.

samreid commented 12 months ago

I'm not sure how scanBounds is used or what workaround may be applicable here. I'll assign to @jonathanolson but I'm unaware of how @kathy-phet wants to prioritize or schedule this.