From aca16ca4218675eced3290257174e9e9fba80066 Mon Sep 17 00:00:00 2001 From: Jonathan Olson Date: Tue, 2 Apr 2024 20:48:40 -0600 Subject: [PATCH] Potential Safari workaround for incorrect Quadratic paths - using de Casteljau subdivision (one level) on quadratic bezier curves in Safari, see https://github.com/phetsims/graphing-quadratics/issues/206 --- js/Shape.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/js/Shape.ts b/js/Shape.ts index 079979f..c05f0ec 100644 --- a/js/Shape.ts +++ b/js/Shape.ts @@ -1089,6 +1089,54 @@ class Shape implements CanApplyParsedSVG { return string; } + /** + * Returns something like "M150 0 L75 200 L225 200 Z" for a triangle (to be used with a SVG path element's 'd' + * attribute) + * + * Patched to work around https://github.com/phetsims/graphing-quadratics/issues/206, by splitting Quadratic Bezier + * segments into half with the de Casteljau algorithm. + */ + public getSVGPathWithSafariWorkaround(): string { + let string = ''; + const len = this.subpaths.length; + for ( let i = 0; i < len; i++ ) { + const subpath = this.subpaths[ i ]; + if ( subpath.isDrawable() ) { + // since the commands after this are relative to the previous 'point', we need to specify a move to the initial point + const startPoint = subpath.segments[ 0 ].start; + + string += `M ${svgNumber( startPoint.x )} ${svgNumber( startPoint.y )} `; + + for ( let k = 0; k < subpath.segments.length; k++ ) { + const segment = subpath.segments[ k ]; + if ( segment instanceof Quadratic ) { + const start = segment.start; + const control = segment.control; + const end = segment.end; + + const startMidX = ( start.x + control.x ) / 2; + const startMidY = ( start.y + control.y ) / 2; + const endMidX = ( control.x + end.x ) / 2; + const endMidY = ( control.y + end.y ) / 2; + const midX = ( startMidX + endMidX ) / 2; + const midY = ( startMidY + endMidY ) / 2; + + string += `Q ${svgNumber( startMidX )} ${svgNumber( startMidY )} ${svgNumber( midX )} ${svgNumber( midY )} `; + string += `Q ${svgNumber( endMidX )} ${svgNumber( endMidY )} ${svgNumber( end.x )} ${svgNumber( end.y )} `; + } + else { + string += `${subpath.segments[ k ].getSVGPathFragment()} `; + } + } + + if ( subpath.isClosed() ) { + string += 'Z '; + } + } + } + return string; + } + /** * Returns a new Shape that is transformed by the associated matrix */