scratch-vm/src/blocks/wedo2.js
Christopher Willis-Ford b9af4f7894 WIP implementation for WeDo2 blocks
Hat blocks are still TBD.
Motor blocks assume a `util` argument which has methods for `yield()`
and `done()`.
2016-05-03 09:44:13 -07:00

182 lines
4.9 KiB
JavaScript

function WeDo2Blocks(runtime) {
/**
* The runtime instantiating this block package.
* @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;
}
/**
* Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function.
*/
WeDo2Blocks.prototype.getPrimitives = function() {
return {
'wedo_motorclockwise': this.motorClockwise,
'wedo_motorcounterclockwise': this.motorCounterClockwise,
'wedo_motorspeed': this.motorSpeed,
'wedo_setcolor': this.setColor,
'wedo_whendistanceclose': this.whenDistanceClose,
'wedo_whentilt': this.whenTilt
};
};
/**
* 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));
};
/**
* 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;
};
/**
* 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.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() {
console.log('Running: wedo_whendistanceclose');
};
WeDo2Blocks.prototype.whenTilt = function() {
console.log('Running: wedo_whentilt');
};
module.exports = WeDo2Blocks;