Merge pull request #725 from ericrosenbaum/feature/pen-extension

Set/change pen color blocks with color, brightness, saturation, transparency
This commit is contained in:
Eric Rosenbaum 2017-10-23 11:25:09 -04:00 committed by GitHub
commit 9ffbd60bad

View file

@ -5,12 +5,24 @@ const Clone = require('../util/clone');
const Color = require('../util/color'); const Color = require('../util/color');
const MathUtil = require('../util/math-util'); const MathUtil = require('../util/math-util');
const RenderedTarget = require('../sprites/rendered-target'); const RenderedTarget = require('../sprites/rendered-target');
const log = require('../util/log');
/**
* Enum for pen color parameters.
* @readonly
* @enum {string}
*/
const ColorParam = {
COLOR: 'color',
SATURATION: 'saturation',
BRIGHTNESS: 'brightness',
TRANSPARENCY: 'transparency'
};
/** /**
* @typedef {object} PenState - the pen state associated with a particular target. * @typedef {object} PenState - the pen state associated with a particular target.
* @property {Boolean} penDown - tracks whether the pen should draw for this target. * @property {Boolean} penDown - tracks whether the pen should draw for this target.
* @property {number} hue - the current hue of the pen. * @property {number} color - the current color (hue) of the pen.
* @property {number} shade - the current shade of the pen.
* @property {PenAttributes} penAttributes - cached pen attributes for the renderer. This is the authoritative value for * @property {PenAttributes} penAttributes - cached pen attributes for the renderer. This is the authoritative value for
* diameter but not for pen color. * diameter but not for pen color.
*/ */
@ -55,8 +67,9 @@ class Scratch3PenBlocks {
static get DEFAULT_PEN_STATE () { static get DEFAULT_PEN_STATE () {
return { return {
penDown: false, penDown: false,
hue: 120, color: 66.66,
shade: 50, saturation: 100,
brightness: 100,
transparency: 0, transparency: 0,
penAttributes: { penAttributes: {
color4f: [0, 0, 1, 1], color4f: [0, 0, 1, 1],
@ -171,44 +184,22 @@ class Scratch3PenBlocks {
} }
/** /**
* Update the cached color from the hue, shade and transparency values in the provided * Wrap a color input into the range (0,100).
* PenState object. * @param {number} value - the value to be wrapped.
* @param {PenState} penState - the pen state to update.
* @private
*/
_updatePenColor (penState) {
let rgb = Color.hsvToRgb({h: penState.hue * 180 / 100, s: 1, v: 1});
const shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade;
if (shade < 50) {
rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60);
} else {
rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60);
}
penState.penAttributes.color4f[0] = rgb.r / 255.0;
penState.penAttributes.color4f[1] = rgb.g / 255.0;
penState.penAttributes.color4f[2] = rgb.b / 255.0;
penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency);
}
/**
* Wrap a pen hue or shade values to the range (0,200).
* @param {number} value - the pen hue or shade value to the proper range.
* @returns {number} the wrapped value. * @returns {number} the wrapped value.
* @private * @private
*/ */
_wrapHueOrShade (value) { _wrapColor (value) {
value = value % 200; return MathUtil.wrapClamp(value, 0, 100);
if (value < 0) value += 200;
return value;
} }
/** /**
* Clamp a pen transparency value to the range (0,100). * Clamp a pen color parameter to the range (0,100).
* @param {number} value - the pen transparency value to be clamped. * @param {number} value - the value to be clamped.
* @returns {number} the clamped value. * @returns {number} the clamped value.
* @private * @private
*/ */
_clampTransparency (value) { _clampColorParam (value) {
return MathUtil.clamp(value, 0, 100); return MathUtil.clamp(value, 0, 100);
} }
@ -246,15 +237,7 @@ class Scratch3PenBlocks {
blocks: [ blocks: [
{ {
opcode: 'clear', opcode: 'clear',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND
arguments: {
NUM1: {
type: ArgumentType.NUMBER
},
NUM2: {
type: ArgumentType.NUMBER
}
}
}, },
{ {
opcode: 'stamp', opcode: 'stamp',
@ -281,44 +264,32 @@ class Scratch3PenBlocks {
} }
}, },
{ {
opcode: 'changePenHueBy', opcode: 'changePenColorParamBy',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'change pen color by [COLOR]', text: 'change pen [COLOR_PARAM] by [VALUE]',
arguments: { arguments: {
COLOR: { COLOR_PARAM: {
type: ArgumentType.STRING,
menu: 'colorParam',
defaultValue: ColorParam.COLOR
},
VALUE: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
defaultValue: 10 defaultValue: 10
} }
} }
}, },
{ {
opcode: 'setPenHueToNumber', opcode: 'setPenColorParamTo',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'set pen color to [COLOR]', text: 'set pen [COLOR_PARAM] to [VALUE]',
arguments: { arguments: {
COLOR: { COLOR_PARAM: {
type: ArgumentType.NUMBER, type: ArgumentType.STRING,
defaultValue: 0 menu: 'colorParam',
} defaultValue: ColorParam.COLOR
}
}, },
{ VALUE: {
opcode: 'changePenShadeBy',
blockType: BlockType.COMMAND,
text: 'change pen shade by [SHADE]',
arguments: {
SHADE: {
type: ArgumentType.NUMBER,
defaultValue: 10
}
}
},
{
opcode: 'setPenShadeToNumber',
blockType: BlockType.COMMAND,
text: 'set pen shade to [SHADE]',
arguments: {
SHADE: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
defaultValue: 50 defaultValue: 50
} }
@ -346,7 +317,12 @@ class Scratch3PenBlocks {
} }
} }
} }
] ],
menus: {
colorParam:
[ColorParam.COLOR, ColorParam.SATURATION,
ColorParam.BRIGHTNESS, ColorParam.TRANSPARENCY]
}
}; };
} }
@ -413,6 +389,7 @@ class Scratch3PenBlocks {
/** /**
* The pen "set pen color to {color}" block sets the pen to a particular RGB color. * The pen "set pen color to {color}" block sets the pen to a particular RGB color.
* The transparency is reset to 0.
* @param {object} args - the block arguments. * @param {object} args - the block arguments.
* @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB). * @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB).
* @param {object} util - utility object provided by the runtime. * @param {object} util - utility object provided by the runtime.
@ -421,66 +398,83 @@ class Scratch3PenBlocks {
const penState = this._getPenState(util.target); const penState = this._getPenState(util.target);
const rgb = Cast.toRgbColorObject(args.COLOR); const rgb = Cast.toRgbColorObject(args.COLOR);
const hsv = Color.rgbToHsv(rgb); const hsv = Color.rgbToHsv(rgb);
penState.color = (hsv.h / 360) * 100;
penState.saturation = hsv.s * 100;
penState.brightness = hsv.v * 100;
penState.transparency = 0;
this._updatePenColor(penState);
}
penState.hue = 200 * hsv.h / 360; /**
penState.shade = 50 * hsv.v; * Update the cached color from the color, saturation, brightness and transparency values
* in the provided PenState object.
* @param {PenState} penState - the pen state to update.
* @private
*/
_updatePenColor (penState) {
const rgb = Color.hsvToRgb({
h: penState.color * 360 / 100,
s: penState.saturation / 100,
v: penState.brightness / 100
});
penState.penAttributes.color4f[0] = rgb.r / 255.0; penState.penAttributes.color4f[0] = rgb.r / 255.0;
penState.penAttributes.color4f[1] = rgb.g / 255.0; penState.penAttributes.color4f[1] = rgb.g / 255.0;
penState.penAttributes.color4f[2] = rgb.b / 255.0; penState.penAttributes.color4f[2] = rgb.b / 255.0;
if (rgb.hasOwnProperty('a')) { // Will there always be an 'a'? penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency);
penState.penAttributes.color4f[3] = rgb.a / 255.0;
} else {
penState.penAttributes.color4f[3] = 1;
}
penState.transparency = this._alphaToTransparency(penState.penAttributes.color4f[3]);
} }
/** /**
* The pen "change pen color by {number}" block rotates the hue of the pen by the given amount. * Set or change a single color parameter on the pen state, and update the pen color.
* @param {object} args - the block arguments. * @param {ColorParam} param - the name of the color parameter to set or change.
* @property {number} COLOR - the amount of desired hue rotation. * @param {number} value - the value to set or change the param by.
* @param {object} util - utility object provided by the runtime. * @param {PenState} penState - the pen state to update.
* @param {boolean} change - if true change param by value, if false set param to value.
* @private
*/ */
changePenHueBy (args, util) { _setOrChangeColorParam (param, value, penState, change) {
const penState = this._getPenState(util.target); switch (param) {
penState.hue = this._wrapHueOrShade(penState.hue + Cast.toNumber(args.COLOR)); case ColorParam.COLOR:
penState.color = this._wrapColor(value + (change ? penState.color : 0));
break;
case ColorParam.SATURATION:
penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0));
break;
case ColorParam.BRIGHTNESS:
penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0));
break;
case ColorParam.TRANSPARENCY:
penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0));
break;
default:
log.warn(`Tried to set or change unknown color parameter: ${param}`);
}
this._updatePenColor(penState); this._updatePenColor(penState);
} }
/** /**
* The pen "set pen color to {number}" block sets the hue of the pen. * The "change pen {ColorParam} by {number}" block changes one of the pen's color parameters
* by a given amound.
* @param {object} args - the block arguments. * @param {object} args - the block arguments.
* @property {number} COLOR - the desired hue. * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.
* @property {number} VALUE - the amount to change the selected parameter by.
* @param {object} util - utility object provided by the runtime. * @param {object} util - utility object provided by the runtime.
*/ */
setPenHueToNumber (args, util) { changePenColorParamBy (args, util) {
const penState = this._getPenState(util.target); const penState = this._getPenState(util.target);
penState.hue = this._wrapHueOrShade(Cast.toNumber(args.COLOR)); this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, true);
this._updatePenColor(penState);
} }
/** /**
* The pen "change pen shade by {number}" block changes the "shade" of the pen, related to the HSV value. * The "set pen {ColorParam} to {number}" block sets one of the pen's color parameters
* to a given amound.
* @param {object} args - the block arguments. * @param {object} args - the block arguments.
* @property {number} SHADE - the amount of desired shade change. * @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.
* @property {number} VALUE - the amount to set the selected parameter to.
* @param {object} util - utility object provided by the runtime. * @param {object} util - utility object provided by the runtime.
*/ */
changePenShadeBy (args, util) { setPenColorParamTo (args, util) {
const penState = this._getPenState(util.target); const penState = this._getPenState(util.target);
penState.shade = this._wrapHueOrShade(penState.shade + Cast.toNumber(args.SHADE)); this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, false);
this._updatePenColor(penState);
}
/**
* The pen "set pen shade to {number}" block sets the "shade" of the pen, related to the HSV value.
* @param {object} args - the block arguments.
* @property {number} SHADE - the amount of desired shade change.
* @param {object} util - utility object provided by the runtime.
*/
setPenShadeToNumber (args, util) {
const penState = this._getPenState(util.target);
penState.shade = this._wrapHueOrShade(Cast.toNumber(args.SHADE));
this._updatePenColor(penState);
} }
/** /**
@ -504,30 +498,6 @@ class Scratch3PenBlocks {
const penAttributes = this._getPenState(util.target).penAttributes; const penAttributes = this._getPenState(util.target).penAttributes;
penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE)); penAttributes.diameter = this._clampPenSize(Cast.toNumber(args.SIZE));
} }
/**
* The pen "change pen transparency by {number}" block changes the RGBA "transparency" of the pen.
* @param {object} args - the block arguments.
* @property {number} TRANSPARENCY - the amount of desired transparency change.
* @param {object} util - utility object provided by the runtime.
*/
changePenTransparencyBy (args, util) {
const penState = this._getPenState(util.target);
penState.transparency = this._clampTransparency(penState.transparency + Cast.toNumber(args.TRANSPARENCY));
this._updatePenColor(penState);
}
/**
* The pen "set pen transparency to {number}" block sets the RGBA "transparency" of the pen.
* @param {object} args - the block arguments.
* @property {number} TRANSPARENCY - the amount of desired transparency change.
* @param {object} util - utility object provided by the runtime.
*/
setPenTransparencyTo (args, util) {
const penState = this._getPenState(util.target);
penState.transparency = this._clampTransparency(Cast.toNumber(args.TRANSPARENCY));
this._updatePenColor(penState);
}
} }
module.exports = Scratch3PenBlocks; module.exports = Scratch3PenBlocks;