From b9af4f7894ff068dc87517906f56acb281455ade Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford <cwillisf@media.mit.edu> Date: Mon, 2 May 2016 14:38:27 -0700 Subject: [PATCH] WIP implementation for WeDo2 blocks Hat blocks are still TBD. Motor blocks assume a `util` argument which has methods for `yield()` and `done()`. --- src/blocks/wedo2.js | 149 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 8 deletions(-) diff --git a/src/blocks/wedo2.js b/src/blocks/wedo2.js index 4adab36a4..ca91442ad 100644 --- a/src/blocks/wedo2.js +++ b/src/blocks/wedo2.js @@ -5,6 +5,20 @@ function WeDo2Blocks(runtime) { * @type {Runtime} */ this.runtime = runtime; + + /** + * Current motor speed, as a percentage (100 = full speed). + * @type {number} + * @private + */ + this._motorSpeed = 100; + + /** + * The timeout ID for a pending motor action. + * @type {?int} + * @private + */ + this._motorTimeout = null; } /** @@ -22,20 +36,139 @@ WeDo2Blocks.prototype.getPrimitives = function() { }; }; -WeDo2Blocks.prototype.motorClockwise = function() { - console.log('Running: wedo_motorclockwise'); +/** + * Clamp a value between a minimum and maximum value. + * @todo move this to a common utility class. + * @param val The value to clamp. + * @param min The minimum return value. + * @param max The maximum return value. + * @returns {number} The clamped value. + * @private + */ +WeDo2Blocks.prototype._clamp = function(val, min, max) { + return Math.max(min, Math.min(val, max)); }; -WeDo2Blocks.prototype.motorCounterClockwise = function() { - console.log('Running: wedo_motorcounterclockwise'); +/** + * Convert HSV to RGB. + * See https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV + * @todo move this to a common utility class. + * @param hueDegrees Hue, in degrees. + * @param saturation Saturation in the range [0,1]. + * @param value Value in the range [0,1]. + * @returns {number[]} An array of [r,g,b], each in the range [0,1]. + * @private + */ +WeDo2Blocks.prototype._HSVToRGB = function(hueDegrees, saturation, value) { + hueDegrees %= 360; + if (hueDegrees < 0) hueDegrees += 360; + saturation = this._clamp(saturation, 0, 1); + value = this._clamp(value, 0, 1); + + var chroma = value * saturation; + var huePrime = hueDegrees / 60; + var x = chroma * (1 - Math.abs(huePrime % 2 - 1)); + var rgb; + switch (Math.floor(huePrime)) { + case 0: + rgb = [chroma, x, 0]; + break; + case 1: + rgb = [x, chroma, 0]; + break; + case 2: + rgb = [0, chroma, x]; + break; + case 3: + rgb = [0, x, chroma]; + break; + case 4: + rgb = [x, 0, chroma]; + break; + case 5: + rgb = [chroma, 0, x]; + break; + } + + var m = value - chroma; + rgb[0] += m; + rgb[1] += m; + rgb[2] += m; + + return rgb; }; -WeDo2Blocks.prototype.motorSpeed = function() { - console.log('Running: wedo_motorspeed'); +/** + * Common implementation for motor blocks. + * @param direction The direction to turn ('left' or 'right'). + * @param durationSeconds The number of seconds to run. + * @param util The util instance to use for yielding and finishing. + * @private + */ +WeDo2Blocks.prototype._motorOnFor = function(direction, durationSeconds, util) { + if (this._motorTimeout > 0) { + clearTimeout(this._motorTimeout); + this._motorTimeout = null; + } + if (window.native && window.native.motorRun) { + window.native.motorRun(direction, this._motorSpeed); + } + + var instance = this; + var myTimeout = setTimeout(function() { + if (instance._motorTimeout == myTimeout) { + instance._motorTimeout = null; + } + if (window.native && window.native.motorStop) { + window.native.motorStop(); + } + util.done(); + }, 1000 * durationSeconds); + + util.yield(); }; -WeDo2Blocks.prototype.setColor = function() { - console.log('Running: wedo_setcolor'); +WeDo2Blocks.prototype.motorClockwise = function(argValues, util) { + this._motorOnFor('right', argValues[0], util); +}; + +WeDo2Blocks.prototype.motorCounterClockwise = function(argValues, util) { + this._motorOnFor('left', argValues[0], util); +}; + +WeDo2Blocks.prototype.motorSpeed = function(argValues) { + this._motorSpeed = this._clamp(argValues[0], 1, 100); +}; + +/** + * Convert a color name to an [r,b,g] array. + * Supports 'mystery' for a random hue. + * @param colorName The color to retrieve. + * @returns {number[]} The [r,g,b] values for the color in [0,255] range. + * @private + */ +WeDo2Blocks.prototype._getColor = function(colorName) { + if (colorName == 'mystery') { + return this._HSVToRGB(Math.random() * 360, 1, 1); + } + return { + 'yellow': [255, 255, 0], + 'orange': [255, 165, 0], + 'coral': [255, 127, 80], + 'magenta': [255, 0, 255], + 'purple': [128, 0, 128], + 'blue': [0, 0, 255], + 'green': [0, 255, 0], + 'white': [255, 255, 255] + }[colorName]; +}; + +WeDo2Blocks.prototype.setColor = function(argValues) { + if (window.native && window.native.setLedColor) { + var rgbColor = this._getColor(argValues[0]); + window.native.setLedColor( + 255 * rgbColor[0], 255 * rgbColor[1], 255 * rgbColor[2]); + } }; WeDo2Blocks.prototype.whenDistanceClose = function() {