From 33c96d8d8c0d1a8a875e410f1c75cebc0e140cd5 Mon Sep 17 00:00:00 2001 From: apple502j <33279053+apple502j@users.noreply.github.com> Date: Tue, 17 Mar 2020 15:40:55 +0900 Subject: [PATCH 1/8] Add if on edge, bonce block --- src/Sprite.js | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/Sprite.js b/src/Sprite.js index 62aff76..98f207d 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -606,6 +606,59 @@ export class Sprite extends SpriteBase { } while (t < 1); } + ifOnEdgeBounce() { + const nearestEdge = this.nearestEdge(); + if (!nearestEdge) return; + const rad = this.degToRad(this.scratchToDeg(this.direction)); + let dx = Math.cos(rad); + let dy = -Math.sin(rad); + switch (nearestEdge) { + case Sprite.Edge.LEFT: + dx = Math.max(0.2, Math.abs(dx)); + break; + case Sprite.Edge.RIGHT: + dx = -Math.max(0.2, Math.abs(dx)); + break; + case Sprite.Edge.TOP: + dy = Math.max(0.2, Math.abs(dy)); + break; + case Sprite.Edge.BOTTOM: + dy = -Math.max(0.2, Math.abs(dy)); + break; + } + this.direction = this.degToScratch(this.radToDeg(Math.atan2(dy, dx))); + const {x, y} = this.keepInFence(this.x, this.y); + this.goto(x, y); + } + + keepInFence(newX, newY) { + // https://github.com/LLK/scratch-vm/blob/develop/src/sprites/rendered-target.js#L949 + const fence = this.stage.fence; + const bounds = this._project.renderer.getBoundingBox(this); + bounds.left += (newX - this.x); + bounds.right += (newX - this.x); + bounds.top += (newY - this.y); + bounds.bottom += (newY - this.y); + + let dx = 0, dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + return ({ + x: newX + dx, + y: newY + dy + }); + } + get penDown() { return this._penDown; } @@ -705,6 +758,42 @@ export class Sprite extends SpriteBase { } } + nearestEdge() { + const bounds = this._project.renderer.getBoundingBox(this); + const {width: stageWidth, height: stageHeight} = this.stage; + const distLeft = Math.max(0, (stageWidth / 2) + bounds.left); + const distTop = Math.max(0, (stageHeight / 2) - bounds.top); + const distRight = Math.max(0, (stageWidth / 2) - bounds.right); + const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); + // Find the nearest edge. + let nearestEdge = ''; + let minDist = Infinity; + if (distLeft < minDist) { + minDist = distLeft; + nearestEdge = Sprite.Edge.LEFT; + } + if (distTop < minDist) { + minDist = distTop; + nearestEdge = Sprite.Edge.TOP; + } + if (distRight < minDist) { + minDist = distRight; + nearestEdge = Sprite.Edge.RIGHT; + } + if (distBottom < minDist) { + minDist = distBottom; + nearestEdge = Sprite.Edge.BOTTOM; + } + if (minDist > 0) { + nearestEdge = null; + } + return nearestEdge; + } + + touchingEdge() { + return !!this.nearestEdge(); + } + say(text) { clearTimeout(this._speechBubble.timeout); this._speechBubble = { text: String(text), style: "say", timeout: null }; @@ -750,6 +839,13 @@ Sprite.RotationStyle = Object.freeze({ DONT_ROTATE: Symbol("DONT_ROTATE") }); +Sprite.Edge = Object.freeze({ + BOTTOM: Symbol("BOTTOM"), + LEFT: Symbol("LEFT"), + RIGHT: Symbol("RIGHT"), + TOP: Symbol("TOP") +}); + export class Stage extends SpriteBase { constructor(initialConditions, ...args) { super(initialConditions, ...args); @@ -778,4 +874,12 @@ export class Stage extends SpriteBase { backdrop: this.costume.name }); } + get fence() { + return { + left: -this.width / 2, + right: this.width / 2, + top: this.height / 2, + bottom: -this.height / 2 + }; + } } From 8d644c6c02721c5fd43121cc4c250ebd023ec24f Mon Sep 17 00:00:00 2001 From: apple502j <33279053+apple502j@users.noreply.github.com> Date: Thu, 19 Mar 2020 01:36:32 +0900 Subject: [PATCH 2/8] Moved fence to constructor --- src/Sprite.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Sprite.js b/src/Sprite.js index 98f207d..278ba9f 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -863,6 +863,13 @@ export class Stage extends SpriteBase { } }); + this.fence = { + left: -this.width / 2, + right: this.width / 2, + top: this.height / 2, + bottom: -this.height / 2 + }; + this.name = "Stage"; // For obsolete counter blocks. @@ -874,12 +881,4 @@ export class Stage extends SpriteBase { backdrop: this.costume.name }); } - get fence() { - return { - left: -this.width / 2, - right: this.width / 2, - top: this.height / 2, - bottom: -this.height / 2 - }; - } } From 93abe8f85c847431ea535055f4176e62f0162142 Mon Sep 17 00:00:00 2001 From: apple502j <33279053+apple502j@users.noreply.github.com> Date: Sun, 22 Mar 2020 02:18:58 +0900 Subject: [PATCH 3/8] add it to touching --- src/Sprite.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Sprite.js b/src/Sprite.js index 278ba9f..8b9d360 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -790,10 +790,6 @@ export class Sprite extends SpriteBase { return nearestEdge; } - touchingEdge() { - return !!this.nearestEdge(); - } - say(text) { clearTimeout(this._speechBubble.timeout); this._speechBubble = { text: String(text), style: "say", timeout: null }; From 06a166a8a88a421b139c843849beb59dce4e46ca Mon Sep 17 00:00:00 2001 From: Clint Herron Date: Fri, 15 Jul 2022 20:47:32 -0400 Subject: [PATCH 4/8] Formatting cleanup with Prettier --- src/Sprite.js | 91 ++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/src/Sprite.js b/src/Sprite.js index 8b9d360..34b5859 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -614,20 +614,20 @@ export class Sprite extends SpriteBase { let dy = -Math.sin(rad); switch (nearestEdge) { case Sprite.Edge.LEFT: - dx = Math.max(0.2, Math.abs(dx)); - break; + dx = Math.max(0.2, Math.abs(dx)); + break; case Sprite.Edge.RIGHT: - dx = -Math.max(0.2, Math.abs(dx)); - break; + dx = -Math.max(0.2, Math.abs(dx)); + break; case Sprite.Edge.TOP: - dy = Math.max(0.2, Math.abs(dy)); - break; + dy = Math.max(0.2, Math.abs(dy)); + break; case Sprite.Edge.BOTTOM: - dy = -Math.max(0.2, Math.abs(dy)); - break; + dy = -Math.max(0.2, Math.abs(dy)); + break; } this.direction = this.degToScratch(this.radToDeg(Math.atan2(dy, dx))); - const {x, y} = this.keepInFence(this.x, this.y); + const { x, y } = this.keepInFence(this.x, this.y); this.goto(x, y); } @@ -635,12 +635,13 @@ export class Sprite extends SpriteBase { // https://github.com/LLK/scratch-vm/blob/develop/src/sprites/rendered-target.js#L949 const fence = this.stage.fence; const bounds = this._project.renderer.getBoundingBox(this); - bounds.left += (newX - this.x); - bounds.right += (newX - this.x); - bounds.top += (newY - this.y); - bounds.bottom += (newY - this.y); + bounds.left += newX - this.x; + bounds.right += newX - this.x; + bounds.top += newY - this.y; + bounds.bottom += newY - this.y; - let dx = 0, dy = 0; + let dx = 0, + dy = 0; if (bounds.left < fence.left) { dx += fence.left - bounds.left; } @@ -653,10 +654,10 @@ export class Sprite extends SpriteBase { if (bounds.bottom < fence.bottom) { dy += fence.bottom - bounds.bottom; } - return ({ + return { x: newX + dx, y: newY + dy - }); + }; } get penDown() { @@ -759,35 +760,35 @@ export class Sprite extends SpriteBase { } nearestEdge() { - const bounds = this._project.renderer.getBoundingBox(this); - const {width: stageWidth, height: stageHeight} = this.stage; - const distLeft = Math.max(0, (stageWidth / 2) + bounds.left); - const distTop = Math.max(0, (stageHeight / 2) - bounds.top); - const distRight = Math.max(0, (stageWidth / 2) - bounds.right); - const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom); - // Find the nearest edge. - let nearestEdge = ''; - let minDist = Infinity; - if (distLeft < minDist) { - minDist = distLeft; - nearestEdge = Sprite.Edge.LEFT; - } - if (distTop < minDist) { - minDist = distTop; - nearestEdge = Sprite.Edge.TOP; - } - if (distRight < minDist) { - minDist = distRight; - nearestEdge = Sprite.Edge.RIGHT; - } - if (distBottom < minDist) { - minDist = distBottom; - nearestEdge = Sprite.Edge.BOTTOM; - } - if (minDist > 0) { - nearestEdge = null; - } - return nearestEdge; + const bounds = this._project.renderer.getBoundingBox(this); + const { width: stageWidth, height: stageHeight } = this.stage; + const distLeft = Math.max(0, stageWidth / 2 + bounds.left); + const distTop = Math.max(0, stageHeight / 2 - bounds.top); + const distRight = Math.max(0, stageWidth / 2 - bounds.right); + const distBottom = Math.max(0, stageHeight / 2 + bounds.bottom); + // Find the nearest edge. + let nearestEdge = ""; + let minDist = Infinity; + if (distLeft < minDist) { + minDist = distLeft; + nearestEdge = Sprite.Edge.LEFT; + } + if (distTop < minDist) { + minDist = distTop; + nearestEdge = Sprite.Edge.TOP; + } + if (distRight < minDist) { + minDist = distRight; + nearestEdge = Sprite.Edge.RIGHT; + } + if (distBottom < minDist) { + minDist = distBottom; + nearestEdge = Sprite.Edge.BOTTOM; + } + if (minDist > 0) { + nearestEdge = null; + } + return nearestEdge; } say(text) { From adaabd6ad93bb4360aae0090e10ff2a8fbd2e3ac Mon Sep 17 00:00:00 2001 From: Clint Herron Date: Fri, 15 Jul 2022 20:49:27 -0400 Subject: [PATCH 5/8] Cleaning code to skip conversion to degrees before conversion to radians. Also, inverted Y-axis logic because the code copied from the Scratch VM does not take into account the y-axis difference between Scratch VM and Leopard. --- src/Sprite.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Sprite.js b/src/Sprite.js index 34b5859..e6bf350 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -609,9 +609,9 @@ export class Sprite extends SpriteBase { ifOnEdgeBounce() { const nearestEdge = this.nearestEdge(); if (!nearestEdge) return; - const rad = this.degToRad(this.scratchToDeg(this.direction)); + const rad = this.scratchToRad(this.direction); let dx = Math.cos(rad); - let dy = -Math.sin(rad); + let dy = Math.sin(rad); switch (nearestEdge) { case Sprite.Edge.LEFT: dx = Math.max(0.2, Math.abs(dx)); @@ -620,13 +620,13 @@ export class Sprite extends SpriteBase { dx = -Math.max(0.2, Math.abs(dx)); break; case Sprite.Edge.TOP: - dy = Math.max(0.2, Math.abs(dy)); + dy = -Math.max(0.2, Math.abs(dy)); break; case Sprite.Edge.BOTTOM: - dy = -Math.max(0.2, Math.abs(dy)); + dy = Math.max(0.2, Math.abs(dy)); break; } - this.direction = this.degToScratch(this.radToDeg(Math.atan2(dy, dx))); + this.direction = this.radToScratch(Math.atan2(dy, dx)); const { x, y } = this.keepInFence(this.x, this.y); this.goto(x, y); } From 90559dee20520a79a370cb67db28188eb8a7dda8 Mon Sep 17 00:00:00 2001 From: Clint Herron Date: Mon, 18 Jul 2022 10:47:42 -0400 Subject: [PATCH 6/8] Updating ifOnEdgeBounce to use new tight bounding box for more precise edge detection. --- src/Sprite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sprite.js b/src/Sprite.js index e6bf350..67caa21 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -760,7 +760,7 @@ export class Sprite extends SpriteBase { } nearestEdge() { - const bounds = this._project.renderer.getBoundingBox(this); + const bounds = this._project.renderer.getTightBoundingBox(this); const { width: stageWidth, height: stageHeight } = this.stage; const distLeft = Math.max(0, stageWidth / 2 + bounds.left); const distTop = Math.max(0, stageHeight / 2 - bounds.top); From 2f46f9dd2e2e14b2aa36517ae330e6a6f85b5750 Mon Sep 17 00:00:00 2001 From: Clint Herron Date: Mon, 18 Jul 2022 10:50:04 -0400 Subject: [PATCH 7/8] keepInFence should also use the tight bounding box. --- src/Sprite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sprite.js b/src/Sprite.js index 67caa21..424b80c 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -634,7 +634,7 @@ export class Sprite extends SpriteBase { keepInFence(newX, newY) { // https://github.com/LLK/scratch-vm/blob/develop/src/sprites/rendered-target.js#L949 const fence = this.stage.fence; - const bounds = this._project.renderer.getBoundingBox(this); + const bounds = this._project.renderer.getTightBoundingBox(this); bounds.left += newX - this.x; bounds.right += newX - this.x; bounds.top += newY - this.y; From 5186824f7941619b6870e587e83852b3cf9f8c9d Mon Sep 17 00:00:00 2001 From: Clint Herron Date: Sat, 11 Mar 2023 00:23:10 -0500 Subject: [PATCH 8/8] Merging KeepInFence and Goto calls into a single function call. Removing unnecessary no-op math as pointed out by @towerofnix. --- src/Sprite.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Sprite.js b/src/Sprite.js index 424b80c..26ee11e 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -627,18 +627,13 @@ export class Sprite extends SpriteBase { break; } this.direction = this.radToScratch(Math.atan2(dy, dx)); - const { x, y } = this.keepInFence(this.x, this.y); - this.goto(x, y); + this.positionInFence(); } - keepInFence(newX, newY) { + positionInFence() { // https://github.com/LLK/scratch-vm/blob/develop/src/sprites/rendered-target.js#L949 const fence = this.stage.fence; const bounds = this._project.renderer.getTightBoundingBox(this); - bounds.left += newX - this.x; - bounds.right += newX - this.x; - bounds.top += newY - this.y; - bounds.bottom += newY - this.y; let dx = 0, dy = 0; @@ -654,10 +649,8 @@ export class Sprite extends SpriteBase { if (bounds.bottom < fence.bottom) { dy += fence.bottom - bounds.bottom; } - return { - x: newX + dx, - y: newY + dy - }; + + this.goto(this.x + dx, this.y + dy); } get penDown() {