@@ -267,8 +267,7 @@ class RenderWebGL extends EventEmitter {
267
267
this . _yBottom = yBottom ;
268
268
this . _yTop = yTop ;
269
269
270
- // swap yBottom & yTop to fit Scratch convention of +y=up
271
- this . _projection = twgl . m4 . ortho ( xLeft , xRight , yBottom , yTop , - 1 , 1 ) ;
270
+ this . _projection = this . _makeOrthoProjection ( xLeft , xRight , yBottom , yTop ) ;
272
271
273
272
this . _setNativeSize ( Math . abs ( xRight - xLeft ) , Math . abs ( yBottom - yTop ) ) ;
274
273
}
@@ -292,6 +291,20 @@ class RenderWebGL extends EventEmitter {
292
291
this . emit ( RenderConstants . Events . NativeSizeChanged , { newSize : this . _nativeSize } ) ;
293
292
}
294
293
294
+ /**
295
+ * Build a projection matrix for Scratch coordinates. For example, `_makeOrthoProjection(-240,240,-180,180)` will
296
+ * mean the lower-left pixel is at (-240,-179) and the upper right pixel is at (239,180), matching Scratch 2.0.
297
+ * @param {number } xLeft - the left edge of the projection volume (-240)
298
+ * @param {number } xRight - the right edge of the projection volume (240)
299
+ * @param {number } yBottom - the bottom edge of the projection volume (-180)
300
+ * @param {number } yTop - the top edge of the projection volume (180)
301
+ * @returns {module:twgl/m4.Mat4 } - a projection matrix containing [xLeft,xRight) and (yBottom,yTop]
302
+ */
303
+ _makeOrthoProjection ( xLeft , xRight , yBottom , yTop ) {
304
+ // swap yBottom & yTop to fit Scratch convention of +y=up
305
+ return twgl . m4 . ortho ( xLeft , xRight , yBottom , yTop , - 1 , 1 ) ;
306
+ }
307
+
295
308
/**
296
309
* Create a new bitmap skin from a snapshot of the provided bitmap data.
297
310
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement } bitmapData - new contents for this skin.
@@ -529,7 +542,7 @@ class RenderWebGL extends EventEmitter {
529
542
* Returns the position of the given drawableID in the draw list. This is
530
543
* the absolute position irrespective of layer group.
531
544
* @param {number } drawableID The drawable ID to find.
532
- * @return {number } The postion of the given drawable ID.
545
+ * @return {number } The position of the given drawable ID.
533
546
*/
534
547
getDrawableOrder ( drawableID ) {
535
548
return this . _drawList . indexOf ( drawableID ) ;
@@ -543,7 +556,7 @@ class RenderWebGL extends EventEmitter {
543
556
* "go to back": setDrawableOrder(id, 1); (assuming stage at 0).
544
557
* "go to front": setDrawableOrder(id, Infinity);
545
558
* @param {int } drawableID ID of Drawable to reorder.
546
- * @param {number } order New absolute order or relative order adjusment .
559
+ * @param {number } order New absolute order or relative order adjustment .
547
560
* @param {string= } group Name of layer group drawable belongs to.
548
561
* Reordering will not take place if drawable cannot be found within the bounds
549
562
* of the layer group.
@@ -714,7 +727,7 @@ class RenderWebGL extends EventEmitter {
714
727
715
728
/**
716
729
* Check if a particular Drawable is touching a particular color.
717
- * Unlike touching drawable, if the "tester" is invisble , we will still test.
730
+ * Unlike touching drawable, if the "tester" is invisible , we will still test.
718
731
* @param {int } drawableID The ID of the Drawable to check.
719
732
* @param {Array<int> } color3b Test if the Drawable is touching this color.
720
733
* @param {Array<int> } [mask3b] Optionally mask the check to this part of Drawable.
@@ -738,7 +751,7 @@ class RenderWebGL extends EventEmitter {
738
751
739
752
// if there are just too many pixels to CPU render efficiently, we need to let readPixels happen
740
753
if ( bounds . width * bounds . height * ( candidates . length + 1 ) >= maxPixelsForCPU ) {
741
- this . _isTouchingColorGpuStart ( drawableID , candidates . map ( ( { id} ) => id ) . reverse ( ) , bounds , color3b , mask3b ) ;
754
+ this . _isTouchingColorGpuStart ( drawableID , candidates . map ( ( { id} ) => id ) , bounds , color3b , mask3b ) ;
742
755
}
743
756
744
757
const drawable = this . _allDrawables [ drawableID ] ;
@@ -747,24 +760,24 @@ class RenderWebGL extends EventEmitter {
747
760
const hasMask = Boolean ( mask3b ) ;
748
761
749
762
// Scratch Space - +y is top
750
- for ( let y = bounds . bottom ; y <= bounds . top ; y ++ ) {
751
- if ( bounds . width * ( y - bounds . bottom ) * ( candidates . length + 1 ) >= maxPixelsForCPU ) {
752
- return this . _isTouchingColorGpuFin ( bounds , color3b , y - bounds . bottom ) ;
763
+ for ( let y = 0 ; y < bounds . height ; ++ y ) {
764
+ if ( bounds . width * y * ( candidates . length + 1 ) >= maxPixelsForCPU ) {
765
+ return this . _isTouchingColorGpuFin ( bounds , color3b , y ) ;
753
766
}
754
- for ( let x = bounds . left ; x <= bounds . right ; x ++ ) {
755
- point [ 1 ] = y ;
756
- point [ 0 ] = x ;
767
+ for ( let x = 0 ; x < bounds . width ; ++ x ) {
768
+ point [ 0 ] = bounds . left + x ; // bounds.left <= point[0] < bounds.right
769
+ point [ 1 ] = bounds . top - y ; // bounds.bottom < point[1] <= bounds.top ("flipped")
757
770
// if we use a mask, check our sample color...
758
771
if ( hasMask ?
759
772
maskMatches ( Drawable . sampleColor4b ( point , drawable , color ) , mask3b ) :
760
773
drawable . isTouching ( point ) ) {
761
774
RenderWebGL . sampleColor3b ( point , candidates , color ) ;
762
775
if ( debugCanvasContext ) {
763
776
debugCanvasContext . fillStyle = `rgb(${ color [ 0 ] } ,${ color [ 1 ] } ,${ color [ 2 ] } )` ;
764
- debugCanvasContext . fillRect ( x - bounds . left , bounds . bottom - y , 1 , 1 ) ;
777
+ debugCanvasContext . fillRect ( x , y , 1 , 1 ) ;
765
778
}
766
779
// ...and the target color is drawn at this pixel
767
- if ( colorMatches ( color , color3b , 0 ) ) {
780
+ if ( colorMatches ( color3b , color , 0 ) ) {
768
781
return true ;
769
782
}
770
783
}
@@ -794,7 +807,7 @@ class RenderWebGL extends EventEmitter {
794
807
// Limit size of viewport to the bounds around the target Drawable,
795
808
// and create the projection matrix for the draw.
796
809
gl . viewport ( 0 , 0 , bounds . width , bounds . height ) ;
797
- const projection = twgl . m4 . ortho ( bounds . left , bounds . right , bounds . top , bounds . bottom , - 1 , 1 ) ;
810
+ const projection = this . _makeOrthoProjection ( bounds . left , bounds . right , bounds . top , bounds . bottom ) ;
798
811
799
812
let fillBackgroundColor = this . _backgroundColor ;
800
813
@@ -877,7 +890,7 @@ class RenderWebGL extends EventEmitter {
877
890
const candidates = this . _candidatesTouching ( drawableID ,
878
891
// even if passed an invisible drawable, we will NEVER touch it!
879
892
candidateIDs . filter ( id => this . _allDrawables [ id ] . _visible ) ) ;
880
- // if we are invisble we don't touch anything.
893
+ // if we are invisible we don't touch anything.
881
894
if ( candidates . length === 0 || ! this . _allDrawables [ drawableID ] . _visible ) {
882
895
return false ;
883
896
}
@@ -910,7 +923,7 @@ class RenderWebGL extends EventEmitter {
910
923
911
924
/**
912
925
* Convert a client based x/y position on the canvas to a Scratch 3 world space
913
- * Rectangle. This creates recangles with a radius to cover selecting multiple
926
+ * Rectangle. This creates rectangles with a radius to cover selecting multiple
914
927
* scratch pixels with touch / small render areas.
915
928
*
916
929
* @param {int } centerX The client x coordinate of the picking location.
@@ -1027,7 +1040,7 @@ class RenderWebGL extends EventEmitter {
1027
1040
for ( worldPos [ 0 ] = bounds . left ; worldPos [ 0 ] <= bounds . right ; worldPos [ 0 ] ++ ) {
1028
1041
1029
1042
// Check candidates in the reverse order they would have been
1030
- // drawn. This will determine what candiate 's silhouette pixel
1043
+ // drawn. This will determine what candidate 's silhouette pixel
1031
1044
// would have been drawn at the point.
1032
1045
for ( let d = candidateIDs . length - 1 ; d >= 0 ; d -- ) {
1033
1046
const id = candidateIDs [ d ] ;
@@ -1111,7 +1124,7 @@ class RenderWebGL extends EventEmitter {
1111
1124
// Limit size of viewport to the bounds around the target Drawable,
1112
1125
// and create the projection matrix for the draw.
1113
1126
gl . viewport ( 0 , 0 , bounds . width , bounds . height ) ;
1114
- const projection = twgl . m4 . ortho ( bounds . left , bounds . right , bounds . top , bounds . bottom , - 1 , 1 ) ;
1127
+ const projection = this . _makeOrthoProjection ( bounds . left , bounds . right , bounds . top , bounds . bottom ) ;
1115
1128
1116
1129
gl . clearColor ( 0 , 0 , 0 , 0 ) ;
1117
1130
gl . clear ( gl . COLOR_BUFFER_BIT ) ;
@@ -1186,7 +1199,7 @@ class RenderWebGL extends EventEmitter {
1186
1199
const pickY = bounds . top - scratchY ;
1187
1200
1188
1201
gl . viewport ( 0 , 0 , bounds . width , bounds . height ) ;
1189
- const projection = twgl . m4 . ortho ( bounds . left , bounds . right , bounds . top , bounds . bottom , - 1 , 1 ) ;
1202
+ const projection = this . _makeOrthoProjection ( bounds . left , bounds . right , bounds . top , bounds . bottom ) ;
1190
1203
1191
1204
gl . clearColor . apply ( gl , this . _backgroundColor ) ;
1192
1205
gl . clear ( gl . COLOR_BUFFER_BIT ) ;
@@ -1255,8 +1268,7 @@ class RenderWebGL extends EventEmitter {
1255
1268
}
1256
1269
1257
1270
/**
1258
- * Filter a list of candidates for a touching query into only those that
1259
- * could possibly intersect the given bounds.
1271
+ * Filter a list of candidates for a touching query into only those that could possibly intersect the given bounds.
1260
1272
* @param {int } drawableID - ID for drawable of query.
1261
1273
* @param {Array<int> } candidateIDs - Candidates for touching query.
1262
1274
* @return {?Array< {id, drawable, intersection} > } Filtered candidates with useful data.
@@ -1267,8 +1279,7 @@ class RenderWebGL extends EventEmitter {
1267
1279
if ( bounds === null ) {
1268
1280
return result ;
1269
1281
}
1270
- // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check
1271
- for ( let index = candidateIDs . length - 1 ; index >= 0 ; index -- ) {
1282
+ for ( let index = 0 ; index < candidateIDs . length ; ++ index ) {
1272
1283
const id = candidateIDs [ index ] ;
1273
1284
if ( id !== drawableID ) {
1274
1285
const drawable = this . _allDrawables [ id ] ;
@@ -1428,7 +1439,7 @@ class RenderWebGL extends EventEmitter {
1428
1439
1429
1440
// Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw.
1430
1441
gl . viewport ( 0 , 0 , bounds . width , bounds . height ) ;
1431
- const projection = twgl . m4 . ortho ( bounds . left , bounds . right , bounds . top , bounds . bottom , - 1 , 1 ) ;
1442
+ const projection = this . _makeOrthoProjection ( bounds . left , bounds . right , bounds . top , bounds . bottom ) ;
1432
1443
1433
1444
gl . clearColor ( 0 , 0 , 0 , 0 ) ;
1434
1445
gl . clear ( gl . COLOR_BUFFER_BIT ) ;
@@ -1517,7 +1528,7 @@ class RenderWebGL extends EventEmitter {
1517
1528
* can skip superfluous extra state calls when it is already in that
1518
1529
* region. Since one region may be entered from within another a exit
1519
1530
* handle can also be registered that is called when a new region is about
1520
- * to be entered to restore a common inbetween state.
1531
+ * to be entered to restore a common in-between state.
1521
1532
*
1522
1533
* @param {any } regionId - id of the region to enter
1523
1534
* @param {function } enter - handle to call when first entering a region
@@ -1649,7 +1660,7 @@ class RenderWebGL extends EventEmitter {
1649
1660
*
1650
1661
* The determinant is useful in this case to know if AC is counter
1651
1662
* clockwise from AB. A positive value means the AC is counter
1652
- * clockwise from AC. A negative value menas AC is clockwise from AB.
1663
+ * clockwise from AC. A negative value means AC is clockwise from AB.
1653
1664
*
1654
1665
* @param {Float32Array } A A 2d vector in space.
1655
1666
* @param {Float32Array } B A 2d vector in space.
@@ -1754,16 +1765,15 @@ class RenderWebGL extends EventEmitter {
1754
1765
* Sample a "final" color from an array of drawables at a given scratch space.
1755
1766
* Will blend any alpha values with the drawables "below" it.
1756
1767
* @param {twgl.v3 } vec Scratch Vector Space to sample
1757
- * @param {Array<Drawables> } drawables A list of drawables with the "top most"
1758
- * drawable at index 0
1768
+ * @param {Array<Drawables> } drawables A list of drawables with the "bottom most" drawable at index 0
1759
1769
* @param {Uint8ClampedArray } dst The color3b space to store the answer in.
1760
1770
* @return {Uint8ClampedArray } The dst vector with everything blended down.
1761
1771
*/
1762
1772
static sampleColor3b ( vec , drawables , dst ) {
1763
1773
dst = dst || new Uint8ClampedArray ( 3 ) ;
1764
1774
dst . fill ( 0 ) ;
1765
1775
let blendAlpha = 1 ;
1766
- for ( let index = 0 ; blendAlpha !== 0 && index < drawables . length ; index ++ ) {
1776
+ for ( let index = drawables . length - 1 ; blendAlpha !== 0 && index >= 0 ; -- index ) {
1767
1777
/*
1768
1778
if (left > vec[0] || right < vec[0] ||
1769
1779
bottom > vec[1] || top < vec[0]) {
0 commit comments