diff --git a/editor/extension.ts b/editor/extension.ts index 90055b3a..d2f8120b 100644 --- a/editor/extension.ts +++ b/editor/extension.ts @@ -4,6 +4,7 @@ import { deployCoreAsync, initAsync } from "./deploy"; import { FieldPorts } from "./field_ports"; import { FieldImages } from "./field_images"; +import {FieldSpeed} from "./field_speed"; pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise { pxt.debug('loading pxt-ev3 target extensions...') @@ -15,6 +16,9 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P }, { selector: "images", editor: FieldImages + }, { + selector: "speed", + editor: FieldSpeed }], deployCoreAsync }; diff --git a/editor/field_speed.ts b/editor/field_speed.ts new file mode 100644 index 00000000..f77b166f --- /dev/null +++ b/editor/field_speed.ts @@ -0,0 +1,92 @@ +/// +/// + +export interface FieldSpeedOptions extends Blockly.FieldCustomOptions { + min?: string; + max?: string; + label?: string; +} + +export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCustom { + public isFieldCustom_ = true; + + private params: any; + + private speedSVG: SVGElement; + private circleBar: SVGCircleElement; + private reporter: SVGTextElement; + + /** + * Class for a color wheel field. + * @param {number|string} value The initial content of the field. + * @param {Function=} opt_validator An optional function that is called + * to validate any constraints on what the user entered. Takes the new + * text as an argument and returns either the accepted text, a replacement + * text, or null to abort the change. + * @extends {Blockly.FieldNumber} + * @constructor + */ + constructor(value_: any, params: FieldSpeedOptions, opt_validator?: Function) { + super(String(value_), '-100', '100', null, '10', 'Speed', opt_validator); + this.params = params; + if (this.params['min']) this.min_ = parseFloat(this.params.min); + if (this.params['max']) this.max_ = parseFloat(this.params.max); + if (this.params['label']) this.labelText_ = this.params.label; + + (this as any).sliderColor_ = '#a8aaa8'; + } + + createLabelDom_(labelText: string) { + var labelContainer = document.createElement('div'); + this.speedSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGGElement; + pxsim.svg.hydrate(this.speedSVG, { + viewBox: "0 0 200 100" + }); + + labelContainer.appendChild(this.speedSVG); + + const outerCircle = pxsim.svg.child(this.speedSVG, "circle", { + 'stroke-dasharray': '565.48', 'stroke-dashoffset': '0', + 'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`, + 'stroke': '#a8aaa8', 'stroke-width': '1rem' + }) as SVGCircleElement; + this.circleBar = pxsim.svg.child(this.speedSVG, "circle", { + 'stroke-dasharray': '565.48', 'stroke-dashoffset': '0', + 'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 0.1s linear;`, + 'stroke': '#f12a21', 'stroke-width': '1rem' + }) as SVGCircleElement; + + this.reporter = pxsim.svg.child(this.speedSVG, "text", { + 'x': 100, 'y': 80, + 'text-anchor': 'middle', 'alignment-baseline': 'middle', + 'style': 'font-size: 50px', + 'class': 'sim-text inverted number' + }) as SVGTextElement; + + // labelContainer.setAttribute('class', 'blocklyFieldSliderLabel'); + var readout = document.createElement('span'); + readout.setAttribute('class', 'blocklyFieldSliderReadout'); + // var label = document.createElement('span'); + // label.setAttribute('class', 'blocklyFieldSliderLabelText'); + // label.innerHTML = labelText; + // labelContainer.appendChild(label); + // labelContainer.appendChild(readout); + return [labelContainer, readout]; + }; + + setReadout_(readout: Element, value: string) { + this.updateSpeed(parseFloat(value)); + // Update reporter + this.reporter.textContent = `${value}`; + } + + private updateSpeed(speed: number) { + speed = Math.abs(speed); + speed = speed / 100 * 50; + speed += 50; + let c = Math.PI * (90 * 2); + let pct = ((100 - speed) / 100) * c; + this.circleBar.setAttribute('stroke-dashoffset', `${pct}`); + } + +} \ No newline at end of file diff --git a/libs/core/_locales/core-strings.json b/libs/core/_locales/core-strings.json index c705c3ef..d2482e3b 100644 --- a/libs/core/_locales/core-strings.json +++ b/libs/core/_locales/core-strings.json @@ -55,8 +55,8 @@ "motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready", "motors.MotorBase.setBrake|block": "set %motor|brake %brake", "motors.MotorBase.setReversed|block": "set %motor|reversed %reversed", - "motors.MotorBase.setSpeedFor|block": "set %motor|speed to %speed|%|for %value|%unit", - "motors.MotorBase.setSpeed|block": "set %motor|speed to %speed|%", + "motors.MotorBase.setSpeedFor|block": "set %motor|speed to %speed=motorSpeedPicker|%|for %value|%unit", + "motors.MotorBase.setSpeed|block": "set %motor|speed to %speed=motorSpeedPicker|%", "motors.SynchedMotorPair.steerFor|block": "steer %chassis|turn ratio %turnRatio|speed %speed|%|for %value|%unit", "motors.SynchedMotorPair.steer|block": "steer %chassis|turn ratio %turnRatio|speed %speed|%", "motors.SynchedMotorPair.tankFor|block": "tank %motors|%speedLeft|%|%speedRight|%|for %value|%unit", diff --git a/libs/core/ns.ts b/libs/core/ns.ts index 8b137891..51ccea08 100644 --- a/libs/core/ns.ts +++ b/libs/core/ns.ts @@ -1 +1,14 @@ +namespace motors { + + /** + * A speed picker + * @param speed the speed + */ + //% blockId=motorSpeedPicker block="%speed" shim=TD_ID + //% speed.fieldEditor="speed" colorSecondary="#FFFFFF" + //% weight=0 blockHidden=1 + export function __speedPicker(speed: number): number { + return speed; + } +} \ No newline at end of file diff --git a/libs/core/output.ts b/libs/core/output.ts index 523a2dd3..3f8a4905 100644 --- a/libs/core/output.ts +++ b/libs/core/output.ts @@ -206,10 +206,9 @@ namespace motors { * Sets the speed of the motor. * @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50 */ - //% blockId=motorSetSpeed block="set %motor|speed to %speed|%" + //% blockId=motorSetSpeed block="set %motor|speed to %speed=motorSpeedPicker|%" //% on.fieldEditor=toggleonoff //% weight=99 blockGap=8 - //% speed.min=-100 speed.max=100 //% group="Motion" setSpeed(speed: number) { this.init(); @@ -226,9 +225,8 @@ namespace motors { * @param value the move quantity, eg: 2 * @param unit the meaning of the value */ - //% blockId=motorMove block="set %motor|speed to %speed|%|for %value|%unit" + //% blockId=motorMove block="set %motor|speed to %speed=motorSpeedPicker|%|for %value|%unit" //% weight=98 blockGap=8 - //% speed.min=-100 speed.max=100 //% group="Motion" setSpeedFor(speed: number, value: number, unit: MoveUnit) { this.init();