committed by
Sam El-Husseini
parent
6ea8a59f58
commit
5384ec567d
@ -2,36 +2,10 @@
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
import { deployCoreAsync, initAsync } from "./deploy";
|
||||
import { FieldPorts } from "./field_ports";
|
||||
import { FieldMotors } from "./field_motors";
|
||||
import { FieldSpeed } from "./field_speed";
|
||||
import { FieldBrickButtons } from "./field_brickbuttons";
|
||||
import { FieldTurnRatio } from "./field_turnratio";
|
||||
import { FieldColorEnum } from "./field_color";
|
||||
|
||||
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||
pxt.debug('loading pxt-ev3 target extensions...')
|
||||
updateBlocklyShape();
|
||||
const res: pxt.editor.ExtensionResult = {
|
||||
fieldEditors: [{
|
||||
selector: "ports",
|
||||
editor: FieldPorts
|
||||
}, {
|
||||
selector: "motors",
|
||||
editor: FieldMotors
|
||||
}, {
|
||||
selector: "speed",
|
||||
editor: FieldSpeed
|
||||
}, {
|
||||
selector: "brickbuttons",
|
||||
editor: FieldBrickButtons
|
||||
}, {
|
||||
selector: "turnratio",
|
||||
editor: FieldTurnRatio
|
||||
}, {
|
||||
selector: "colorenum",
|
||||
editor: FieldColorEnum
|
||||
}],
|
||||
deployCoreAsync,
|
||||
showUploadInstructionsAsync: (fn: string, url: string, confirmAsync: (options: any) => Promise<number>) => {
|
||||
let resolve: (thenableOrResult?: void | PromiseLike<void>) => void;
|
||||
@ -115,109 +89,6 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
||||
return Promise.resolve<pxt.editor.ExtensionResult>(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the shape of Blockly blocks with square corners
|
||||
*/
|
||||
function updateBlocklyShape() {
|
||||
|
||||
/**
|
||||
* Rounded corner radius.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||
|
||||
/**
|
||||
* Inner space between edge of statement input and notch.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from left to right.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
|
||||
'l 8,8 ' +
|
||||
'h 16 ' +
|
||||
'l 8,-8 '
|
||||
);
|
||||
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from right to left.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
|
||||
'l -8,8 ' +
|
||||
'h -16 ' +
|
||||
'l -8,-8 '
|
||||
);
|
||||
|
||||
/**
|
||||
* SVG start point for drawing the top-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
|
||||
'm 0,' + 0;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded top-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded top-right corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
|
||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded bottom-right corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
|
||||
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded bottom-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
|
||||
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
|
||||
|
||||
/**
|
||||
* SVG path for drawing the top-left corner of a statement input.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the bottom-left corner of a statement input.
|
||||
* Includes the rounded inside corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
|
||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
|
||||
|
||||
/**
|
||||
* Corner radius of the flyout background.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
(Blockly as any).Flyout.prototype.CORNER_RADIUS = 0;
|
||||
|
||||
/**
|
||||
* Margin around the edges of the blocks in the flyout.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
(Blockly as any).Flyout.prototype.MARGIN = 8;
|
||||
|
||||
}
|
||||
|
||||
// When require()d from node, bind the global pxt namespace
|
||||
// namespace pxt {
|
||||
// export const dummyExport = 1;
|
||||
|
@ -1,160 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldPortsOptions extends Blockly.FieldCustomDropdownOptions {
|
||||
columns?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export class FieldBrickButtons extends Blockly.FieldDropdown implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
// Width in pixels
|
||||
private width_: number;
|
||||
|
||||
// Columns in grid
|
||||
private columns_: number;
|
||||
|
||||
private savedPrimary_: string;
|
||||
|
||||
constructor(text: string, options: FieldPortsOptions, validator?: Function) {
|
||||
super(options.data);
|
||||
|
||||
this.columns_ = parseInt(options.columns) || 4;
|
||||
this.width_ = parseInt(options.width) || 150;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dropdown menu under the text.
|
||||
* @private
|
||||
*/
|
||||
public showEditor_() {
|
||||
// If there is an existing drop-down we own, this is a request to hide the drop-down.
|
||||
if (Blockly.DropDownDiv.hideIfOwner(this)) {
|
||||
return;
|
||||
}
|
||||
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
// Populate the drop-down with the icons for this field.
|
||||
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
|
||||
let contentDiv = document.createElement('div');
|
||||
// Accessibility properties
|
||||
contentDiv.setAttribute('role', 'menu');
|
||||
contentDiv.setAttribute('aria-haspopup', 'true');
|
||||
|
||||
const buttonsSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGGElement;
|
||||
pxsim.svg.hydrate(buttonsSVG, {
|
||||
viewBox: "0 0 256.68237 256.68237",
|
||||
width: this.width_,
|
||||
height: this.width_
|
||||
});
|
||||
contentDiv.appendChild(buttonsSVG);
|
||||
|
||||
const gWrapper = pxsim.svg.child(buttonsSVG, 'g', { 'transform': 'translate(-4.695057,58.29823)' });
|
||||
const gInnerWrapper = pxsim.svg.child(gWrapper, 'g', { 'transform': 'translate(3.9780427e-6,-32.677281)' });
|
||||
|
||||
const back = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#6a6a6a;stroke-width:3.91719985',
|
||||
d: 'M 106.30882,198.38022 C 84.431262,177.26258 50.453467,142.52878 50.453467,142.52878 v -7.12931 H 37.087971 a 32.381533,32.381533 0 1 1 0,-64.763062 H 50.457376 V 63.503186 L 105.71731,7.0563355 h 55.25604 c 25.02699,25.5048885 55.25994,55.2599395 55.25994,55.2599395 v 8.320133 h 12.77398 a 32.381533,32.381533 0 0 1 0,64.763062 h -12.77398 v 7.13323 c -29.43384,30.27603 -54.66454,55.85144 -54.66454,55.85144 z'
|
||||
})
|
||||
|
||||
const buttonLeft = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'm 36.492567,78.357208 h 40.69971 V 126.48393 H 36.492567 A 24.063359,24.063359 0 0 1 12.429199,102.42057 v 0 A 24.063359,24.063359 0 0 1 36.492567,78.357208 Z'
|
||||
})
|
||||
|
||||
const buttonRight = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'M 229.00727,126.48784 H 188.30756 V 78.361126 h 40.69971 a 24.063359,24.063359 0 0 1 24.06335,24.063354 v 0 a 24.063359,24.063359 0 0 1 -24.06335,24.06336 z'
|
||||
})
|
||||
|
||||
const buttonEnter = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#3c3c3c;stroke-width:3.91719985',
|
||||
d: 'm 109.27806,78.357208 h 46.9398 a 1.782326,1.782326 0 0 1 1.78233,1.782326 V 124.7016 a 1.782326,1.782326 0 0 1 -1.78233,1.78233 h -46.9398 a 1.782326,1.782326 0 0 1 -1.78233,-1.78233 V 80.139534 a 1.782326,1.782326 0 0 1 1.78233,-1.782326 z'
|
||||
})
|
||||
|
||||
const buttonTop = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'm 108.09114,15.967966 49.90905,-0.59542 37.43276,38.619675 -15.44943,15.449437 V 97.367379 H 165.7249 V 81.306861 A 11.978797,11.978797 0 0 0 153.84012,69.422075 c -11.59883,-0.184102 -43.37516,0 -43.37516,0 A 9.6676495,9.6676495 0 0 0 100.36251,79.520618 V 97.347793 H 86.103905 V 69.422075 L 70.654464,53.97264 Z'
|
||||
})
|
||||
|
||||
const buttonBottom = pxsim.svg.child(gInnerWrapper, 'path', {
|
||||
style: 'fill:#a8a9a8;stroke-width:3.91719985',
|
||||
d: 'M 157.78865,189.01028 108.18908,189.38233 70.654464,150.794 86.323259,135.4895 v -28.08625 h 14.101921 v 16.11144 a 12.006218,12.006218 0 0 0 11.85346,11.9788 c 11.59882,0.1841 43.13227,0 43.13227,0 a 10.18472,10.18472 0 0 0 10.38059,-10.38058 v -17.70966 h 14.39179 v 28.08632 l 15.3045,15.3045 z'
|
||||
})
|
||||
|
||||
const buttons = [buttonEnter, buttonLeft, buttonRight, buttonTop, buttonBottom];
|
||||
const options = this.getOptions();
|
||||
for (let i = 0, option: any; option = options[i]; i++) {
|
||||
let content = (options[i] as any)[0]; // Human-readable text or image.
|
||||
const value = (options[i] as any)[1]; // Language-neutral value.
|
||||
const button = buttons[i];
|
||||
button.setAttribute('id', ':' + i); // For aria-activedescendant
|
||||
button.setAttribute('role', 'menuitem');
|
||||
button.setAttribute('cursor', 'pointer');
|
||||
const title = pxsim.svg.child(button, 'title');
|
||||
title.textContent = content;
|
||||
|
||||
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
|
||||
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
|
||||
// These are applied manually instead of using the :hover pseudoclass
|
||||
// because Android has a bad long press "helper" menu and green highlight
|
||||
// that we must prevent with ontouchstart preventDefault
|
||||
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
|
||||
this.setAttribute('stroke', '#ffffff');
|
||||
e.preventDefault();
|
||||
});
|
||||
Blockly.bindEvent_(button, 'mouseover', button, function () {
|
||||
this.setAttribute('stroke', '#ffffff');
|
||||
});
|
||||
Blockly.bindEvent_(button, 'mouseout', button, function () {
|
||||
this.setAttribute('stroke', 'transparent');
|
||||
});
|
||||
|
||||
button.setAttribute('data-value', value);
|
||||
}
|
||||
|
||||
contentDiv.style.width = this.width_ + 'px';
|
||||
dropdownDiv.appendChild(contentDiv);
|
||||
|
||||
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
|
||||
|
||||
// Calculate positioning based on the field position.
|
||||
var scale = this.sourceBlock_.workspace.scale;
|
||||
var bBox = { width: this.size_.width, height: this.size_.height };
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = this.fieldGroup_.getBoundingClientRect();
|
||||
var primaryX = position.left + bBox.width / 2;
|
||||
var primaryY = position.top + bBox.height;
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.top;
|
||||
// Set bounds to workspace; show the drop-down.
|
||||
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
|
||||
this.onHide_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a button is clicked inside the drop-down.
|
||||
* Should be bound to the FieldIconMenu.
|
||||
* @param {Event} e DOM event for the click/touch
|
||||
* @private
|
||||
*/
|
||||
private buttonClick_ = function (e: any) {
|
||||
let value = e.target.getAttribute('data-value');
|
||||
this.setValue(value);
|
||||
Blockly.DropDownDiv.hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for when the drop-down is hidden.
|
||||
*/
|
||||
private onHide_ = function () {
|
||||
Blockly.DropDownDiv.content_.removeAttribute('role');
|
||||
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
|
||||
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
|
||||
Blockly.DropDownDiv.getContentDiv().style.width = '';
|
||||
};
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldColorEnumOptions extends pxtblockly.FieldColourNumberOptions {
|
||||
}
|
||||
|
||||
export class FieldColorEnum extends pxtblockly.FieldColorNumber implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
constructor(text: string, params: FieldColorEnumOptions, opt_validator?: Function) {
|
||||
super(text, params, opt_validator);
|
||||
}
|
||||
|
||||
mapColour(enumString: string) {
|
||||
switch(enumString) {
|
||||
case '#000000': return 'ColorSensorColor.Black';
|
||||
case '#006db3': return 'ColorSensorColor.Blue';
|
||||
case '#00934b': return 'ColorSensorColor.Green';
|
||||
case '#ffd01b': return 'ColorSensorColor.Yellow';
|
||||
case '#f12a21': return 'ColorSensorColor.Red';
|
||||
case '#ffffff': return 'ColorSensorColor.White';
|
||||
case '#6c2d00': return 'ColorSensorColor.Brown';
|
||||
default: return 'ColorSensorColor.None';
|
||||
}
|
||||
}
|
||||
|
||||
mapEnum(colorString: string) {
|
||||
switch(colorString) {
|
||||
case 'ColorSensorColor.Black': return '#000000';
|
||||
case 'ColorSensorColor.Blue': return '#006db3';
|
||||
case 'ColorSensorColor.Green': return '#00934b';
|
||||
case 'ColorSensorColor.Yellow': return '#ffd01b';
|
||||
case 'ColorSensorColor.Red': return '#f12a21';
|
||||
case 'ColorSensorColor.White': return '#ffffff';
|
||||
case 'ColorSensorColor.Brown': return '#6c2d00';
|
||||
case 'ColorSensorColor.None': return '#dfe6e9'; // Grey
|
||||
default: return colorString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current colour.
|
||||
* @param {boolean} opt_asHex optional field if the returned value should be a hex
|
||||
* @return {string} Current colour in '#rrggbb' format.
|
||||
*/
|
||||
getValue(opt_asHex?: boolean) {
|
||||
var colour = this.mapColour(this.colour_);
|
||||
if (!opt_asHex && colour.indexOf('#') > -1) {
|
||||
return `0x${colour.replace(/^#/, '')}`;
|
||||
}
|
||||
return colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colour.
|
||||
* @param {string} colour The new colour in '#rrggbb' format.
|
||||
*/
|
||||
setValue(colorStr: string) {
|
||||
var colour = this.mapEnum(colorStr);
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
|
||||
this.colour_ != colour) {
|
||||
Blockly.Events.fire(new (Blockly as any).Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.colour_, colour));
|
||||
}
|
||||
this.colour_ = colour;
|
||||
if (this.sourceBlock_) {
|
||||
this.sourceBlock_.setColour(colour, colour, colour);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,32 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldPortsOptions extends pxtblockly.FieldImagesOptions {
|
||||
columns?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export class FieldPorts extends pxtblockly.FieldImages implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
constructor(text: string, options: FieldPortsOptions, validator?: Function) {
|
||||
super(text, { sort: true, data: options.data }, validator);
|
||||
|
||||
this.columns_ = parseInt(options.columns) || 4;
|
||||
this.width_ = parseInt(options.width) || 300;
|
||||
|
||||
this.setText = Blockly.FieldDropdown.prototype.setText;
|
||||
this.updateWidth = (Blockly.Field as any).prototype.updateWidth;
|
||||
this.updateTextNode_ = Blockly.Field.prototype.updateTextNode_;
|
||||
}
|
||||
|
||||
trimOptions_() {
|
||||
}
|
||||
|
||||
protected buttonClick_ = function (e: any) {
|
||||
let value = e.target.getAttribute('data-value');
|
||||
this.setValue(value);
|
||||
Blockly.DropDownDiv.hide();
|
||||
};
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
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",
|
||||
width: "170"
|
||||
});
|
||||
|
||||
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', 'dominant-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) {
|
||||
let sign = this.sign(speed);
|
||||
speed = (Math.abs(speed) / 100 * 50) + 50;
|
||||
if (sign == -1) speed = 50 - speed;
|
||||
let c = Math.PI * (90 * 2);
|
||||
let pct = ((100 - speed) / 100) * c;
|
||||
this.circleBar.setAttribute('stroke-dashoffset', `${pct}`);
|
||||
}
|
||||
|
||||
// A re-implementation of Math.sign (since IE11 doesn't support it)
|
||||
private sign(num: number) {
|
||||
return num ? num < 0 ? -1 : 1 : 0;
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
export interface FieldTurnRatioOptions extends Blockly.FieldCustomOptions {
|
||||
}
|
||||
|
||||
export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.FieldCustom {
|
||||
public isFieldCustom_ = true;
|
||||
|
||||
private params: any;
|
||||
|
||||
private path_: SVGPathElement;
|
||||
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: FieldTurnRatioOptions, opt_validator?: Function) {
|
||||
super(String(value_), '-100', '100', null, '10', 'TurnRatio', opt_validator);
|
||||
this.params = params;
|
||||
(this as any).sliderColor_ = '#a8aaa8';
|
||||
}
|
||||
|
||||
static HALF = 80;
|
||||
static HANDLE_RADIUS = 30;
|
||||
static RADIUS = FieldTurnRatio.HALF - FieldTurnRatio.HANDLE_RADIUS - 1;
|
||||
|
||||
createLabelDom_(labelText: string) {
|
||||
let labelContainer = document.createElement('div');
|
||||
let svg = Blockly.utils.createSvgElement('svg', {
|
||||
'xmlns': 'http://www.w3.org/2000/svg',
|
||||
'xmlns:html': 'http://www.w3.org/1999/xhtml',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
'version': '1.1',
|
||||
'height': (FieldTurnRatio.HALF + FieldTurnRatio.HANDLE_RADIUS + 10) + 'px',
|
||||
'width': (FieldTurnRatio.HALF * 2) + 'px'
|
||||
}, labelContainer);
|
||||
let defs = Blockly.utils.createSvgElement('defs', {}, svg);
|
||||
let marker = Blockly.utils.createSvgElement('marker', {
|
||||
'id': 'head',
|
||||
'orient': "auto",
|
||||
'markerWidth': '2',
|
||||
'markerHeight': '4',
|
||||
'refX': '0.1', 'refY': '1.5'
|
||||
}, defs);
|
||||
let markerPath = Blockly.utils.createSvgElement('path', {
|
||||
'd': 'M0,0 V3 L1.5,1.5 Z',
|
||||
'fill': '#f12a21'
|
||||
}, marker);
|
||||
this.reporter_ = pxsim.svg.child(svg, "text", {
|
||||
'x': FieldTurnRatio.HALF, 'y': 96,
|
||||
'text-anchor': 'middle', 'dominant-baseline': 'middle',
|
||||
'style': 'font-size: 50px',
|
||||
'class': 'sim-text inverted number'
|
||||
}) as SVGTextElement;
|
||||
this.path_ = Blockly.utils.createSvgElement('path', {
|
||||
'x1': FieldTurnRatio.HALF,
|
||||
'y1': FieldTurnRatio.HALF,
|
||||
'marker-end': 'url(#head)',
|
||||
'style': 'fill: none; stroke: #f12a21; stroke-width: 10'
|
||||
}, svg);
|
||||
this.updateGraph_();
|
||||
let readout = document.createElement('span');
|
||||
readout.setAttribute('class', 'blocklyFieldSliderReadout');
|
||||
return [labelContainer, readout];
|
||||
};
|
||||
|
||||
updateGraph_() {
|
||||
if (!this.path_) {
|
||||
return;
|
||||
}
|
||||
let v = goog.math.clamp(parseFloat(this.getText()), -100, 100);
|
||||
if (isNaN(v)) {
|
||||
v = 0;
|
||||
}
|
||||
|
||||
const x = goog.math.clamp(parseFloat(this.getText()), -100, 100) / 100;
|
||||
const theta = x * Math.PI / 2;
|
||||
const cx = FieldTurnRatio.HALF;
|
||||
const cy = FieldTurnRatio.HALF - 14;
|
||||
const gamma = Math.PI - 2 * theta;
|
||||
const r = FieldTurnRatio.RADIUS;
|
||||
const alpha = 0.2 + Math.abs(x) * 0.5;
|
||||
const x1 = 0;
|
||||
const y1 = r * alpha;
|
||||
const y2 = r * Math.sin(Math.PI / 2 - theta);
|
||||
const x2 = r * Math.cos(Math.PI / 2 - theta);
|
||||
const y3 = y2 - r * alpha * Math.cos(2 * theta);
|
||||
const x3 = x2 - r * alpha * Math.sin(2 * theta);
|
||||
|
||||
|
||||
const d = `M ${cx} ${cy} C ${cx} ${cy - y1} ${cx + x3} ${cy - y3} ${cx + x2} ${cy - y2}`;
|
||||
this.path_.setAttribute('d', d);
|
||||
|
||||
this.reporter_.textContent = `${v}`;
|
||||
}
|
||||
|
||||
setReadout_(readout: Element, value: string) {
|
||||
this.updateGraph_();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user