From d93eeed05d21dbfd5e8a844eabe3861fef45c466 Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Mon, 12 Sep 2016 10:58:50 -0400 Subject: [PATCH] More color utilities, touching color, color touching color (#155) * Fill out color utilities * Amend Color.decimalToHex name * Add `Cast.toScaledRgbColor` * Add touching color, color touching color blocks * Fix scale of Cast.toRgbColor * Fix format of colors to use renderer-style lists * Update `Color.decimalToRgb` * Lint self --- src/blocks/scratch3_sensing.js | 20 ++++++++++ src/import/sb2import.js | 2 +- src/sprites/clone.js | 29 ++++++++++++++ src/util/cast.js | 17 ++++++++ src/util/color.js | 71 +++++++++++++++++++++++++++++++--- 5 files changed, 132 insertions(+), 7 deletions(-) diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js index 01717340b..d9605a03c 100644 --- a/src/blocks/scratch3_sensing.js +++ b/src/blocks/scratch3_sensing.js @@ -1,3 +1,5 @@ +var Cast = require('../util/cast'); + function Scratch3SensingBlocks(runtime) { /** * The runtime instantiating this block package. @@ -12,6 +14,9 @@ function Scratch3SensingBlocks(runtime) { */ Scratch3SensingBlocks.prototype.getPrimitives = function() { return { + 'colour_picker': this.colorPicker, + 'sensing_touchingcolor': this.touchingColor, + 'sensing_coloristouchingcolor': this.colorTouchingColor, 'sensing_timer': this.getTimer, 'sensing_resettimer': this.resetTimer, 'sensing_mousex': this.getMouseX, @@ -24,6 +29,21 @@ Scratch3SensingBlocks.prototype.getPrimitives = function() { }; }; +Scratch3SensingBlocks.prototype.colorPicker = function (args) { + return args.COLOUR; +}; + +Scratch3SensingBlocks.prototype.touchingColor = function (args, util) { + var color = Cast.toRgbColorList(args.COLOR); + return util.target.isTouchingColor(color); +}; + +Scratch3SensingBlocks.prototype.colorTouchingColor = function (args, util) { + var maskColor = Cast.toRgbColorList(args.COLOR); + var targetColor = Cast.toRgbColorList(args.COLOR2); + return util.target.colorIsTouchingColor(targetColor, maskColor); +}; + Scratch3SensingBlocks.prototype.getTimer = function (args, util) { return util.ioQuery('clock', 'projectTimer'); }; diff --git a/src/import/sb2import.js b/src/import/sb2import.js index e8f14b708..b337f9326 100644 --- a/src/import/sb2import.js +++ b/src/import/sb2import.js @@ -258,7 +258,7 @@ function parseBlock (sb2block) { } } else if (expectedArg.inputOp == 'colour_picker') { // Convert SB2 color to hex. - fieldValue = Color.scratchColorToHex(providedArg); + fieldValue = Color.decimalToHex(providedArg); fieldName = 'COLOUR'; if (shadowObscured) { fieldValue = '#990000'; diff --git a/src/sprites/clone.js b/src/sprites/clone.js index 9e6d23c8d..72b26d139 100644 --- a/src/sprites/clone.js +++ b/src/sprites/clone.js @@ -273,4 +273,33 @@ Clone.prototype.getName = function () { return this.sprite.name; }; +/** + * Return whether the clone is touching a color. + * @param {Array.} rgb [r,g,b], values between 0-255. + * @return {Promise.} True iff the clone is touching the color. + */ +Clone.prototype.isTouchingColor = function (rgb) { + if (this.renderer) { + return this.renderer.isTouchingColor(this.drawableID, rgb); + } + return false; +}; + +/** + * Return whether the clone's color is touching a color. + * @param {Object} targetRgb {Array.} [r,g,b], values between 0-255. + * @param {Object} maskRgb {Array.} [r,g,b], values between 0-255. + * @return {Promise.} True iff the clone's color is touching the color. + */ +Clone.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) { + if (this.renderer) { + return this.renderer.isTouchingColor( + this.drawableID, + targetRgb, + maskRgb + ); + } + return false; +}; + module.exports = Clone; diff --git a/src/util/cast.js b/src/util/cast.js index 1f84663a3..8c1e534ab 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -1,3 +1,5 @@ +var Color = require('../util/color'); + function Cast () {} /** @@ -63,6 +65,21 @@ Cast.toString = function (value) { return String(value); }; +/** + * Cast any Scratch argument to an RGB color object to be used for the renderer. + * @param {*} value Value to convert to RGB color object. + * @return {Array.} [r,g,b], values between 0-255. + */ +Cast.toRgbColorList = function (value) { + var color; + if (typeof value == 'string' && value.substring(0, 1) == '#') { + color = Color.hexToRgb(value); + } else { + color = Color.decimalToRgb(Cast.toNumber(value)); + } + return [color.r, color.g, color.b]; +}; + /** * Compare two values, using Scratch cast, case-insensitive string compare, etc. * In Scratch 2.0, this is captured by `interp.compare.` diff --git a/src/util/color.js b/src/util/color.js index 81fe8b01e..2635c1b6c 100644 --- a/src/util/color.js +++ b/src/util/color.js @@ -1,17 +1,76 @@ function Color () {} /** - * Convert a Scratch color number to a hex string, #RRGGBB. - * @param {number} color RGB color as a decimal. + * Convert a Scratch decimal color to a hex string, #RRGGBB. + * @param {number} decimal RGB color as a decimal. * @return {string} RGB color as #RRGGBB hex string. */ -Color.scratchColorToHex = function (color) { - if (color < 0) { - color += 0xFFFFFF + 1; +Color.decimalToHex = function (decimal) { + if (decimal < 0) { + decimal += 0xFFFFFF + 1; } - var hex = Number(color).toString(16); + var hex = Number(decimal).toString(16); hex = '#' + '000000'.substr(0, 6 - hex.length) + hex; return hex; }; +/** + * Convert a Scratch decimal color to an RGB color object. + * @param {number} decimal RGB color as decimal. + * @returns {Object} {r: R, g: G, b: B}, values between 0-255 + */ +Color.decimalToRgb = function (decimal) { + var r = (decimal >> 16) & 0xFF; + var g = (decimal >> 8) & 0xFF; + var b = decimal & 0xFF; + return {r: r, g: g, b: b}; +}; + +/** + * Convert a hex color (e.g., F00, #03F, #0033FF) to an RGB color object. + * CC-BY-SA Tim Down: + * https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + * @param {!string} hex Hex representation of the color. + * @return {Object} {r: R, g: G, b: B}, 0-255, or null. + */ +Color.hexToRgb = function (hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; +}; + +/** + * Convert an RGB color object to a hex color. + * @param {Object} rgb {r: R, g: G, b: B}, values between 0-255. + * @return {!string} Hex representation of the color. + */ +Color.rgbToHex = function (rgb) { + return Color.decimalToHex(Color.rgbToDecimal(rgb)); +}; + +/** + * Convert an RGB color object to a Scratch decimal color. + * @param {Object} rgb {r: R, g: G, b: B}, values between 0-255. + * @return {!number} Number representing the color. + */ +Color.rgbToDecimal = function (rgb) { + return (rgb.r << 16) + (rgb.g << 8) + rgb.b; +}; + +/** +* Convert a hex color (e.g., F00, #03F, #0033FF) to a decimal color number. +* @param {!string} hex Hex representation of the color. +* @return {!number} Number representing the color. +*/ +Color.hexToDecimal = function (hex) { + return Color.rgbToDecimal(Color.hexToRgb(hex)); +}; + module.exports = Color;