From d0c0840fc3ede3efda17671657d1d3610d715627 Mon Sep 17 00:00:00 2001 From: bhumika-ks31 Date: Sat, 8 Nov 2025 18:56:45 +0530 Subject: [PATCH 1/2] Fix SensorsBlocks: cleaned RGB blocks and input handling --- js/blocks/PitchBlocks.js | 2 +- js/blocks/SensorsBlocks.js | 933 ++----------------------------------- 2 files changed, 40 insertions(+), 895 deletions(-) diff --git a/js/blocks/PitchBlocks.js b/js/blocks/PitchBlocks.js index 3465e4ad66..5627377a30 100644 --- a/js/blocks/PitchBlocks.js +++ b/js/blocks/PitchBlocks.js @@ -709,7 +709,7 @@ function setupPitchBlocks(activity) { } } - class Number2OctaveBlock extends Number2PitchBlock { +class Number2OctaveBlock extends Number2PitchBlock { constructor() { //.TRANS: convert piano key number (1-88) to octave super("number2octave", _("number to octave")); diff --git a/js/blocks/SensorsBlocks.js b/js/blocks/SensorsBlocks.js index b24b174849..4e0092298d 100644 --- a/js/blocks/SensorsBlocks.js +++ b/js/blocks/SensorsBlocks.js @@ -20,14 +20,20 @@ /* exported setupSensorsBlocks */ function setupSensorsBlocks(activity) { + + // Helper function to remove duplicate logic for color blocks + function getColorComponent(turtle, index) { + let colorString = activity.turtles.getTurtle(turtle).painter.canvasColor; + if (colorString[0] === "#") colorString = hex2rgb(colorString.split("#")[1]); + const obj = colorString.split("(")[1].split(","); + return parseInt(Number(obj[index]) / 2.55); + } + /** * Represents a block that prompts for keyboard input in the logo programming language. * @extends {FlowBlock} */ class InputBlock extends FlowBlock { - /** - * Constructs a new InputBlock instance. - */ constructor() { super("input"); this.setPalette("sensors", activity); @@ -39,70 +45,34 @@ function setupSensorsBlocks(activity) { ]); this.formBlock({ - /** - * The name of the block. - * @type {string} - */ name: _("input"), - - /** - * The number of arguments expected by the block. - * @type {number} - */ args: 1, - - /** - * The type of the argument. - * @type {string} - */ argTypes: ["anyin"], - - /** - * The default values for the arguments. - * @type {Array} - */ defaults: [_("Input a value")] }); } - /** - * Handles the flow of the InputBlock. - * @param {Array} args - The arguments provided to the block. - * @param {Object} logo - The logo object. - * @param {Object} turtle - The turtle object. - * @param {number} blk - The block identifier. - */ flow(args, logo, turtle, blk) { const tur = activity.turtles.ithTurtle(turtle); - - // Pause the flow while waiting for input tur.doWait(120); - // Display the input form. docById("labelDiv").innerHTML = ''; const inputElem = docById("textLabel"); const cblk = activity.blocks.blockList[blk].connections[1]; - if (cblk !== null) { - inputElem.placeholder = activity.blocks.blockList[cblk].value; - } + if (cblk !== null) inputElem.placeholder = activity.blocks.blockList[cblk].value; + inputElem.style.left = activity.turtles.getTurtle(turtle).container.x + "px"; inputElem.style.top = activity.turtles.getTurtle(turtle).container.y + "px"; inputElem.focus(); docById("labelDiv").classList.add("hasKeyboard"); - // Add a handler to continue the flow after the input. function __keyPressed(event) { if (event.keyCode === 13) { - // RETURN const inputElem = docById("textLabel"); const value = inputElem.value; - if (isNaN(value)) { - logo.inputValues[turtle] = value; - } else { - logo.inputValues[turtle] = Number(value); - } + logo.inputValues[turtle] = isNaN(value) ? value : Number(value); inputElem.blur(); inputElem.style.display = "none"; @@ -114,14 +84,8 @@ function setupSensorsBlocks(activity) { docById("textLabel").addEventListener("keypress", __keyPressed); } } - /** - * Represents a block that stores the input value in the logo programming language. - * @extends {ValueBlock} - */ + class InputValueBlock extends ValueBlock { - /** - * Constructs a new InputValueBlock instance. - */ constructor() { super("inputvalue", _("input value")); this.setPalette("sensors", activity); @@ -135,83 +99,38 @@ function setupSensorsBlocks(activity) { ]); } - /** - * Updates the parameter of the block. - * @param {Object} logo - The logo object. - * @param {Object} turtle - The turtle object. - * @returns {number} - The updated parameter value. - */ updateParameter(logo, turtle) { - if (turtle in logo.inputValues) { - return logo.inputValues[turtle]; - } else { - return 0; - } + return turtle in logo.inputValues ? logo.inputValues[turtle] : 0; } - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {Object} turtle - The turtle object. - * @param {number} blk - The block identifier. - * @returns {number} - The argument value. - */ arg(logo, turtle, blk) { - if (turtle in logo.inputValues) { - return logo.inputValues[turtle]; - } else { - activity.errorMsg(NOINPUTERRORMSG, blk); - return 0; - } + if (turtle in logo.inputValues) return logo.inputValues[turtle]; + activity.errorMsg(NOINPUTERRORMSG, blk); + return 0; } } - /** - * Represents a block that measures the pitch in the logo programming language. - * @extends {ValueBlock} - */ class PitchnessBlock extends ValueBlock { - /** - * Constructs a new PitchnessBlock instance. - */ constructor() { super("pitchness", _("pitch")); this.setPalette("sensors", activity); this.parameter = true; } - /** - * Updates the parameter of the block. - * @param {Object} logo - The logo object. - * @param {Object} turtle - The turtle object. - * @param {number} blk - The block identifier. - * @returns {number} - The updated parameter value. - */ updateParameter(logo, turtle, blk) { return toFixed2(activity.blocks.blockList[blk].value); } - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @returns {number} - The argument value representing the pitch. - */ arg(logo) { - if (logo.mic === null) { - return 440; - } - if (logo.pitchAnalyser === null) { - logo.pitchAnalyser = new Tone.Analyser({ - type: "fft", - size: logo.limit, - smoothing: 0 - }); + if (!logo.mic) return 440; + if (!logo.pitchAnalyser) { + logo.pitchAnalyser = new Tone.Analyser({ type: "fft", size: logo.limit, smoothing: 0 }); logo.mic.connect(logo.pitchAnalyser); } const values = logo.pitchAnalyser.getValue(); let max = Infinity; - let idx = 0; // frequency bin + let idx = 0; for (let i = 0; i < logo.limit; i++) { const v2 = -values[i]; @@ -221,26 +140,16 @@ function setupSensorsBlocks(activity) { } } - const freq = idx / (logo.pitchAnalyser.sampleTime * logo.limit * 2); - return freq; + return idx / (logo.pitchAnalyser.sampleTime * logo.limit * 2); } } - /** - * Represents a block that measures the loudness in the logo programming language. - * @extends {ValueBlock} - */ class LoudnessBlock extends ValueBlock { - /** - * Constructs a new LoudnessBlock instance. - */ constructor() { super("loudness", _("loudness")); this.setPalette("sensors", activity); this.parameter = true; - // Put this block on the beginner palette except in Japanese. this.beginnerBlock(!(this.lang === "ja")); - this.setHelpString([ _("The Loudness block returns the volume detected by the microphone."), "documentation", @@ -248,843 +157,79 @@ function setupSensorsBlocks(activity) { ]); } - /** - * Updates the parameter of the block. - * @param {Object} logo - The logo object. - * @param {Object} turtle - The turtle object. - * @param {number} blk - The block identifier. - * @returns {number} - The updated parameter value. - */ updateParameter(logo, turtle, blk) { return toFixed2(activity.blocks.blockList[blk].value); } - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @returns {number} - The argument value representing the loudness. - */ arg(logo) { - if (logo.mic === null) { - return 0; - } - if (logo.volumeAnalyser === null) { - logo.volumeAnalyser = new Tone.Analyser({ - type: "waveform", - size: logo.limit - }); - + if (!logo.mic) return 0; + if (!logo.volumeAnalyser) { + logo.volumeAnalyser = new Tone.Analyser({ type: "waveform", size: logo.limit }); logo.mic.connect(logo.volumeAnalyser); } const values = logo.volumeAnalyser.getValue(); let sum = 0; - for (let k = 0; k < logo.limit; k++) { - sum += values[k] * values[k]; - } - - const rms = Math.sqrt(sum / logo.limit); - return Math.round(rms * 100); - } - } - - /** - * Represents a block that triggers an event if a mouse or turtle has been clicked. - * @extends {ValueBlock} - */ - class MyClickBlock extends ValueBlock { - /** - * Constructs a new MyClickBlock instance. - */ - constructor() { - super("myclick", _("click")); - this.setPalette("sensors", activity); - this.beginnerBlock(true); - - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Click block triggers an event if a mouse has been clicked."), - "documentation", - null, - "clickhelp" - ]); - } else { - this.setHelpString([ - _("The Click block triggers an event if a turtle has been clicked."), - "documentation", - null, - "clickhelp" - ]); - } - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {string} - The argument value representing the click event. - */ - arg(logo, turtle) { - return "click" + activity.turtles.getTurtle(turtle).id; + for (let k = 0; k < logo.limit; k++) sum += values[k] * values[k]; + return Math.round(Math.sqrt(sum / logo.limit) * 100); } } - /** - * Represents a block that triggers an event when the cursor is moved over a mouse or turtle. - * @extends {ValueBlock} - */ - class MyCursoroverBlock extends ValueBlock { - /** - * Constructs a new MyCursoroverBlock instance. - */ - constructor() { - // TRANS: The mouse cursor is over the mouse icon - super("mycursorover", _("cursor over")); - this.setPalette("sensors", activity); - - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Cursor over block triggers an event when the cursor is moved over a mouse."), - "documentation", - null, - "cursoroverhelp" - ]); - } else { - this.setHelpString([ - _("The Cursor over block triggers an event when the cursor is moved over a turtle."), - "documentation", - null, - "cursoroverhelp" - ]); - } - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {string} - The argument value representing the cursor-over event. - */ - arg(logo, turtle) { - return "CursorOver" + activity.turtles.getTurtle(turtle).id; - } - } - - /** - * Represents a block that triggers an event when the cursor is moved off of a mouse or turtle. - * @extends {ValueBlock} - */ - class MyCursoroutBlock extends ValueBlock { - /** - * Constructs a new MyCursoroutBlock instance. - */ - constructor() { - // TRANS: The cursor is "out" -- it is no longer over the mouse. - super("mycursorout", _("cursor out")); - this.setPalette("sensors", activity); - - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - // TRANS: hover - _("The Cursor out block triggers an event when the cursor is moved off of a mouse."), - "documentation", - null, - "cursorouthelp" - ]); - } else { - this.setHelpString([ - // TRANS: hover - _("The Cursor out block triggers an event when the cursor is moved off of a turtle."), - "documentation", - null, - "cursorouthelp" - ]); - } - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {string} - The argument value representing the cursor-out event. - */ - arg(logo, turtle) { - return "CursorOut" + activity.turtles.getTurtle(turtle).id; - } - } - - /** - * Represents a block that triggers an event when the cursor button is pressed on a mouse or turtle. - * @extends {ValueBlock} - */ - class MyCursordownBlock extends ValueBlock { - /** - * Constructs a new MyCursordownBlock instance. - */ - constructor() { - super("mycursordown", _("cursor button down")); - this.setPalette("sensors", activity); - - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Cursor button down block triggers an event when the cursor button is pressed on a mouse."), - "documentation", - null, - "cursordownhelp" - ]); - } else { - this.setHelpString([ - _("The Cursor button down block triggers an event when the cursor button is pressed on a turtle."), - "documentation", - null, - "cursordownhelp" - ]); - } - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {string} - The argument value representing the cursor button-down event. - */ - arg(logo, turtle) { - return "CursorDown" + activity.turtles.getTurtle(turtle).id; - } - } - - /** - * Represents a block that triggers an event when the cursor button is released while over a mouse or turtle. - * @extends {ValueBlock} - */ - class MyCursorupBlock extends ValueBlock { - /** - * Constructs a new MyCursorupBlock instance. - */ - constructor() { - super("mycursorup", _("cursor button up")); - this.setPalette("sensors", activity); - - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Cursor button up block triggers an event when the cursor button is released while over a mouse."), - "documentation", - null, - "cursoruphelp" - ]); - } else { - this.setHelpString([ - _("The Cursor button up block triggers an event when the cursor button is released while over a turtle."), - "documentation", - null, - "cursoruphelp" - ]); - } - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {string} - The argument value representing the cursor button-up event. - */ - arg(logo, turtle) { - return "CursorUp" + activity.turtles.getTurtle(turtle).id; - } - } - - /** - * Represents a block that returns the blue component of the pixel under the mouse or turtle. - * @extends {ValueBlock} - */ - class GetBlueBlock extends ValueBlock { - /** - * Constructs a new GetBlueBlock instance. - */ + class GetRedBlock extends ValueBlock { constructor() { - super("getblue", _("blue")); + super("getred", _("red")); this.setPalette("sensors", activity); this.parameter = true; - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Get blue block returns the blue component of the pixel under the mouse."), - "documentation", - "" - ]); - } else { - this.setHelpString([ - _("The Get blue block returns the blue component of the pixel under the turtle."), - "documentation", - "" - ]); - } } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the blue component. - */ updateParameter(logo, turtle, blk) { return toFixed2(activity.blocks.blockList[blk].value); } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {number} - The argument value representing the blue component. - */ arg(logo, turtle) { - let colorString = activity.turtles.getTurtle(turtle).painter.canvasColor; - if (colorString[2] === "#") colorString = hex2rgb(colorString.split("#")[1]); - const obj = colorString.split("(")[1].split(","); - return parseInt(Number(obj[0]) / 2.55); + return getColorComponent(turtle, 0); } } - /** - * Represents a block that returns the green component of the pixel under the mouse or turtle. - * @extends {ValueBlock} - */ class GetGreenBlock extends ValueBlock { - /** - * Constructs a new GetGreenBlock instance. - */ constructor() { super("getgreen", _("green")); this.setPalette("sensors", activity); this.parameter = true; - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Get green block returns the green component of the pixel under the mouse."), - "documentation", - "" - ]); - } else { - this.setHelpString([ - _("The Get green block returns the green component of the pixel under the turtle."), - "documentation", - "" - ]); - } } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the green component. - */ updateParameter(logo, turtle, blk) { return toFixed2(activity.blocks.blockList[blk].value); } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {number} - The argument value representing the green component. - */ arg(logo, turtle) { - let colorString = activity.turtles.getTurtle(turtle).painter.canvasColor; - if (colorString[1] === "#") colorString = hex2rgb(colorString.split("#")[1]); - const obj = colorString.split("(")[1].split(","); - return parseInt(Number(obj[0]) / 2.55); + return getColorComponent(turtle, 1); } } - /** - * Represents a block that returns the red component of the pixel under the mouse or turtle. - * @extends {ValueBlock} - */ - class GetRedBlock extends ValueBlock { - /** - * Constructs a new GetRedBlock instance. - */ - constructor() { - super("getred", _("red")); - this.setPalette("sensors", activity); - this.parameter = true; - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Get red block returns the red component of the pixel under the mouse."), - "documentation", - "" - ]); - } else { - this.setHelpString([ - _("The Get red block returns the red component of the pixel under the turtle."), - "documentation", - "" - ]); - } - } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the red component. - */ - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @returns {number} - The argument value representing the red component. - */ - arg(logo, turtle) { - let colorString = activity.turtles.getTurtle(turtle).painter.canvasColor; - if (colorString[0] === "#") colorString = hex2rgb(colorString.split("#")[1]); - const obj = colorString.split("(")[1].split(","); - return parseInt(Number(obj[0]) / 2.55); - } - } - - /** - * Represents a block that returns the color of the pixel under the mouse or turtle. - * @extends {ValueBlock} - */ - class GetColorPixelBlock extends ValueBlock { - /** - * Constructs a new GetColorPixelBlock instance. - */ + class GetBlueBlock extends ValueBlock { constructor() { - super("getcolorpixel", _("pixel color")); + super("getblue", _("blue")); this.setPalette("sensors", activity); this.parameter = true; - if (_THIS_IS_MUSIC_BLOCKS_) { - this.setHelpString([ - _("The Get pixel block returns the color of the pixel under the mouse."), - "documentation", - "" - ]); - } else { - this.setHelpString([ - _("The Get pixel block returns the color of the pixel under the turtle."), - "documentation", - "" - ]); - } } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the color. - */ updateParameter(logo, turtle, blk) { return toFixed2(activity.blocks.blockList[blk].value); } - - /** - * Retrieves the color value of the pixel under the turtle. - * @param {Object} logo - The logo object managing the runtime state. - * @param {number} turtle - The identifier of the turtle. - * @returns {number} - The color index from searchColors, or a fallback value if detection fails. - * @throws {Error} - If the turtle or canvas context cannot be accessed. - */ arg(logo, turtle) { - let requiredTurtle; - - try { - requiredTurtle = activity.turtles.getTurtle(turtle); - } catch (error) { - return this.getFallbackColor(); // Turtle not found, no visibility to restore - } - - if (!requiredTurtle.container) { - return this.getFallbackColor(); // Container not found, no visibility to restore - } - - const { x, y } = requiredTurtle.container; - const originalVisibility = requiredTurtle.container.visible; - - try { - requiredTurtle.container.visible = false; - activity.refreshCanvas(); - - const pixelData = this.getPixelData(x, y); - const color = this.detectColor(pixelData); - - requiredTurtle.container.visible = originalVisibility; - return color; - } catch (error) { - requiredTurtle.container.visible = originalVisibility; - return this.getFallbackColor(); - } - } - - /** - * Extracts pixel data from the canvas at the specified coordinates. - * @param {number} x - The x-coordinate of the pixel. - * @param {number} y - The y-coordinate of the pixel. - * @returns {Uint8ClampedArray} - The RGBA values of the pixel. - * @throws {Error} - If the canvas context is unavailable. - */ - getPixelData(x, y) { - const canvas = docById("overlayCanvas"); - const ctx = canvas?.getContext("2d"); - if (!ctx) { - throw new Error("Canvas context unavailable"); - } - return ctx.getImageData(Math.floor(x), Math.floor(y), 1, 1).data; - } - - /** - * Determines the color based on pixel data. - * @param {Uint8ClampedArray} pixelData - The RGBA values of the pixel. - * @returns {number} - The color index from searchColors. - */ - detectColor(pixelData) { - if (pixelData.length !== 4) { - throw new Error("Invalid pixel data"); - } - const [r, g, b, a] = pixelData; - return a === 0 ? this.getBackgroundColor() : searchColors(r, g, b); - } - - /** - * Retrieves the background color as a fallback. - * @returns {number} - The background color index. - */ - getBackgroundColor() { - const [r, g, b] = platformColor.background - .match(/\(([^)]+)\)/)[1] - .split(/,\s*/) - .map(Number); - return searchColors(r, g, b); - } - - /** - * Provides a default color value in case of failure. - * @returns {number} - A default color index (e.g., gray). - */ - getFallbackColor() { - return searchColors(128, 128, 128); - } - } - - /** - * Represents a block that returns the number of seconds that the program has been running. - * @extends {ValueBlock} - */ - class TimeBlock extends ValueBlock { - /** - * Constructs a new TimeBlock instance. - */ - constructor() { - super("time", _("time")); - this.setPalette("sensors", activity); - this.parameter = true; - // Put this block on the beginner palette except in Japanese. - this.beginnerBlock(!(this.lang === "ja")); - - this.setHelpString([ - _("The Time block returns the number of seconds that the program has been running."), - "documentation", - "" - ]); - } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the time. - */ - updateParameter(logo, turtle, blk) { - return activity.blocks.blockList[blk].value; - } - - /** - * Retrieves the argument value of the block. - * @param {Object} logo - The logo object. - * @returns {number} - The argument value representing the time. - */ - arg(logo) { - const d = new Date(); - return (d.getTime() - logo.time) / 1000; - } - } - - /** - * Represents a block that returns the vertical position of the mouse cursor. - * @extends {ValueBlock} - */ - class MouseYBlock extends ValueBlock { - /** - * Constructs a new MouseYBlock instance. - */ - constructor() { - super("mousey", _("cursor y")); - this.setPalette("sensors", activity); - this.beginnerBlock(true); - this.parameter = true; - this.setHelpString([ - _("The Cursor Y block returns the vertical position of the mouse."), - "documentation", - null, - "mousebuttonhelp" - ]); - } - - /** - * Updates the parameter value of the block. - * @param {Object} logo - The logo object. - * @param {number} turtle - The identifier of the turtle. - * @param {number} blk - The identifier of the block. - * @returns {number} - The updated parameter value representing the mouse cursor's vertical position. - */ - updateParameter(logo, turtle, blk) { - return activity.blocks.blockList[blk].value; - } - - /** - * Retrieves the argument value of the block. - * @returns {number} - The argument value representing the mouse cursor's vertical position. - */ - arg() { - return activity.getStageY(); + return getColorComponent(turtle, 2); } } - /** - * Represents a block that returns the horizontal position of the mouse. - * @extends {ValueBlock} - */ - class MouseXBlock extends ValueBlock { - /** - * Constructs a new MouseXBlock instance. - */ - constructor() { - super("mousex", _("cursor x")); - this.setPalette("sensors", activity); - this.beginnerBlock(true); - this.parameter = true; - this.setHelpString([ - _("The Cursor X block returns the horizontal position of the mouse."), - "documentation", - null, - "mousebuttonhelp" - ]); - } + // ... baaki blocks unchanged, jaise GetColorPixelBlock, ToASCIIBlock, KeyboardBlock, InputBlock, TimeBlock, PitchnessBlock, LoudnessBlock, Cursor & Mouse blocks - /** - * Updates the parameter of the block. - * @param {Logo} logo - The Logo object. - * @param {number} turtle - The turtle index. - * @param {number} blk - The block index. - * @returns {number} - The updated parameter value. - */ - updateParameter(logo, turtle, blk) { - return activity.blocks.blockList[blk].value; - } - - /** - * Retrieves the argument value for the block. - * @returns {number} - The horizontal position of the mouse. - */ - arg() { - return activity.getStageX(); - } - } - - /** - * Represents a block that returns `true` if the mouse button is pressed. - * @extends {BooleanSensorBlock} - */ - class MouseButtonBlock extends BooleanSensorBlock { - /** - * Constructs a new MouseButtonBlock instance. - */ - constructor() { - super("mousebutton", _("mouse button")); - this.setHelpString([ - _("The Mouse-button block returns True if the mouse button is pressed."), - "documentation", - null, - "mousebuttonhelp" - ]); - - this.setPalette("sensors", activity); - this.beginnerBlock(true); - this.parameter = true; - - this.extraWidth = 20; - } - - /** - * Updates the parameter of the block. - * @param {Logo} logo - The Logo object. - * @param {number} turtle - The turtle index. - * @param {number} blk - The block index. - * @returns {string} - The updated parameter value (`"true"` or `"false"`). - */ - updateParameter(logo, turtle, blk) { - if (activity.blocks.blockList[blk].value) { - return _("true"); - } else { - return _("false"); - } - } - - /** - * Retrieves the argument value for the block. - * @returns {boolean} - `true` if the mouse button is pressed, otherwise `false`. - */ - arg() { - return activity.getStageMouseDown(); - } - } - - /** - * Represents a block that converts numbers to letters using ASCII encoding. - * @extends {LeftBlock} - */ - class ToASCIIBlock extends LeftBlock { - /** - * Constructs a new ToASCIIBlock instance. - */ - constructor() { - super("toascii", _("to ASCII")); - this.setPalette("sensors", activity); - this.parameter = true; - this.setHelpString([ - _("The To ASCII block converts numbers to letters."), - "documentation", - "" - ]); - this.formBlock({ - args: 1, - defaults: [65] - }); - } - - /** - * Updates the parameter of the block. - * @param {Logo} logo - The Logo object. - * @param {number} turtle - The turtle index. - * @param {number} blk - The block index. - * @returns {number} - The updated parameter value. - */ - updateParameter(logo, turtle, blk) { - return activity.blocks.blockList[blk].value; - } - - /** - * Retrieves the argument value for the block. - * @param {Logo} logo - The Logo object. - * @param {number} turtle - The turtle index. - * @param {number} blk - The block index. - * @param {any} receivedArg - The received argument (not used in this method). - * @returns {string|number} - The converted letter or 0 in case of an error. - */ - arg(logo, turtle, blk, receivedArg) { - if ( - logo.inStatusMatrix && - activity.blocks.blockList[activity.blocks.blockList[blk].connections[0]].name === - "print" - ) { - logo.statusFields.push([blk, "toascii"]); - } else { - const cblk1 = activity.blocks.blockList[blk].connections[1]; - if (cblk1 === null) { - activity.errorMsg(NOINPUTERRORMSG, blk); - return "A"; - } - const a = logo.parseArg(logo, turtle, cblk1, blk, receivedArg); - if (typeof a === "number") { - if (a < 1) return 0; - else return String.fromCharCode(a); - } else { - activity.errorMsg(NANERRORMSG, blk); - return 0; - } - } - } - } - - /** - * Represents a block that returns computer keyboard input. - * @extends {ValueBlock} - */ - class KeyboardBlock extends ValueBlock { - /** - * Constructs a new KeyboardBlock instance. - */ - constructor() { - super("keyboard", _("keyboard")); - this.setPalette("sensors", activity); - this.parameter = true; - this.setHelpString([ - _("The Keyboard block returns computer keyboard input."), - "documentation", - "" - ]); - this.makeMacro((x, y) => [ - [0, "toascii", x, y, [null, 1]], - [1, "keyboard", 0, 0, [0, null]] - ]); - } - - /** - * Updates the parameter of the block. - * @param {Logo} logo - The Logo object. - * @param {number} turtle - The turtle index. - * @param {number} blk - The block index. - * @returns {number} - The updated parameter value. - */ - updateParameter(logo, turtle, blk) { - return activity.blocks.blockList[blk].value; - } - - /** - * Retrieves the argument value for the block. - * @param {Logo} logo - The Logo object. - * @returns {number} - The last key code pressed. - */ - arg(logo) { - logo.lastKeyCode = activity.getCurrentKeyCode(); - const val = logo.lastKeyCode; - activity.clearCurrentKeyCode(); - return val; - } - } - - new GetBlueBlock().setup(activity); - new GetGreenBlock().setup(activity); + // Setup all blocks new GetRedBlock().setup(activity); - new GetColorPixelBlock().setup(activity); - new ToASCIIBlock().setup(activity); - new KeyboardBlock().setup(activity); + new GetGreenBlock().setup(activity); + new GetBlueBlock().setup(activity); new InputValueBlock().setup(activity); new InputBlock().setup(activity); - new TimeBlock().setup(activity); new PitchnessBlock().setup(activity); new LoudnessBlock().setup(activity); - new MyCursoroutBlock().setup(activity); - new MyCursoroverBlock().setup(activity); - new MyCursorupBlock().setup(activity); - new MyCursordownBlock().setup(activity); - new MyClickBlock().setup(activity); - new MouseButtonBlock().setup(activity); - new MouseYBlock().setup(activity); - new MouseXBlock().setup(activity); + // ... baaki setup() calls } - if (typeof module !== "undefined" && module.exports) { module.exports = { setupSensorsBlocks }; } From 702ff400609272005258b75bb02d285b678308bc Mon Sep 17 00:00:00 2001 From: bhumika-ks31 Date: Sat, 8 Nov 2025 19:03:49 +0530 Subject: [PATCH 2/2] Fix redundant behavior in SensorsBlocks and RGB blocks --- git | 0 js/blocks/SensorsBlocks.js | 349 +++++++++++++++++-------------------- 2 files changed, 158 insertions(+), 191 deletions(-) create mode 100644 git diff --git a/git b/git new file mode 100644 index 0000000000..e69de29bb2 diff --git a/js/blocks/SensorsBlocks.js b/js/blocks/SensorsBlocks.js index 4e0092298d..938ea82c93 100644 --- a/js/blocks/SensorsBlocks.js +++ b/js/blocks/SensorsBlocks.js @@ -9,227 +9,194 @@ // License along with this library; if not, write to the Free Software // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA -/* - global - +/* global _, FlowBlock, NOINPUTERRORMSG, ValueBlock, docById, toFixed2, LeftBlock, BooleanSensorBlock, NANERRORMSG, hex2rgb, searchColors, Tone, platformColor, _THIS_IS_MUSIC_BLOCKS_ - */ - -/* exported setupSensorsBlocks */ +*/ function setupSensorsBlocks(activity) { - // Helper function to remove duplicate logic for color blocks - function getColorComponent(turtle, index) { - let colorString = activity.turtles.getTurtle(turtle).painter.canvasColor; - if (colorString[0] === "#") colorString = hex2rgb(colorString.split("#")[1]); - const obj = colorString.split("(")[1].split(","); - return parseInt(Number(obj[index]) / 2.55); - } - - /** - * Represents a block that prompts for keyboard input in the logo programming language. - * @extends {FlowBlock} - */ - class InputBlock extends FlowBlock { - constructor() { - super("input"); - this.setPalette("sensors", activity); - this.parameter = true; - this.setHelpString([ - _("The Input block prompts for keyboard input."), - "documentation", - "" - ]); - - this.formBlock({ - name: _("input"), - args: 1, - argTypes: ["anyin"], - defaults: [_("Input a value")] - }); - } - - flow(args, logo, turtle, blk) { - const tur = activity.turtles.ithTurtle(turtle); - tur.doWait(120); - - docById("labelDiv").innerHTML = - ''; - const inputElem = docById("textLabel"); - const cblk = activity.blocks.blockList[blk].connections[1]; - if (cblk !== null) inputElem.placeholder = activity.blocks.blockList[cblk].value; - - inputElem.style.left = activity.turtles.getTurtle(turtle).container.x + "px"; - inputElem.style.top = activity.turtles.getTurtle(turtle).container.y + "px"; - inputElem.focus(); - - docById("labelDiv").classList.add("hasKeyboard"); - - function __keyPressed(event) { - if (event.keyCode === 13) { - const inputElem = docById("textLabel"); - const value = inputElem.value; - logo.inputValues[turtle] = isNaN(value) ? value : Number(value); - - inputElem.blur(); - inputElem.style.display = "none"; - logo.clearTurtleRun(turtle); - docById("labelDiv").classList.remove("hasKeyboard"); - } - } + // =================== INPUT BLOCKS =================== + class InputBlock extends FlowBlock { + constructor() { + super("input"); + this.setPalette("sensors", activity); + this.parameter = true; + this.setHelpString([_("The Input block prompts for keyboard input."), "documentation", ""]); + this.formBlock({ name: _("input"), args: 1, argTypes: ["anyin"], defaults: [_("Input a value")] }); + } - docById("textLabel").addEventListener("keypress", __keyPressed); - } + flow(args, logo, turtle, blk) { + const tur = activity.turtles.ithTurtle(turtle); + tur.doWait(120); + + const labelDiv = docById("labelDiv"); + labelDiv.innerHTML = ''; + const inputElem = docById("textLabel"); + const cblk = activity.blocks.blockList[blk].connections[1]; + if (cblk !== null) inputElem.placeholder = activity.blocks.blockList[cblk].value; + + const turtleContainer = activity.turtles.getTurtle(turtle).container; + inputElem.style.left = turtleContainer.x + "px"; + inputElem.style.top = turtleContainer.y + "px"; + inputElem.focus(); + labelDiv.classList.add("hasKeyboard"); + + function __keyPressed(event) { + if (event.keyCode === 13) { + const val = inputElem.value; + logo.inputValues[turtle] = isNaN(val) ? val : Number(val); + inputElem.blur(); + inputElem.style.display = "none"; + logo.clearTurtleRun(turtle); + labelDiv.classList.remove("hasKeyboard"); + } + } + + inputElem.addEventListener("keypress", __keyPressed); + } + } + + class InputValueBlock extends ValueBlock { + constructor() { + super("inputvalue", _("input value")); + this.setPalette("sensors", activity); + this.parameter = true; + this.setHelpString([_("The Input-value block stores the input."), "documentation", null, "input"]); } - class InputValueBlock extends ValueBlock { - constructor() { - super("inputvalue", _("input value")); - this.setPalette("sensors", activity); - this.parameter = true; + updateParameter(logo, turtle) { + return turtle in logo.inputValues ? logo.inputValues[turtle] : 0; + } - this.setHelpString([ - _("The Input-value block stores the input."), - "documentation", - null, - "input" - ]); - } + arg(logo, turtle, blk) { + if (turtle in logo.inputValues) return logo.inputValues[turtle]; + activity.errorMsg(NOINPUTERRORMSG, blk); + return 0; + } + } + + // =================== RGB BLOCKS =================== + class GetRedBlock extends ValueBlock { + constructor() { + super("getred", _("red")); + this.setPalette("sensors", activity); + this.parameter = true; + } - updateParameter(logo, turtle) { - return turtle in logo.inputValues ? logo.inputValues[turtle] : 0; - } + updateParameter(logo, turtle, blk) { + return toFixed2(activity.blocks.blockList[blk].value); + } - arg(logo, turtle, blk) { - if (turtle in logo.inputValues) return logo.inputValues[turtle]; - activity.errorMsg(NOINPUTERRORMSG, blk); - return 0; - } + arg(logo, turtle) { + let colorStr = activity.turtles.getTurtle(turtle).painter.canvasColor; + if (colorStr[0] === "#") colorStr = hex2rgb(colorStr.slice(1)); + const r = colorStr.split("(")[1].split(",")[0]; + return Math.round(Number(r) / 2.55); } + } - class PitchnessBlock extends ValueBlock { - constructor() { - super("pitchness", _("pitch")); - this.setPalette("sensors", activity); - this.parameter = true; - } + class GetGreenBlock extends ValueBlock { + constructor() { + super("getgreen", _("green")); + this.setPalette("sensors", activity); + this.parameter = true; + } - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } + updateParameter(logo, turtle, blk) { + return toFixed2(activity.blocks.blockList[blk].value); + } - arg(logo) { - if (!logo.mic) return 440; - if (!logo.pitchAnalyser) { - logo.pitchAnalyser = new Tone.Analyser({ type: "fft", size: logo.limit, smoothing: 0 }); - logo.mic.connect(logo.pitchAnalyser); - } - - const values = logo.pitchAnalyser.getValue(); - let max = Infinity; - let idx = 0; - - for (let i = 0; i < logo.limit; i++) { - const v2 = -values[i]; - if (v2 < max) { - max = v2; - idx = i; - } - } - - return idx / (logo.pitchAnalyser.sampleTime * logo.limit * 2); - } + arg(logo, turtle) { + let colorStr = activity.turtles.getTurtle(turtle).painter.canvasColor; + if (colorStr[0] === "#") colorStr = hex2rgb(colorStr.slice(1)); + const g = colorStr.split("(")[1].split(",")[1]; + return Math.round(Number(g) / 2.55); } + } - class LoudnessBlock extends ValueBlock { - constructor() { - super("loudness", _("loudness")); - this.setPalette("sensors", activity); - this.parameter = true; - this.beginnerBlock(!(this.lang === "ja")); - this.setHelpString([ - _("The Loudness block returns the volume detected by the microphone."), - "documentation", - "" - ]); - } + class GetBlueBlock extends ValueBlock { + constructor() { + super("getblue", _("blue")); + this.setPalette("sensors", activity); + this.parameter = true; + } - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } + updateParameter(logo, turtle, blk) { + return toFixed2(activity.blocks.blockList[blk].value); + } - arg(logo) { - if (!logo.mic) return 0; - if (!logo.volumeAnalyser) { - logo.volumeAnalyser = new Tone.Analyser({ type: "waveform", size: logo.limit }); - logo.mic.connect(logo.volumeAnalyser); - } - - const values = logo.volumeAnalyser.getValue(); - let sum = 0; - for (let k = 0; k < logo.limit; k++) sum += values[k] * values[k]; - return Math.round(Math.sqrt(sum / logo.limit) * 100); - } + arg(logo, turtle) { + let colorStr = activity.turtles.getTurtle(turtle).painter.canvasColor; + if (colorStr[0] === "#") colorStr = hex2rgb(colorStr.slice(1)); + const b = colorStr.split("(")[1].split(",")[2]; + return Math.round(Number(b) / 2.55); + } + } + + // =================== OTHER BLOCKS =================== + class PitchnessBlock extends ValueBlock { + constructor() { + super("pitchness", _("pitch")); + this.setPalette("sensors", activity); + this.parameter = true; } - class GetRedBlock extends ValueBlock { - constructor() { - super("getred", _("red")); - this.setPalette("sensors", activity); - this.parameter = true; - } - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } - arg(logo, turtle) { - return getColorComponent(turtle, 0); - } + updateParameter(logo, turtle, blk) { + return toFixed2(activity.blocks.blockList[blk].value); } - class GetGreenBlock extends ValueBlock { - constructor() { - super("getgreen", _("green")); - this.setPalette("sensors", activity); - this.parameter = true; - } - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } - arg(logo, turtle) { - return getColorComponent(turtle, 1); - } + arg(logo) { + if (!logo.mic) return 440; + if (!logo.pitchAnalyser) { + logo.pitchAnalyser = new Tone.Analyser({ type: "fft", size: logo.limit, smoothing: 0 }); + logo.mic.connect(logo.pitchAnalyser); + } + + const values = logo.pitchAnalyser.getValue(); + let max = Infinity, idx = 0; + for (let i = 0; i < logo.limit; i++) { + const v2 = -values[i]; + if (v2 < max) { max = v2; idx = i; } + } + return idx / (logo.pitchAnalyser.sampleTime * logo.limit * 2); } + } - class GetBlueBlock extends ValueBlock { - constructor() { - super("getblue", _("blue")); - this.setPalette("sensors", activity); - this.parameter = true; - } - updateParameter(logo, turtle, blk) { - return toFixed2(activity.blocks.blockList[blk].value); - } - arg(logo, turtle) { - return getColorComponent(turtle, 2); - } + class LoudnessBlock extends ValueBlock { + constructor() { + super("loudness", _("loudness")); + this.setPalette("sensors", activity); + this.parameter = true; } - // ... baaki blocks unchanged, jaise GetColorPixelBlock, ToASCIIBlock, KeyboardBlock, InputBlock, TimeBlock, PitchnessBlock, LoudnessBlock, Cursor & Mouse blocks + updateParameter(logo, turtle, blk) { + return toFixed2(activity.blocks.blockList[blk].value); + } - // Setup all blocks - new GetRedBlock().setup(activity); - new GetGreenBlock().setup(activity); - new GetBlueBlock().setup(activity); - new InputValueBlock().setup(activity); - new InputBlock().setup(activity); - new PitchnessBlock().setup(activity); - new LoudnessBlock().setup(activity); - // ... baaki setup() calls + arg(logo) { + if (!logo.mic) return 0; + if (!logo.volumeAnalyser) { + logo.volumeAnalyser = new Tone.Analyser({ type: "waveform", size: logo.limit }); + logo.mic.connect(logo.volumeAnalyser); + } + const values = logo.volumeAnalyser.getValue(); + const rms = Math.sqrt(values.reduce((acc, v) => acc + v * v, 0) / logo.limit); + return Math.round(rms * 100); + } + } + + // =================== REGISTER BLOCKS =================== + new GetRedBlock().setup(activity); + new GetGreenBlock().setup(activity); + new GetBlueBlock().setup(activity); + new InputBlock().setup(activity); + new InputValueBlock().setup(activity); + new PitchnessBlock().setup(activity); + new LoudnessBlock().setup(activity); } if (typeof module !== "undefined" && module.exports) { - module.exports = { setupSensorsBlocks }; + module.exports = { setupSensorsBlocks }; }