Spaces:
Runtime error
Runtime error
| /* eslint-disable no-multi-spaces */ | |
| /* eslint-disable no-invalid-this */ | |
| /* eslint-disable no-undef */ | |
| const BlockType = require('../../extension-support/block-type'); | |
| const ArgumentType = require('../../extension-support/argument-type'); | |
| const MathUtil = require('../../util/math-util'); | |
| const CanvasVar = require('./canvasData'); | |
| const uid = require('../../util/uid'); | |
| const sanitize = string => { | |
| if (typeof string !== 'string') { | |
| log.warn(`sanitize got unexpected type: ${typeof string}`); | |
| string = '' + string; | |
| } | |
| return JSON.stringify(string).slice(1, -1); | |
| }; | |
| const DefaultDrawImage = 'https://studio.penguinmod.com/favicon.ico'; | |
| const canvasPropInfos = [ | |
| ['compositing method', 'globalCompositeOperation', [ | |
| ['source over', 'source-over'], | |
| ['source in', 'source-in'], | |
| ['source out', 'source-out'], | |
| ['source atop', 'source-atop'], | |
| ['destination over', 'destination-over'], | |
| ['destination in', 'destination-in'], | |
| ['destination out', 'destination-out'], | |
| ['destination atop', 'destination-atop'], | |
| ['lighter', 'lighter'], | |
| ['copy', 'copy'], | |
| ['xor', 'xor'], | |
| ['multiply', 'multiply'], | |
| ['screen', 'screen'], | |
| ['overlay', 'overlay'], | |
| ['darken', 'darken'], | |
| ['lighten', 'lighten'], | |
| ['color dodge', 'color-dodge'], | |
| ['color burn', 'color-burn'], | |
| ['hard light', 'hard-light'], | |
| ['soft light', 'soft-light'], | |
| ['difference', 'difference'], | |
| ['exclusion', 'exclusion'], | |
| ['hue', 'hue'], | |
| ['saturation', 'saturation'], | |
| ['color', 'color'], | |
| ['luminosity', 'luminosity'] | |
| ], 'source-over'], | |
| ['CSS filter', 'filter', ArgumentType.STRING, 'none'], | |
| ['font', 'font', ArgumentType.STRING, ''], | |
| ['font kerning method', 'fontKerning', [ | |
| ['browser defined', 'auto'], | |
| ['font defined', 'normal'], | |
| ['none', 'none'] | |
| ], 'normal'], | |
| ['font stretch', 'fontStretch', [ | |
| ['ultra condensed', 'ultra-condensed'], | |
| ['extra condensed', 'extra-condensed'], | |
| ['condensed', 'condensed'], | |
| ['normal', 'normal'], | |
| ['semi expanded', 'semi-expanded'], | |
| ['expanded', 'expanded'], | |
| ['extra expanded', 'extra-expanded'], | |
| ['ultra expanded', 'ultra-expanded'] | |
| ], 'normal'], | |
| ['font case sizing', 'fontVariantCaps', [ | |
| ['normal', 'normal'], | |
| ['uni-case', 'unicase'], | |
| ['titling-case', 'titling-caps'], | |
| ['smaller uppercase', 'small-caps'], | |
| ['smaller cased characters', 'all-small-caps'], | |
| ['petite uppercase', 'petite-caps'], | |
| ['petite cased characters', 'all-petite-caps'] | |
| ], 'normal'], | |
| ['transparency', 'globalAlpha', ArgumentType.NUMBER, '0'], | |
| ['image smoothing', 'imageSmoothingEnabled', ArgumentType.BOOLEAN, ''], | |
| ['image smoothing quality', 'imageSmoothingQuality', [ | |
| ['low', 'low'], | |
| ['medium', 'medium'], | |
| ['high', 'high'] | |
| ], 'low'], | |
| ['letter spacing', 'letterSpacing', ArgumentType.NUMBER, '0'], | |
| ['line cap shape', 'lineCap', [ | |
| ['sharp', 'butt'], | |
| ['round', 'round'], | |
| ['square', 'square'] | |
| ], 'butt'], | |
| ['line dash offset', 'lineDashOffset', ArgumentType.NUMBER, '0'], | |
| ['line join shape', 'lineJoin', [ | |
| ['round', 'round'], | |
| ['beveled', 'bevel'], | |
| ['sharp', 'miter'] | |
| ], 'miter'], | |
| ['line size', 'lineWidth', ArgumentType.NUMBER, '1'], | |
| ['sharp line join limit', 'miterLimit', ArgumentType.NUMBER, '10'], | |
| ['shadow blur', 'shadowBlur', ArgumentType.NUMBER, '0'], | |
| ['shadow color', 'shadowColor', ArgumentType.COLOR, null], | |
| ['shadow X offset', 'shadowOffsetX', ArgumentType.NUMBER, '0'], | |
| ['shadow Y offset', 'shadowOffsetY', ArgumentType.NUMBER, '0'], | |
| ['line color', 'strokeStyle', ArgumentType.COLOR, null], | |
| ['text horizontal alignment', 'textAlign', [ | |
| ['start', 'start'], | |
| ['left', 'left'], | |
| ['center', 'center'], | |
| ['right', 'right'], | |
| ['end', 'end'] | |
| ], 'start'], | |
| ['text vertical alignment', 'textBaseline', [ | |
| ['top', 'top'], | |
| ['hanging', 'hanging'], | |
| ['middle', 'middle'], | |
| ['alphabetic', 'alphabetic'], | |
| ['ideographic', 'ideographic'], | |
| ['bottom', 'bottom'] | |
| ], 'alphabetic'], | |
| ['text rendering optimisation', 'textRendering', [ | |
| ['auto', 'auto'], | |
| ['render speed', 'optimizeSpeed'], | |
| ['legibility', 'optimizeLegibility'], | |
| ['geometric precision', 'geometricPrecision'] | |
| ], 'auto'], | |
| ['word spacing', 'wordSpacing', ArgumentType.NUMBER, '0'] | |
| ]; | |
| /** | |
| * Class | |
| * @constructor | |
| */ | |
| class canvas { | |
| constructor(runtime) { | |
| /** | |
| * The runtime instantiating this block package. | |
| * @type {runtime} | |
| */ | |
| this.runtime = runtime; | |
| this.lastVars = []; | |
| this.preloadedImages = {}; | |
| this.propList = []; | |
| this.sbInfo = {}; | |
| for (const item of canvasPropInfos) { | |
| this.propList.push(item.slice(0, 2)); | |
| const info = { | |
| isDummy: false, | |
| default: item[3], | |
| type: item[2] | |
| }; | |
| switch (item[2]) { | |
| case ArgumentType.STRING: | |
| info.shadow = 'text'; | |
| break; | |
| case ArgumentType.NUMBER: | |
| info.shadow = 'math_number'; | |
| break; | |
| case ArgumentType.BOOLEAN: | |
| info.check = 'Boolean'; | |
| break; | |
| case ArgumentType.COLOR: | |
| info.shadow = 'colour_picker'; | |
| break; | |
| default: | |
| info.isDummy = true; | |
| info.options = item[2]; | |
| } | |
| this.sbInfo[item[1]] = info; | |
| } | |
| this.runtime.registerVariable('canvas', CanvasVar); | |
| this.runtime.registerSerializer( | |
| CanvasVar.customId, | |
| canvas => canvas.id, | |
| (varId, target) => { | |
| let variable = target.variables[varId]; | |
| if (!variable) { | |
| for (const target of this.runtime.targets) { | |
| if (target.variables[varId]) { | |
| variable = target.variables[varId]; | |
| break; | |
| } | |
| } | |
| } | |
| return variable; | |
| } | |
| ); | |
| this.runtime.registerCompiledExtensionBlocks('newCanvas', this.getCompileInfo()); | |
| const updateVariables = type => { | |
| if (type === 'canvas') { | |
| this.runtime.vm.emitWorkspaceUpdate(); | |
| } | |
| }; | |
| this.runtime.on('variableCreate', updateVariables); | |
| this.runtime.on('variableChange', updateVariables); | |
| this.runtime.on('variableDelete', updateVariables); | |
| let infoObj = {}; | |
| Object.defineProperty(ScratchBlocks.Blocks, 'newCanvas_setProperty', { | |
| set: block => { | |
| this._implementSBInfo(block); | |
| infoObj = block; | |
| }, | |
| get: () => infoObj | |
| }); | |
| } | |
| orderCategoryBlocks(blocks) { | |
| const button = blocks[0]; | |
| const varBlock = blocks[1]; | |
| const variables = [button]; | |
| delete blocks[0]; | |
| delete blocks[1]; | |
| const stage = this.runtime.getTargetForStage(); | |
| const target = this.runtime.vm.editingTarget; | |
| const stageVars = Object.values(stage.variables) | |
| .filter(variable => variable.type === 'canvas') | |
| .map(variable => variable.toToolboxDefault('canvas')) | |
| .map(xml => varBlock.replace('></block>', `>${xml}</block>`)); | |
| const privateVars = Object.values(target.variables) | |
| .filter(variable => variable.type === 'canvas') | |
| .map(variable => variable.toToolboxDefault('canvas')) | |
| .map(xml => varBlock.replace('></block>', `>${xml}</block>`)); | |
| if (stageVars.length) { | |
| variables.push(`<label text="Canvases for all sprites"></label>`); | |
| variables.push(...stageVars); | |
| } | |
| if (privateVars.length && target.id !== stage.id) { | |
| variables.push(`<label text="Canvases for this sprite"></label>`); | |
| variables.push(...privateVars); | |
| } | |
| if (stageVars.length || privateVars.length) { | |
| variables.push(...blocks); | |
| } | |
| return variables; | |
| } | |
| _implementSBInfo(block) { | |
| const info = this.sbInfo; | |
| block.renderInput = function(item) { | |
| if (!item) item = this.getFieldValue('prop'); | |
| const existingInput = this.getInput('value'); | |
| const isInputCurrentlyUsed = existingInput.type !== ScratchBlocks.DUMMY_INPUT | |
| && !existingInput.connection.targetBlock()?.isShadow?.(); | |
| const target = info[item]; | |
| if (this.lastItem === item || (isInputCurrentlyUsed && !target.isDummy)) return; | |
| this.removeInput('value'); | |
| if (target.isDummy) { | |
| const inp = this.appendDummyInput('value'); | |
| const field = new ScratchBlocks.FieldDropdown(target.options); | |
| inp.appendField(field, 'value'); | |
| field.setValue(target.default); | |
| return; | |
| } | |
| const inp = this.appendValueInput('value'); | |
| inp.setCheck(target.check); | |
| if (target.shadow && !this.isInsertionMarker()) { | |
| const shadow = this.workspace.newBlock(target.shadow); | |
| shadow.setShadow(true); | |
| shadow.initSvg(); | |
| shadow.inputList[0].fieldRow[0].setValue(target.default); | |
| inp.connection.connect(shadow.outputConnection); | |
| shadow.render(false); | |
| } | |
| }; | |
| const oldInit = block.init; | |
| block.init = function() { | |
| oldInit.apply(this); | |
| this.appendDummyInput('value'); | |
| const dropdownField = this.getField('prop'); | |
| dropdownField.setValidator(item => { | |
| this.renderInput(item); | |
| return item; | |
| }); | |
| this.renderInput(); | |
| }; | |
| } | |
| /** | |
| * @returns {object} metadata for this extension and its blocks. | |
| */ | |
| getInfo() { | |
| return { | |
| id: 'newCanvas', | |
| name: 'html canvas', | |
| color1: '#0069c2', | |
| isDynamic: true, | |
| orderBlocks: this.orderCategoryBlocks.bind(this), | |
| blocks: [ | |
| { | |
| opcode: 'createNewCanvas', | |
| blockType: BlockType.BUTTON, | |
| text: 'create new canvas' | |
| }, | |
| { | |
| opcode: 'canvasGetter', | |
| blockType: BlockType.REPORTER, | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| }, | |
| text: '[canvas]' | |
| }, | |
| { | |
| blockType: BlockType.LABEL, | |
| text: "stylizing" | |
| }, | |
| { | |
| opcode: 'setSize', | |
| text: 'set width: [width] height: [height] of [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageWidth | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageHeight | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'setProperty', | |
| text: 'set [prop] of [canvas] to ', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| prop: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvasProps' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'getProperty', | |
| text: 'get [prop] of [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| prop: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvasProps' | |
| } | |
| }, | |
| blockType: BlockType.REPORTER | |
| }, | |
| { | |
| opcode: 'dash', | |
| blockType: BlockType.COMMAND, | |
| text: 'set line dash to [dashing] in [canvas]', | |
| arguments: { | |
| dashing: { | |
| type: ArgumentType.STRING, | |
| defaultValue: '[10, 10]' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| blockType: BlockType.LABEL, | |
| text: "direct drawing" | |
| }, | |
| { | |
| opcode: 'clearCanvas', | |
| text: 'clear canvas [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'clearAria', | |
| text: 'clear area at x: [x] y: [y] with width: [width] height: [height] on [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageWidth | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageHeight | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| '---', | |
| { | |
| opcode: 'drawText', | |
| text: 'draw text [text] at [x] [y] onto [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| text: { | |
| type: ArgumentType.STRING, | |
| defaultValue: 'photos printed' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'drawTextWithCap', | |
| text: 'draw text [text] at [x] [y] with size cap [cap] onto [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| text: { | |
| type: ArgumentType.STRING, | |
| defaultValue: 'photos printed' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| cap: { | |
| type: ArgumentType.NUMBER, | |
| defauleValue: '10' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'outlineText', | |
| text: 'draw text outline for [text] at [x] [y] onto [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| text: { | |
| type: ArgumentType.STRING, | |
| defaultValue: 'photos printed' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'outlineTextWithCap', | |
| text: 'draw text outline for [text] at [x] [y] with size cap [cap] onto [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| text: { | |
| type: ArgumentType.STRING, | |
| defaultValue: 'photos printed' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| cap: { | |
| type: ArgumentType.NUMBER, | |
| defauleValue: '10' | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'drawRect', | |
| text: 'draw rectangle at x: [x] y: [y] with width: [width] height: [height] on [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageWidth | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageHeight | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'outlineRect', | |
| text: 'draw rectangle outline at x: [x] y: [y] with width: [width] height: [height] on [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageWidth | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: this.runtime.stageHeight | |
| } | |
| }, | |
| blockType: BlockType.COMMAND | |
| }, | |
| { | |
| opcode: 'preloadUriImage', | |
| blockType: BlockType.COMMAND, | |
| text: 'preload image [URI] as [NAME]', | |
| arguments: { | |
| URI: { | |
| type: ArgumentType.STRING, | |
| exemptFromNormalization: true, | |
| defaultValue: DefaultDrawImage | |
| }, | |
| NAME: { | |
| type: ArgumentType.STRING, | |
| defaultValue: "preloaded image" | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'unloadUriImage', | |
| blockType: BlockType.COMMAND, | |
| text: 'unload image [NAME]', | |
| arguments: { | |
| NAME: { | |
| type: ArgumentType.STRING, | |
| defaultValue: "preloaded image" | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getWidthOfPreloaded', | |
| blockType: BlockType.REPORTER, | |
| text: 'get width of [name]', | |
| arguments: { | |
| name: { | |
| type: ArgumentType.STRING, | |
| defaultValue: "preloaded image" | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getHeightOfPreloaded', | |
| blockType: BlockType.REPORTER, | |
| text: 'get height of [name]', | |
| arguments: { | |
| name: { | |
| type: ArgumentType.STRING, | |
| defaultValue: "preloaded image" | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'drawUriImage', | |
| blockType: BlockType.COMMAND, | |
| text: 'draw image [URI] at x:[X] y:[Y] onto canvas [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| URI: { | |
| type: ArgumentType.STRING, | |
| exemptFromNormalization: true, | |
| defaultValue: DefaultDrawImage | |
| }, | |
| X: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'drawUriImageWHR', | |
| blockType: BlockType.COMMAND, | |
| text: 'draw image [URI] at x:[X] y:[Y] width:[WIDTH] height:[HEIGHT] pointed at: [ROTATE] onto canvas [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| URI: { | |
| type: ArgumentType.STRING, | |
| exemptFromNormalization: true, | |
| defaultValue: DefaultDrawImage | |
| }, | |
| X: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| WIDTH: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 64 | |
| }, | |
| HEIGHT: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 64 | |
| }, | |
| ROTATE: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 90 | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'drawUriImageWHCX1Y1X2Y2R', | |
| blockType: BlockType.COMMAND, | |
| text: 'draw image [URI] at x:[X] y:[Y] width:[WIDTH] height:[HEIGHT] cropping from x:[CROPX] y:[CROPY] width:[CROPW] height:[CROPH] pointed at: [ROTATE] onto canvas [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| }, | |
| URI: { | |
| type: ArgumentType.STRING, | |
| exemptFromNormalization: true, | |
| defaultValue: DefaultDrawImage | |
| }, | |
| X: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| WIDTH: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 64 | |
| }, | |
| HEIGHT: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 64 | |
| }, | |
| CROPX: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| CROPY: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| CROPW: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 100 | |
| }, | |
| CROPH: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 100 | |
| }, | |
| ROTATE: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 90 | |
| } | |
| } | |
| }, | |
| { | |
| blockType: BlockType.LABEL, | |
| text: "path drawing" | |
| }, | |
| { | |
| opcode: 'beginPath', | |
| blockType: BlockType.COMMAND, | |
| text: 'begin path drawing on [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'moveTo', | |
| blockType: BlockType.COMMAND, | |
| text: 'move pen to x:[x] y:[y] on [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'lineTo', | |
| blockType: BlockType.COMMAND, | |
| text: 'add line going to x:[x] y:[y] on [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'arcTo', | |
| blockType: BlockType.COMMAND, | |
| text: 'add arc going to x:[x] y:[y] on [canvas] with control points [controlPoints] and radius [radius]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| controlPoints: { | |
| type: ArgumentType.POLYGON, | |
| nodes: 2 | |
| }, | |
| radius: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'addRect', | |
| blockType: BlockType.COMMAND, | |
| text: 'add a rectangle at x:[x] y:[y] with width:[width] height:[height] to [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'addEllipse', | |
| blockType: BlockType.COMMAND, | |
| text: 'add a ellipse at x:[x] y:[y] with width:[width] height:[height] pointed towards [dir] to [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| dir: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 90 | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'addEllipseStartStop', | |
| blockType: BlockType.COMMAND, | |
| text: 'add a ellipse with starting rotation [start] and ending rotation [end] at x:[x] y:[y] with width:[width] height:[height] pointed towards [dir] to [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| width: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| height: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 10 | |
| }, | |
| start: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '0' | |
| }, | |
| end: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '360' | |
| }, | |
| dir: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 90 | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'closePath', | |
| blockType: BlockType.COMMAND, | |
| text: 'attempt to close any open path in [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'stroke', | |
| blockType: BlockType.COMMAND, | |
| text: 'draw outline for current path in [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'fill', | |
| blockType: BlockType.COMMAND, | |
| text: 'draw fill for current path in [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| blockType: BlockType.LABEL, | |
| text: "transforms" | |
| }, | |
| { | |
| opcode: 'saveTransform', | |
| blockType: BlockType.COMMAND, | |
| text: 'save [canvas]\'s transform', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'restoreTransform', | |
| blockType: BlockType.COMMAND, | |
| text: 'reset to [canvas]\'s saved transform', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'turnRotationLeft', | |
| blockType: BlockType.COMMAND, | |
| text: 'turn left [degrees] in [canvas]', | |
| arguments: { | |
| degrees: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '90' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'turnRotationRight', | |
| blockType: BlockType.COMMAND, | |
| text: 'turn right [degrees] in [canvas]', | |
| arguments: { | |
| degrees: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '90' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setRotation', | |
| blockType: BlockType.COMMAND, | |
| text: 'set rotation to [degrees] in [canvas]', | |
| arguments: { | |
| degrees: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: '90' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'setTranslateXY', | |
| blockType: BlockType.COMMAND, | |
| text: 'set translation X: [x] Y: [y] on [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'changeTranslateXY', | |
| blockType: BlockType.COMMAND, | |
| text: 'change translation X: [x] Y: [y] on [canvas]', | |
| arguments: { | |
| x: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'changeTranslateX', | |
| blockType: BlockType.COMMAND, | |
| text: 'change X translation by [amount] on [canvas]', | |
| arguments: { | |
| amount: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setTranslateX', | |
| blockType: BlockType.COMMAND, | |
| text: 'set X scaler to [amount] on [canvas]', | |
| arguments: { | |
| amount: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'changeTranslateY', | |
| blockType: BlockType.COMMAND, | |
| text: 'change Y translation by [amount] on [canvas]', | |
| arguments: { | |
| amount: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setTranslateY', | |
| blockType: BlockType.COMMAND, | |
| text: 'set Y translation by [amount] on [canvas]', | |
| arguments: { | |
| amount: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'changeScaleXY', | |
| blockType: BlockType.COMMAND, | |
| text: 'change XY scaler by [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setScaleXY', | |
| blockType: BlockType.COMMAND, | |
| text: 'set XY scaler to [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'changeScaleX', | |
| blockType: BlockType.COMMAND, | |
| text: 'change X scaler by [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '10' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setScaleX', | |
| blockType: BlockType.COMMAND, | |
| text: 'set X scaler to [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'changeScaleY', | |
| blockType: BlockType.COMMAND, | |
| text: 'change Y scaler by [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'setScaleY', | |
| blockType: BlockType.COMMAND, | |
| text: 'set Y scaler to [percent]% on [canvas]', | |
| arguments: { | |
| percent: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: '50' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'resetTransform', | |
| blockType: BlockType.COMMAND, | |
| text: 'clear transform in [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'loadTransform', | |
| blockType: BlockType.COMMAND, | |
| text: 'set new transform [transform] on [canvas]', | |
| arguments: { | |
| transform: { | |
| type: ArgumentType.STRING, | |
| defaultValue: '[1, 0, 0, 1, 0, 0]' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getTransform', | |
| blockType: BlockType.REPORTER, | |
| text: 'get current transform in [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| blockType: BlockType.LABEL, | |
| text: "utilizing" | |
| }, | |
| { | |
| opcode: 'putOntoSprite', | |
| blockType: BlockType.COMMAND, | |
| text: 'set this sprites costume to [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getDataURI', | |
| blockType: BlockType.REPORTER, | |
| text: 'get data URL of [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getWidthOfCanvas', | |
| blockType: BlockType.REPORTER, | |
| text: 'get width of [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getHeightOfCanvas', | |
| blockType: BlockType.REPORTER, | |
| text: 'get height of [canvas]', | |
| arguments: { | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| }, | |
| { | |
| opcode: 'getDrawnWidthOfText', | |
| blockType: BlockType.REPORTER, | |
| text: 'get [dimension] of text [text] when drawn to [canvas]', | |
| arguments: { | |
| dimension: { | |
| type: ArgumentType.STRING, | |
| menu: 'textDimension' | |
| }, | |
| text: { | |
| type: ArgumentType.STRING, | |
| defaultValue: 'bogos binted' | |
| }, | |
| canvas: { | |
| type: ArgumentType.STRING, | |
| menu: 'canvas' | |
| } | |
| } | |
| } | |
| ], | |
| menus: { | |
| textDimension: { | |
| items: [ | |
| 'width', | |
| 'height', | |
| ['bounding box left', 'actualBoundingBoxLeft'], | |
| ['bounding box right', 'actualBoundingBoxRight'], | |
| ['bounding box ascent', 'actualBoundingBoxAscent'], | |
| ['bounding box descent', 'actualBoundingBoxDescent'], | |
| ['font bounding box ascent', 'fontBoundingBoxAscent'], | |
| ['font bounding box descent', 'fontBoundingBoxDescent'] | |
| // maby add the other ones but the em ones be hella spotty | |
| ] | |
| }, | |
| canvas: { | |
| variableType: 'canvas' | |
| }, | |
| canvasProps: { | |
| items: this.propList | |
| } | |
| } | |
| }; | |
| } | |
| createNewCanvas() { | |
| // expect the global ScratchBlocks from inside the window | |
| ScratchBlocks.prompt('New Canvas name:', '', | |
| (name, additionalVars, {scope}) => { | |
| name = ScratchBlocks.Variables.validateScalarVarOrListName_(name, | |
| ScratchBlocks.getMainWorkspace(), additionalVars, false, | |
| 'canvas', 'A Canvas named "%1" already exists.'); | |
| if (!name) return; | |
| const target = scope | |
| ? this.runtime.getTargetForStage() | |
| : this.runtime.vm.editingTarget; | |
| target.createVariable(uid(), name, 'canvas'); | |
| this.runtime.vm.emitWorkspaceUpdate(); | |
| }, 'New Canvas', 'canvas'); | |
| } | |
| /** | |
| * This function is used for any compiled blocks in the extension if they exist. | |
| * Data in this function is given to the IR & JS generators. | |
| * Data must be valid otherwise errors may occur. | |
| * @returns {object} functions that create data for compiled blocks. | |
| */ | |
| getCompileInfo() { | |
| return { | |
| ir: { | |
| canvasGetter: (generator, block) => ({ | |
| kind: 'input', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setSize: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height') | |
| }), | |
| setProperty: (generator, block) => ({ | |
| kind: 'stack', | |
| isField: !!block.fields.value, | |
| prop: block.fields.prop.value, | |
| value: block.fields?.value?.value ?? generator.descendInputOfBlock(block, 'value'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| getProperty: (generator, block) => ({ | |
| kind: 'input', | |
| prop: block.fields.prop.value, | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| dash: (generator, block) => ({ | |
| kind: 'stack', | |
| dashing: generator.descendInputOfBlock(block, 'dashing'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| clearCanvas: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| clearAria: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height') | |
| }), | |
| drawText: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| text: generator.descendInputOfBlock(block, 'text') | |
| }), | |
| drawTextWithCap: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| text: generator.descendInputOfBlock(block, 'text'), | |
| cap: generator.descendInputOfBlock(block, 'cap') | |
| }), | |
| outlineText: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| text: generator.descendInputOfBlock(block, 'text') | |
| }), | |
| outlineTextWithCap: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| text: generator.descendInputOfBlock(block, 'text'), | |
| cap: generator.descendInputOfBlock(block, 'cap') | |
| }), | |
| drawRect: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height') | |
| }), | |
| outlineRect: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height') | |
| }), | |
| preloadUriImage: (generator, block) => ({ | |
| kind: 'stack', | |
| URI: generator.descendInputOfBlock(block, 'URI'), | |
| NAME: generator.descendInputOfBlock(block, 'NAME') | |
| }), | |
| unloadUriImage: (generator, block) => ({ | |
| kind: 'stack', | |
| NAME: generator.descendInputOfBlock(block, 'NAME') | |
| }), | |
| getWidthOfPreloaded: (generator, block) => ({ | |
| kind: 'input', | |
| name: generator.descendInputOfBlock(block, 'name') | |
| }), | |
| getHeightOfPreloaded: (generator, block) => ({ | |
| kind: 'input', | |
| name: generator.descendInputOfBlock(block, 'name') | |
| }), | |
| drawUriImage: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| URI: generator.descendInputOfBlock(block, 'URI'), | |
| X: generator.descendInputOfBlock(block, 'X'), | |
| Y: generator.descendInputOfBlock(block, 'Y') | |
| }), | |
| drawUriImageWHR: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| URI: generator.descendInputOfBlock(block, 'URI'), | |
| X: generator.descendInputOfBlock(block, 'X'), | |
| Y: generator.descendInputOfBlock(block, 'Y'), | |
| WIDTH: generator.descendInputOfBlock(block, 'WIDTH'), | |
| HEIGHT: generator.descendInputOfBlock(block, 'HEIGHT'), | |
| ROTATE: generator.descendInputOfBlock(block, 'ROTATE') | |
| }), | |
| drawUriImageWHCX1Y1X2Y2R: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas'), | |
| URI: generator.descendInputOfBlock(block, 'URI'), | |
| X: generator.descendInputOfBlock(block, 'X'), | |
| Y: generator.descendInputOfBlock(block, 'Y'), | |
| WIDTH: generator.descendInputOfBlock(block, 'WIDTH'), | |
| HEIGHT: generator.descendInputOfBlock(block, 'HEIGHT'), | |
| CROPX: generator.descendInputOfBlock(block, 'CROPX'), | |
| CROPY: generator.descendInputOfBlock(block, 'CROPY'), | |
| CROPW: generator.descendInputOfBlock(block, 'CROPW'), | |
| CROPH: generator.descendInputOfBlock(block, 'CROPH'), | |
| ROTATE: generator.descendInputOfBlock(block, 'ROTATE') | |
| }), | |
| getWidthOfCanvas: (generator, block) => ({ | |
| kind: 'input', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| getHeightOfCanvas: (generator, block) => ({ | |
| kind: 'input', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| beginPath: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| moveTo: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| lineTo: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| arcTo: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| controlPoints: generator.descendInputOfBlock(block, 'controlPoints'), | |
| radius: generator.descendInputOfBlock(block, 'radius'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| addRect: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| addEllipse: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height'), | |
| dir: generator.descendInputOfBlock(block, 'dir'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| addEllipseStartStop: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| width: generator.descendInputOfBlock(block, 'width'), | |
| height: generator.descendInputOfBlock(block, 'height'), | |
| start: generator.descendInputOfBlock(block, 'start'), | |
| end: generator.descendInputOfBlock(block, 'end'), | |
| dir: generator.descendInputOfBlock(block, 'dir'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| stroke: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| fill: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| saveTransform: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| restoreTransform: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| turnRotationLeft: (generator, block) => ({ | |
| kind: 'stack', | |
| degrees: generator.descendInputOfBlock(block, 'degrees'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| turnRotationRight: (generator, block) => ({ | |
| kind: 'stack', | |
| degrees: generator.descendInputOfBlock(block, 'degrees'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setRotation: (generator, block) => ({ | |
| kind: 'stack', | |
| degrees: generator.descendInputOfBlock(block, 'degrees'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setTranslateXY: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeTranslateXY: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'x'), | |
| y: generator.descendInputOfBlock(block, 'y'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeTranslateX: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'amount'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setTranslateX: (generator, block) => ({ | |
| kind: 'stack', | |
| x: generator.descendInputOfBlock(block, 'amount'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeTranslateY: (generator, block) => ({ | |
| kind: 'stack', | |
| y: generator.descendInputOfBlock(block, 'amount'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setTranslateY: (generator, block) => ({ | |
| kind: 'stack', | |
| y: generator.descendInputOfBlock(block, 'amount'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeScaleXY: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setScaleXY: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeScaleX: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setScaleX: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| changeScaleY: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| setScaleY: (generator, block) => ({ | |
| kind: 'stack', | |
| scale: generator.descendInputOfBlock(block, 'percent'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| resetTransform: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| loadTransform: (generator, block) => ({ | |
| kind: 'stack', | |
| transform: generator.descendInputOfBlock(block, 'transform'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| getTransform: (generator, block) => ({ | |
| kind: 'input', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| putOntoSprite: (generator, block) => ({ | |
| kind: 'stack', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| getDataURI: (generator, block) => ({ | |
| kind: 'input', | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }), | |
| getDrawnWidthOfText: (generator, block) => ({ | |
| kind: 'input', | |
| prop: block.fields.dimension.value, | |
| text: generator.descendInputOfBlock(block, 'text'), | |
| canvas: generator.descendVariable(block, 'canvas', 'canvas') | |
| }) | |
| }, | |
| js: { | |
| canvasGetter: (node, compiler, {TypedInput, TYPE_UNKNOWN}) => | |
| new TypedInput(compiler.referenceVariable(node.canvas), TYPE_UNKNOWN), | |
| setSize: (node, compiler) => { | |
| console.log(node); | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| compiler.source += `${canvas}.canvas.width = ${width};\n`; | |
| compiler.source += `${canvas}.canvas.height = ${height};\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| setProperty: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const val = node.isField | |
| ? node.value | |
| : compiler.descendInput(node.value); | |
| compiler.source += `${ctx}.${node.prop} = `; | |
| const target = this.sbInfo[node.prop]; | |
| switch (target.type) { | |
| case ArgumentType.STRING: | |
| compiler.source += val.asString(); | |
| break; | |
| case ArgumentType.NUMBER: | |
| compiler.source += val.asNumber(); | |
| break; | |
| case ArgumentType.BOOLEAN: | |
| compiler.source += val.asBoolean(); | |
| break; | |
| case ArgumentType.COLOR: | |
| compiler.source += val.asString(); | |
| break; | |
| default: | |
| compiler.source += `"${sanitize(val)}"`; | |
| } | |
| compiler.source += ';\n'; | |
| }, | |
| getProperty: (node, compiler, {TypedInput, TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, TYPE_UNKNOWN}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| let type = TYPE_UNKNOWN; | |
| const target = this.sbInfo[node.prop]; | |
| switch (target.type) { | |
| case ArgumentType.STRING: | |
| type = TYPE_STRING; | |
| break; | |
| case ArgumentType.NUMBER: | |
| type = TYPE_NUMBER; | |
| break; | |
| case ArgumentType.BOOLEAN: | |
| type = TYPE_BOOLEAN; | |
| break; | |
| case ArgumentType.COLOR: | |
| type = TYPE_STRING; | |
| break; | |
| default: | |
| type = TYPE_STRING; | |
| } | |
| return new TypedInput(`${ctx}.${node.prop}`, type); | |
| }, | |
| dash: (node, compiler, {ConstantInput}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const arrInp = compiler.descendInput(node.dashing); | |
| const isConstant = arrInp instanceof ConstantInput; | |
| compiler.source += `${ctx}.setLineDash(`; | |
| if (!isConstant) compiler.source += `parseJSONSafe(`; | |
| compiler.source += isConstant | |
| ? arrInp.constantValue | |
| : arrInp.asUnknown(); | |
| if (!isConstant) compiler.source += ')'; | |
| compiler.source += ');\n'; | |
| }, | |
| clearCanvas: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.clearRect(0, 0, ${canvas}.canvas.width, ${canvas}.canvas.height);\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| clearAria: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| compiler.source += `${ctx}.clearRect(${x}, ${y}, ${width}, ${height});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| drawText: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const text = compiler.descendInput(node.text).asString(); | |
| compiler.source += `${ctx}.fillText(${text}, ${x}, ${y});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| drawTextWithCap: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const text = compiler.descendInput(node.text).asString(); | |
| const cap = compiler.descendInput(node.cap).asNumber(); | |
| compiler.source += `${ctx}.fillText(${text}, ${x}, ${y}, ${cap});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| outlineText: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const text = compiler.descendInput(node.text).asString(); | |
| compiler.source += `${ctx}.strokeText(${text}, ${x}, ${y});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| outlineTextWithCap: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const text = compiler.descendInput(node.text).asString(); | |
| const cap = compiler.descendInput(node.cap).asNumber(); | |
| compiler.source += `${ctx}.strokeText(${text}, ${x}, ${y}, ${cap});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| drawRect: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| compiler.source += `${ctx}.fillRect(${x}, ${y}, ${width}, ${height});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| outlineRect: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| compiler.source += `${ctx}.strokeRect(${x}, ${y}, ${width}, ${height});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| preloadUriImage: (node, compiler) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const preloadName = compiler.descendInput(node.NAME).asString(); | |
| const preloadUri = compiler.descendInput(node.URI).asUnknown(); | |
| compiler.source += `${allPreloaded}[${preloadName}] = yield* waitPromise(`; | |
| compiler.source += `resolveImageURL(${preloadUri})`; | |
| compiler.source += ');\n'; | |
| }, | |
| unloadUriImage: (node, compiler) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const preloadName = compiler.descendInput(node.NAME).asString(); | |
| compiler.source += `if (${allPreloaded}[${preloadName}]) {`; | |
| compiler.source += `${allPreloaded}[${preloadName}].remove();\n`; | |
| compiler.source += `delete ${allPreloaded}[${preloadName}];\n`; | |
| compiler.source += '}'; | |
| }, | |
| getWidthOfPreloaded: (node, compiler, {TypedInput, TYPE_NUMBER}) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const preloadName = compiler.descendInput(node.name).asString(); | |
| return new TypedInput(`${allPreloaded}[${preloadName}]?.width ?? 0`, TYPE_NUMBER); | |
| }, | |
| getHeightOfPreloaded: (node, compiler, {TypedInput, TYPE_NUMBER}) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const preloadName = compiler.descendInput(node.name).asString(); | |
| return new TypedInput(`${allPreloaded}[${preloadName}]?.height ?? 0`, TYPE_NUMBER); | |
| }, | |
| drawUriImage: (node, compiler) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const uri = compiler.descendInput(node.URI).asUnknown(); | |
| const x = compiler.descendInput(node.X).asNumber(); | |
| const y = compiler.descendInput(node.Y).asNumber(); | |
| compiler.source += `${ctx}.drawImage(`; | |
| compiler.source += `${allPreloaded}[${uri}]`; | |
| compiler.source += `? ${allPreloaded}[${uri}]`; | |
| compiler.source += `: yield* waitPromise(resolveImageURL(${uri}))`; | |
| compiler.source += `, ${x}, ${y});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| drawUriImageWHR: (node, compiler) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const uri = compiler.descendInput(node.URI).asUnknown(); | |
| const x = compiler.descendInput(node.X).asNumber(); | |
| const y = compiler.descendInput(node.Y).asNumber(); | |
| const width = compiler.descendInput(node.WIDTH).asNumber(); | |
| const height = compiler.descendInput(node.HEIGHT).asNumber(); | |
| const dir = compiler.descendInput(node.ROTATE).asNumber(); | |
| compiler.source += `${ctx}.drawImage(`; | |
| compiler.source += `${allPreloaded}[${uri}] ? `; | |
| compiler.source += `${allPreloaded}[${uri}] : `; | |
| compiler.source += `yield* waitPromise(resolveImageURL(${uri}))`; | |
| compiler.source += `, ${x}, ${y}, ${width}, ${height}, ${dir});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| drawUriImageWHCX1Y1X2Y2R: (node, compiler) => { | |
| const allPreloaded = compiler.evaluateOnce('{}'); | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const uri = compiler.descendInput(node.URI).asUnknown(); | |
| const x = compiler.descendInput(node.X).asNumber(); | |
| const y = compiler.descendInput(node.Y).asNumber(); | |
| const width = compiler.descendInput(node.WIDTH).asNumber(); | |
| const height = compiler.descendInput(node.HEIGHT).asNumber(); | |
| const dir = compiler.descendInput(node.ROTATE).asNumber(); | |
| const cropX = compiler.descendInput(node.CROPX).asNumber(); | |
| const cropY = compiler.descendInput(node.CROPY).asNumber(); | |
| const cropWidth = compiler.descendInput(node.CROPW).asNumber(); | |
| const cropHeight = compiler.descendInput(node.CROPH).asNumber(); | |
| compiler.source += `${ctx}.drawImage(`; | |
| compiler.source += `${allPreloaded}[${uri}] ? `; | |
| compiler.source += `${allPreloaded}[${uri}] : `; | |
| compiler.source += `yield* waitPromise(resolveImageURL(${uri}))`; | |
| compiler.source += `, ${x}, ${y}, ${width}, ${height}, ${dir}, `; | |
| compiler.source += `${cropX}, ${cropY}, ${cropWidth}, ${cropHeight});\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| getWidthOfCanvas: (node, compiler, {TYPE_NUMBER, TypedInput}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| return new TypedInput(`${canvas}.canvas.width`, TYPE_NUMBER); | |
| }, | |
| getHeightOfCanvas: (node, compiler, {TYPE_NUMBER, TypedInput}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| return new TypedInput(`${canvas}.canvas.height`, TYPE_NUMBER); | |
| }, | |
| beginPath: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.beginPath();\n`; | |
| }, | |
| moveTo: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.moveTo(${x}, ${y});\n`; | |
| }, | |
| lineTo: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.lineTo(${x}, ${y});\n`; | |
| }, | |
| arcTo: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const controlPoints = compiler.descendInput(node.controlPoints).asUnknown(); | |
| const radius = compiler.descendInput(node.radius).asNumber(); | |
| compiler.source += `${ctx}.arcTo(${x}, ${y}, ...${controlPoints}, ${radius});\n`; | |
| }, | |
| addRect: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| compiler.source += `${ctx}.rect(${x}, ${y}, ${width}, ${height});\n`; | |
| }, | |
| addEllipse: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| const dir = compiler.descendInput(node.dir).asNumber(); | |
| compiler.source += `${ctx}.ellipse(${x}, ${y}, ${width}, ${height}`; | |
| compiler.source += `, (${dir} - 90) * Math.PI / 180, 0, 2 * Math.PI);\n`; | |
| }, | |
| addEllipseStartStop: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| const width = compiler.descendInput(node.width).asNumber(); | |
| const height = compiler.descendInput(node.height).asNumber(); | |
| const dir = compiler.descendInput(node.dir).asNumber(); | |
| const start = compiler.descendInput(node.start).asNumber(); | |
| const end = compiler.descendInput(node.end).asNumber(); | |
| compiler.source += `${ctx}.ellipse(${x}, ${y}, ${width}, ${height}, `; | |
| compiler.source += `(${dir} - 90) * Math.PI / 180, (${start} - 90) * Math.PI / 180, (${end} - 90) * Math.PI / 180);\n`; | |
| }, | |
| closePath: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.soource += `${ctx}.closePath()`; | |
| }, | |
| stroke: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.stroke();\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| fill: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.fill();\n`; | |
| compiler.source += `${canvas}.updateCanvasContentRenders();\n`; | |
| }, | |
| saveTransform: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.save();\n`; | |
| }, | |
| restoreTransform: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.restore();\n`; | |
| }, | |
| turnRotationLeft: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const degrees = compiler.descendInput(node.degrees).asNumber(); | |
| compiler.source += `${ctx}.rotate(`; | |
| compiler.source += `(${canvas}._cameraStuff.rotation -= ${degrees}) * Math.PI / 180`; | |
| compiler.source += `);\n`; | |
| }, | |
| turnRotationRight: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const degrees = compiler.descendInput(node.degrees).asNumber(); | |
| compiler.source += `${ctx}.rotate(`; | |
| compiler.source += `(${canvas}._cameraStuff.rotation += ${degrees}) * Math.PI / 180`; | |
| compiler.source += `);\n`; | |
| }, | |
| setRotation: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const degrees = compiler.descendInput(node.degrees).asNumber(); | |
| compiler.source += `${ctx}.rotate(`; | |
| compiler.source += `((${canvas}._cameraStuff.rotation = ${degrees}) - 90) * Math.PI / 180`; | |
| compiler.source += `);\n`; | |
| }, | |
| setTranslateXY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x = ${x},`; | |
| compiler.source += `${canvas}._cameraStuff.y = ${y}`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeTranslateXY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x += ${x},`; | |
| compiler.source += `${canvas}._cameraStuff.y += ${y}`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeTranslateX: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x += ${x},`; | |
| compiler.source += `${canvas}._cameraStuff.y`; | |
| compiler.source += `);\n`; | |
| }, | |
| setTranslateX: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const x = compiler.descendInput(node.x).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x = ${x},`; | |
| compiler.source += `${canvas}._cameraStuff.y`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeTranslateY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x,`; | |
| compiler.source += `${canvas}._cameraStuff.y += ${y}`; | |
| compiler.source += `);\n`; | |
| }, | |
| setTranslateY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const y = compiler.descendInput(node.y).asNumber(); | |
| compiler.source += `${ctx}.translate(`; | |
| compiler.source += `${canvas}._cameraStuff.x,`; | |
| compiler.source += `${canvas}._cameraStuff.y = ${y}`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeScaleXY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX += (${scale} / 100),`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY += (${scale} / 100)`; | |
| compiler.source += `);\n`; | |
| }, | |
| setScaleXY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX = (${scale} / 100),`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY = (${scale} / 100)`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeScaleX: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX += (${scale} / 100),`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY`; | |
| compiler.source += `);\n`; | |
| }, | |
| setScaleX: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX = (${scale} / 100),`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY`; | |
| compiler.source += `);\n`; | |
| }, | |
| changeScaleY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX,`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY += (${scale} / 100)`; | |
| compiler.source += `);\n`; | |
| }, | |
| setScaleY: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const scale = compiler.descendInput(node.scale).asNumber(); | |
| compiler.source += `${ctx}.scale(`; | |
| compiler.source += `${canvas}._cameraStuff.scaleX,`; | |
| compiler.source += `${canvas}._cameraStuff.scaleY = (${scale} / 100)`; | |
| compiler.source += `);\n`; | |
| }, | |
| resetTransform: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| compiler.source += `${ctx}.resetTransform();\n`; | |
| }, | |
| loadTransform: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const transform = compiler.descendInput(node.transform).asString(); | |
| compiler.source += `${ctx}.setTransform(`; | |
| compiler.source += `parseJSONSafe(${transform})`; | |
| compiler.source += `);\n`; | |
| }, | |
| getTransform: (node, compiler, { TypedInput, TYPE_STRING }) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| let content = '(() => {'; | |
| content += `const transform = ${ctx}.getTransform(); `; | |
| content += 'return JSON.stringify(['; | |
| content += 'transform.a, transform.b, transform.c, '; | |
| content += 'transform.d, transform.e, transform.f'; | |
| content += '])})()'; | |
| return new TypedInput(content, TYPE_STRING); | |
| }, | |
| putOntoSprite: (node, compiler) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| compiler.source += `${canvas}.applyCanvasToTarget(target);\n`; | |
| }, | |
| getDataURI: (node, compiler, {TypedInput, TYPE_STRING}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| return new TypedInput(`${canvas}.toString()`, TYPE_STRING); | |
| }, | |
| getDrawnWidthOfText: (node, compiler, {TypedInput, TYPE_NUMBER}) => { | |
| const canvas = compiler.referenceVariable(node.canvas); | |
| const text = compiler.descendInput(node.text).asString(); | |
| const ctx = compiler.evaluateOnce(`${canvas}.canvas.getContext('2d')`); | |
| const cache = compiler.evaluateOnce(`{}`); | |
| let code = `(text => {`; | |
| code += `let textMeasure = ${cache}[text + ${ctx}.font]`; | |
| code += `if (!textMeasure) {`; | |
| code += `textMeasure = ${ctx}.measureText(text);\n`; | |
| code += `${cache}[text + ${ctx}.font] = textMeasure;\n`; | |
| code += '}\n'; | |
| code += 'return textMeasure.'; | |
| switch (node.prop) { | |
| case 'height': | |
| code += `actualBoundingBoxAscent + textMeasure.actualBoundingBoxDescent`; | |
| break; | |
| default: | |
| code += node.prop; | |
| } | |
| code += `;})(${text})`; | |
| return new TypedInput(code, TYPE_NUMBER); | |
| } | |
| } | |
| }; | |
| } | |
| getOrCreateVariable(target, id, name) { | |
| const stage = this.runtime.getTargetForStage(); | |
| const variable = target.variables[id] ?? stage.variables[id]; | |
| if (!variable) { | |
| return target.createVariable(id, name); | |
| } | |
| return variable; | |
| } | |
| // display monitors | |
| canvasGetter(args, util) { | |
| const canvasObj = this.getOrCreateVariable(util.target, args.canvas.id, args.canvas.name); | |
| return canvasObj; | |
| } | |
| getProperty(args, util) { | |
| const canvasObj = this.getOrCreateVariable(util.target, args.canvas.id, args.canvas.name); | |
| const ctx = canvasObj.canvas.getContext('2d'); | |
| return ctx[args.prop]; | |
| } | |
| getDataURI(args, util) { | |
| const canvasObj = this.getOrCreateVariable(util.target, args.canvas.id, args.canvas.name); | |
| return canvasObj.toString(); | |
| } | |
| getWidthOfPreloaded ({ name }) { | |
| if (!this.preloadedImages.hasOwnProperty(name)) return 0; | |
| return this.preloadedImages[name].width; | |
| } | |
| getHeightOfPreloaded ({ name }) { | |
| if (!this.preloadedImages.hasOwnProperty(name)) return 0; | |
| return this.preloadedImages[name].height; | |
| } | |
| getWidthOfCanvas({ canvas }, util) { | |
| const canvasObj = this.getOrCreateVariable(util.target, canvas.id, canvas.name); | |
| return canvasObj.size[0]; | |
| } | |
| getHeightOfCanvas({ canvas }, util) { | |
| const canvasObj = this.getOrCreateVariable(util.target, canvas.id, canvas.name); | |
| return canvasObj.size[1]; | |
| } | |
| } | |
| module.exports = canvas; | |