Spaces:
Runtime error
Runtime error
| const BlockType = require('../../extension-support/block-type') | |
| const BlockShape = require('../../extension-support/block-shape') | |
| const ArgumentType = require('../../extension-support/argument-type') | |
| const Cast = require('../../util/cast') | |
| /** | |
| * @param {number} x | |
| * @returns {string} | |
| */ | |
| function formatNumber(x) { | |
| if (x >= 1e6) { | |
| return x.toExponential(4) | |
| } else { | |
| x = Math.floor(x * 1000) / 1000 | |
| return x.toFixed(Math.min(3, (String(x).split('.')[1] || '').length)) | |
| } | |
| } | |
| function span(text) { | |
| let el = document.createElement('span') | |
| el.innerHTML = text | |
| el.style.display = 'hidden' | |
| el.style.whiteSpace = 'nowrap' | |
| el.style.width = '100%' | |
| el.style.textAlign = 'center' | |
| return el | |
| } | |
| class VectorType { | |
| customId = "jwVector" | |
| constructor(x = 0, y = 0) { | |
| this.x = isNaN(x) ? 0 : x | |
| this.y = isNaN(y) ? 0 : y | |
| } | |
| static toVector(x) { | |
| if (x instanceof VectorType) return x | |
| if (x instanceof Array && x.length == 2) return new VectorType(x[0], x[1]) | |
| if (String(x).split(',')) return new VectorType(Cast.toNumber(String(x).split(',')[0]), Cast.toNumber(String(x).split(',')[1])) | |
| return new VectorType(0, 0) | |
| } | |
| jwArrayHandler() { | |
| return 'Vector' | |
| } | |
| toString() { | |
| return `${this.x},${this.y}` | |
| } | |
| toMonitorContent = () => span(this.toString()) | |
| toReporterContent() { | |
| let root = document.createElement('div') | |
| root.style.display = 'flex' | |
| root.style.width = "200px" | |
| root.style.overflow = "hidden" | |
| let details = document.createElement('div') | |
| details.style.display = 'flex' | |
| details.style.flexDirection = 'column' | |
| details.style.justifyContent = 'center' | |
| details.style.width = "100px" | |
| details.appendChild(span(`<b>X:</b> ${formatNumber(this.x)}`)) | |
| details.appendChild(span(`<b>Y:</b> ${formatNumber(this.y)}`)) | |
| root.appendChild(details) | |
| let angle = document.createElement('div') | |
| angle.style.width = "100px" | |
| let circle = document.createElement('div') | |
| circle.style.width = "84px" | |
| circle.style.height = "84px" | |
| circle.style.margin = "8px" | |
| circle.style.border = "4px solid black" | |
| circle.style.borderRadius = "100%" | |
| circle.style.boxSizing = "border-box" | |
| circle.style.transform = `rotate(${this.angle}deg)` | |
| let line = document.createElement('div') | |
| line.style.width = "8px" | |
| line.style.height = "50%" | |
| line.style.background = "black" | |
| line.style.position = "absolute" | |
| line.style.left = "calc(50% - 4px)" | |
| circle.appendChild(line) | |
| angle.appendChild(circle) | |
| root.appendChild(angle) | |
| return root | |
| } | |
| /** @returns {number} */ | |
| get magnitude() { return Math.hypot(this.x, this.y) } | |
| /** @returns {number} */ | |
| get angle() {return Math.atan2(this.x, this.y) * (180 / Math.PI)} | |
| } | |
| const Vector = { | |
| Type: VectorType, | |
| Block: { | |
| blockType: BlockType.REPORTER, | |
| blockShape: BlockShape.LEAF, | |
| forceOutputType: "Vector", | |
| disableMonitor: true | |
| }, | |
| Argument: { | |
| shape: BlockShape.LEAF, | |
| check: ["Vector"] | |
| } | |
| } | |
| class Extension { | |
| constructor() { | |
| vm.jwVector = Vector | |
| vm.runtime.registerSerializer( | |
| "jwVector", | |
| v => [v.x, v.y], | |
| v => new Vector.Type(v[0], v[1]) | |
| ); | |
| } | |
| getInfo() { | |
| return { | |
| id: "jwVector", | |
| name: "Vector", | |
| color1: "#6babff", | |
| menuIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM6Yng9Imh0dHBzOi8vYm94eS1zdmcuY29tIj4KICA8ZWxsaXBzZSBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAycHg7IHBhaW50LW9yZGVyOiBzdHJva2U7IGZpbGw6IHJnYigxMDcsIDE3MSwgMjU1KTsgc3Ryb2tlOiByZ2IoNjksIDEyNiwgMjA0KTsiIGN4PSIxMCIgY3k9IjEwIiByeD0iOSIgcnk9IjkiPjwvZWxsaXBzZT4KICA8cGF0aCBkPSJNIDQuMzUyIDEzLjc2NiBDIDQuMzUyIDE0LjgwNSA1LjE5NCAxNS42NDggNi4yMzUgMTUuNjQ4IEwgMTAgMTUuNjQ4IEMgMTEuMDM5IDE1LjY0OCAxMS44ODIgMTQuODA1IDExLjg4MiAxMy43NjYgTCAxMS44ODIgMTAgQyAxMS44ODIgOC45NTkgMTEuMDM5IDguMTE4IDEwIDguMTE4IEwgNi4yMzUgOC4xMTggQyA1LjE5NCA4LjExOCA0LjM1MiA4Ljk1OSA0LjM1MiAxMCBMIDQuMzUyIDEzLjc2NiBNIDguMTE3IDEzLjc2NiBDIDYuNjY4IDEzLjc2NiA1Ljc2MiAxMi4xOTUgNi40ODcgMTAuOTQyIEMgNi44MjIgMTAuMzU4IDcuNDQzIDEwIDguMTE3IDEwIEMgOS41NjcgMTAgMTAuNDcyIDExLjU2OSA5Ljc0NyAxMi44MjQgQyA5LjQxMSAxMy40MDYgOC43ODkgMTMuNzY2IDguMTE3IDEzLjc2NiBNIDcuMTc2IDkuMDU5IEwgOS4wNTggOS4wNTkgTCA5LjA1OCA1LjI5NCBDIDkuMDU4IDQuNTY5IDguMjczIDQuMTE2IDcuNjQ3IDQuNDc5IEMgNy4zNTUgNC42NDYgNy4xNzYgNC45NTcgNy4xNzYgNS4yOTQgTCA3LjE3NiA5LjA1OSBaIE0gMTAuOTQxIDEwLjk0MiBMIDEwLjk0MSAxMi44MjQgTCAxNC43MDYgMTIuODI0IEMgMTUuNDMxIDEyLjgyNCAxNS44ODMgMTIuMDM5IDE1LjUyMSAxMS40MTIgQyAxNS4zNTIgMTEuMTIxIDE1LjA0MSAxMC45NDIgMTQuNzA2IDEwLjk0MiBMIDEwLjk0MSAxMC45NDIgWiIgc3R5bGU9ImZpbGw6IHJnYigyNTUsIDI1NSwgMjU1KTsiPjwvcGF0aD4KPC9zdmc+", | |
| blocks: [ | |
| { | |
| opcode: 'newVector', | |
| text: 'new vector x: [X] y: [Y]', | |
| arguments: { | |
| X: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| }, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 0 | |
| } | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'newVectorFromMagnitude', | |
| text: 'new vector magnitude: [X] angle: [Y]', | |
| arguments: { | |
| X: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 1 | |
| }, | |
| Y: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 0 | |
| } | |
| }, | |
| ...Vector.Block | |
| }, | |
| "---", | |
| { | |
| opcode: 'vectorX', | |
| text: '[VECTOR] x', | |
| blockType: BlockType.REPORTER, | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| } | |
| }, | |
| { | |
| opcode: 'vectorY', | |
| text: '[VECTOR] y', | |
| blockType: BlockType.REPORTER, | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| } | |
| }, | |
| "---", | |
| { | |
| opcode: 'add', | |
| text: '[X] + [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'subtract', | |
| text: '[X] - [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'multiplyA', | |
| text: '[X] * [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 1 | |
| } | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'multiplyB', | |
| text: '[X] * [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'divideA', | |
| text: '[X] / [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: { | |
| type: ArgumentType.NUMBER, | |
| defaultValue: 1 | |
| } | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'divideB', | |
| text: '[X] / [Y]', | |
| arguments: { | |
| X: Vector.Argument, | |
| Y: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| "---", | |
| { | |
| opcode: 'magnitude', | |
| text: 'magnitude of [VECTOR]', | |
| blockType: BlockType.REPORTER, | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| } | |
| }, | |
| { | |
| opcode: 'angle', | |
| text: 'angle of [VECTOR]', | |
| blockType: BlockType.REPORTER, | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| } | |
| }, | |
| { | |
| opcode: 'normalize', | |
| text: 'normalize [VECTOR]', | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'absolute', | |
| text: 'absolute [VECTOR]', | |
| arguments: { | |
| VECTOR: Vector.Argument | |
| }, | |
| ...Vector.Block | |
| }, | |
| { | |
| opcode: 'rotate', | |
| text: 'rotate [VECTOR] by [ANGLE]', | |
| arguments: { | |
| VECTOR: Vector.Argument, | |
| ANGLE: { | |
| type: ArgumentType.ANGLE, | |
| defaultValue: 90 | |
| } | |
| }, | |
| ...Vector.Block | |
| }, | |
| ] | |
| }; | |
| } | |
| newVector(args) { | |
| const X = Cast.toNumber(args.X) | |
| const Y = Cast.toNumber(args.Y) | |
| return new VectorType(X, Y) | |
| } | |
| newVectorFromMagnitude(args) { | |
| return this.rotate({VECTOR: new VectorType(0, Cast.toNumber(args.X)), ANGLE: args.Y}) | |
| } | |
| vectorX(args) { | |
| return VectorType.toVector(args.VECTOR).x | |
| } | |
| vectorY(args) { | |
| return VectorType.toVector(args.VECTOR).y | |
| } | |
| add(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = VectorType.toVector(args.Y) | |
| return new VectorType(X.x + Y.x, X.y + Y.y) | |
| } | |
| subtract(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = VectorType.toVector(args.Y) | |
| return new VectorType(X.x - Y.x, X.y - Y.y) | |
| } | |
| multiplyA(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = Cast.toNumber(args.Y) | |
| return new VectorType(X.x * Y, X.y * Y) | |
| } | |
| multiplyB(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = VectorType.toVector(args.Y) | |
| return new VectorType(X.x * Y.x, X.y * Y.y) | |
| } | |
| divideA(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = Cast.toNumber(args.Y) | |
| return new VectorType(X.x / Y, X.y / Y) | |
| } | |
| divideB(args) { | |
| const X = VectorType.toVector(args.X) | |
| const Y = VectorType.toVector(args.Y) | |
| return new VectorType(X.x / Y.x, X.y / Y.y) | |
| } | |
| magnitude(args) { | |
| return VectorType.toVector(args.VECTOR).magnitude | |
| } | |
| angle(args) { | |
| return VectorType.toVector(args.VECTOR).angle | |
| } | |
| normalize(args) { | |
| const v = VectorType.toVector(args.VECTOR) | |
| return new VectorType(v.x / v.magnitude, v.y / v.magnitude) | |
| } | |
| absolute(args) { | |
| const v = VectorType.toVector(args.VECTOR) | |
| return new VectorType(Math.abs(v.x), Math.abs(v.y)) | |
| } | |
| rotate(args) { | |
| const v = VectorType.toVector(args.VECTOR) | |
| const ANGLE = Cast.toNumber(args.ANGLE) / 180 * -Math.PI | |
| const cos = Math.cos(ANGLE) | |
| const sin = Math.sin(ANGLE) | |
| return new VectorType( | |
| v.x * cos - v.y * sin, | |
| v.x * sin + v.y * cos | |
| ) | |
| } | |
| } | |
| module.exports = Extension |