mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 23:12:24 -05:00
Merge pull request #725 from ericrosenbaum/feature/pen-extension
Set/change pen color blocks with color, brightness, saturation, transparency
This commit is contained in:
commit
9ffbd60bad
1 changed files with 105 additions and 135 deletions
|
@ -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;
|
/**
|
||||||
|
* Set or change a single color parameter on the pen state, and update the pen color.
|
||||||
|
* @param {ColorParam} param - the name of the color parameter to set or change.
|
||||||
|
* @param {number} value - the value to set or change the param by.
|
||||||
|
* @param {PenState} penState - the pen state to update.
|
||||||
|
* @param {boolean} change - if true change param by value, if false set param to value.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_setOrChangeColorParam (param, value, penState, change) {
|
||||||
|
switch (param) {
|
||||||
|
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}`);
|
||||||
}
|
}
|
||||||
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.
|
|
||||||
* @param {object} args - the block arguments.
|
|
||||||
* @property {number} COLOR - the amount of desired hue rotation.
|
|
||||||
* @param {object} util - utility object provided by the runtime.
|
|
||||||
*/
|
|
||||||
changePenHueBy (args, util) {
|
|
||||||
const penState = this._getPenState(util.target);
|
|
||||||
penState.hue = this._wrapHueOrShade(penState.hue + Cast.toNumber(args.COLOR));
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in a new issue