Compare commits

...

36 Commits

Author SHA1 Message Date
83793fc668 strings 2018-01-09 12:39:56 -08:00
47ee87fe72 adding logging 2018-01-09 09:03:22 -08:00
374fa36548 cholesky 2018-01-08 23:15:43 -08:00
358ef7e19f started on matrix library 2018-01-08 23:05:09 -08:00
9cbf5efd7e 0.0.57 2018-01-08 22:01:11 -08:00
a27a7fcd55 removing measured overloads until blocks support (#215) 2018-01-08 15:27:37 -08:00
85263fb84d Merge branch 'master' of https://github.com/Microsoft/pxt-ev3 2018-01-08 15:13:20 -08:00
b5ad898c9e Screentweak (#214)
* adding pid

* hiding functions

* precision of value
2018-01-08 15:00:00 -08:00
456df3c442 Merge branch 'master' of https://github.com/microsoft/pxt-ev3 2018-01-08 14:43:19 -08:00
1552eb05b4 matter most info 2018-01-08 14:43:15 -08:00
d60e2c4a7d adding pid 2018-01-08 14:36:34 -08:00
f4b78c3ee7 PID example 2018-01-08 14:06:39 -08:00
dd5e1957d5 fix coding page 2018-01-08 14:06:29 -08:00
d61a63f70a avoid overlaps in port view 2018-01-08 09:08:59 -08:00
4207bd06c0 removing microbit font 2018-01-08 08:53:03 -08:00
58763e398b 0.0.56 2018-01-07 23:39:04 -08:00
f77bf165eb updates strings 2018-01-07 20:51:48 -08:00
4880d9ea5b Merge branch 'master' of https://github.com/microsoft/pxt-ev3 2018-01-07 20:45:56 -08:00
ab4fb019f9 more changes to "print" 2018-01-07 20:45:48 -08:00
2c2df31ba3 Merge pull request #205 from Microsoft/motorshuffle
shuffling motor blocks
2018-01-07 20:34:20 -08:00
0f2bda2496 shuffling motor blocks 2018-01-07 20:33:02 -08:00
44386be3c7 Merge pull request #185 from Microsoft/printlineinverted
invert arguments in print line
2018-01-07 12:14:16 -08:00
89ca66b8a9 Merge pull request #201 from Microsoft/speedfield
Add a speed field editor
2018-01-07 12:11:40 -08:00
64389a7689 more annotations 2018-01-07 10:06:02 -08:00
f77778ef85 Merge branch 'master' into speedfield 2018-01-07 09:59:11 -08:00
7d01823caf remove stopAllMotors on escape key 2018-01-07 09:58:08 -08:00
b3f9a4c92f Make sure we stop motors on exit (#199) 2018-01-07 07:54:40 -08:00
7fe8580de8 Add brick buttons field editor (#202)
* Add brick buttons field editor

* add hover title text
2018-01-07 07:52:07 -08:00
c70d6fe01a Remove unnecessary extra 'light' text in block pauseUntilLight (#203) 2018-01-07 00:12:29 -08:00
ed7099cc97 more square shaped corners 2018-01-06 21:00:39 -08:00
a79704fecc Decompilation and minor PR feedback 2018-01-06 17:16:08 -08:00
6a1b560101 Fix default 2018-01-06 14:40:09 -08:00
5e21f9ab6d Add a speed field editor 2018-01-06 14:25:50 -08:00
74648bd1df Merge pull request #198 from Microsoft/moresquarecorners
More square shaped corners on blocks
2018-01-06 12:55:51 -08:00
e24d4d56b1 More square shaped corners on blocks (toggle rect and arrow corner) 2018-01-06 00:12:32 -08:00
ac428a3936 invert arguments in print line 2018-01-04 22:13:17 -08:00
27 changed files with 680 additions and 162 deletions

View File

@ -4,7 +4,8 @@
This repo contains the editor target hosted at https://d541eec2-1e96-4b7b-a223-da9d01d0337a.pxt.io/
Issue tracker: https://src.education.lego.com/groups/ev3-makecode
LEGO Auth: https://src.education.lego.com/groups/ev3-makecode (use Google Authenticator)
LEGO Chat: https://chat.internal.education.lego.com/make-code/channels/town-square
## Local Dev setup

View File

@ -167,4 +167,5 @@
"description": "Activity 2",
"url":"/coding/roaming-2",
"cardType": "example"
}, {
}]
```

View File

@ -0,0 +1,50 @@
# Gradien follower PID + calibration
```blocks
let lasterror = 0
let D = 0
let I = 0
let P = 0
let error = 0
let setpoint = 0
let max = 0
let min = 0
let v = 0
v = sensors.color3.light(LightIntensityMode.Reflected)
min = v
max = v
setpoint = v
while (!(brick.buttonEnter.wasPressed())) {
brick.clearScreen()
brick.showString("Move robot on terrain", 1)
brick.showString("Press ENTER when done", 2)
v = sensors.color3.light(LightIntensityMode.Reflected)
min = Math.min(min, v)
max = Math.max(max, v)
setpoint = (max + min) / 2
brick.showValue("v", v, 3)
brick.showValue("min", min, 4)
brick.showValue("max", v, 5)
brick.showValue("setpoint", setpoint, 6)
loops.pause(100)
}
loops.forever(function () {
brick.clearScreen()
v = sensors.color3.light(LightIntensityMode.Reflected)
brick.showValue("light", v, 1)
error = v - setpoint
brick.showValue("error", error, 2)
P = error * 5
brick.showValue("P", P, 3)
I = I + error * 0.01
brick.showValue("I", I, 4)
D = (error - lasterror) * 0.2
brick.showValue("D", D, 5)
motors.largeBC.steer(P + (I + D), 100)
lasterror = error
if (brick.buttonEnter.wasPressed()) {
motors.largeBC.setSpeed(0)
brick.buttonDown.pauseUntil(ButtonEvent.Click)
}
})
```

View File

@ -4,6 +4,8 @@
import { deployCoreAsync, initAsync } from "./deploy";
import { FieldPorts } from "./field_ports";
import { FieldImages } from "./field_images";
import {FieldSpeed} from "./field_speed";
import { FieldBrickButtons } from "./field_brickbuttons";
pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
@ -15,6 +17,12 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
}, {
selector: "images",
editor: FieldImages
}, {
selector: "speed",
editor: FieldSpeed
}, {
selector: "brickbuttons",
editor: FieldBrickButtons
}],
deployCoreAsync
};

View File

@ -0,0 +1,159 @@
/// <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');
};
}

96
editor/field_speed.ts Normal file
View File

@ -0,0 +1,96 @@
/// <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"
});
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) {
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;
}
}

View File

@ -1,7 +1,7 @@
{
"behaviors.addBehavior|block": "add behavior %behavior",
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
"behaviors.driveForward|block": "drive %motors|forward at %speed|%",
"behaviors.driveForward|block": "drive %motors|forward at %speed=motorSpeedPicker|%",
"behaviors|block": "behaviors",
"{id:category}Behaviors": "Behaviors"
}

View File

@ -49,7 +49,7 @@ namespace behaviors {
* @param motors
* @param speed the desired speed, eg: 50
*/
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed|%"
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed=motorSpeedPicker|%"
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
return new DriveForwardBehavior(motors, speed);
}

View File

@ -18,7 +18,7 @@
"sensors.ColorSensor.onColorDetected|block": "on %sensor|detected color %color",
"sensors.ColorSensor.onLightChanged|block": "on %sensor|%mode|%condition",
"sensors.ColorSensor.pauseForColor|block": "pause %sensor|for color %color",
"sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|light %condition",
"sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|%condition",
"sensors.color1|block": "color 1",
"sensors.color2|block": "color 2",
"sensors.color3|block": "color 3",

View File

@ -182,7 +182,7 @@ namespace sensors {
* @param color the color to detect
*/
//% help=sensors/color-sensor/pause-for-light
//% block="pause %sensor|for %mode|light %condition"
//% block="pause %sensor|for %mode|%condition"
//% blockId=colorPauseForLight
//% parts="colorsensor"
//% blockNamespace=sensors

View File

@ -30,14 +30,20 @@
"brick.clearScreen": "Clears the screen",
"brick.lightPattern": "Pattern block.",
"brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
"brick.printLine": "Show text on the screen at a specific line.",
"brick.printLine|param|line": "the line number to print the text at, eg: 0",
"brick.printLine|param|text": "the text to print on the screen, eg: \"Hello world\"",
"brick.printPorts": "Prints the port states on the screen",
"brick.setLight": "Set lights.",
"brick.setLight|param|pattern": "the lights pattern to use.",
"brick.showImage": "Shows an image on screen",
"brick.showImage|param|image": "image to draw",
"brick.showNumber": "Shows a number on the screen",
"brick.showNumber|param|line": "the line number to print the text at, eg: 1",
"brick.showNumber|param|value": "the numeric value",
"brick.showString": "Show text on the screen at a specific line.",
"brick.showString|param|line": "the line number to print the text at, eg: 1",
"brick.showString|param|text": "the text to print on the screen, eg: \"Hello world\"",
"brick.showValue": "Shows a name, value pair on the screen",
"brick.showValue|param|line": "the line number to print the text at, eg: 1",
"brick.showValue|param|value": "the numeric value",
"console": "Reading and writing data to the console output.\n\nReading and writing data to the console output.",
"console.addListener": "Adds a listener for the log messages",
"console.log": "Write a line of text to the console output.",
@ -65,29 +71,21 @@
"motors.MotorBase.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.MotorBase.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.MotorBase.setReversed": "Reverses the motor polarity",
"motors.MotorBase.setSpeed": "Sets the speed of the motor.",
"motors.MotorBase.setSpeedFor": "Sets the motor speed for limited time or distance",
"motors.MotorBase.setSpeedFor|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.setSpeedFor|param|unit": "the meaning of the value",
"motors.MotorBase.setSpeedFor|param|value": "the move quantity, eg: 2",
"motors.MotorBase.setSpeed": "Sets the motor speed for limited time or distance",
"motors.MotorBase.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.setSpeed|param|unit": "(optional) unit of the value",
"motors.MotorBase.setSpeed|param|value": "(optional) measured distance or rotation",
"motors.MotorBase.stop": "Stops the motor(s).",
"motors.SynchedMotorPair.steer": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.steerFor": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.steerFor|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.steerFor|param|turnRatio": "the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0",
"motors.SynchedMotorPair.steerFor|param|unit": "the meaning of the value",
"motors.SynchedMotorPair.steerFor|param|value": "the move quantity, eg: 2",
"motors.SynchedMotorPair.steer|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.steer|param|turnRatio": "the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0",
"motors.SynchedMotorPair.steer|param|unit": "(optional) unit of the value",
"motors.SynchedMotorPair.steer|param|value": "(optional) move duration or rotation",
"motors.SynchedMotorPair.tank": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.tankFor": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.tankFor|param|speedLeft": "the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tankFor|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tankFor|param|unit": "the unit of the value",
"motors.SynchedMotorPair.tankFor|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.tank|param|speedLeft": "the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tank|param|unit": "(optional) unit of the value",
"motors.SynchedMotorPair.tank|param|value": "(optional) move duration or rotation",
"motors.SynchedMotorPair.toString": "Returns the name(s) of the motor",
"motors.mkCmd": "Allocates a message buffer",
"motors.mkCmd|param|addSize": "required additional bytes",

View File

@ -37,10 +37,12 @@
"brick.buttonUp|block": "up",
"brick.clearScreen|block": "clear screen",
"brick.lightPattern|block": "%pattern",
"brick.printLine|block": "print %text| at line %line",
"brick.printPorts|block": "print ports",
"brick.setLight|block": "set light to %pattern=led_pattern",
"brick.showImage|block": "show image %image=screen_image_picker",
"brick.showNumber|block": "show number %name|at line %line",
"brick.showString|block": "show string %text|at line %line",
"brick.showValue|block": "show value %name|= %text|at line %line",
"brick|block": "brick",
"console.logValue|block": "console|log value %name|= %value",
"console.log|block": "console|log %text",
@ -55,12 +57,9 @@
"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.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",
"motors.SynchedMotorPair.tank|block": "tank %motors|%speedLeft|%|%speedRight|%",
"motors.MotorBase.setSpeed|block": "set %motor|speed to %speed=motorSpeedPicker|%",
"motors.SynchedMotorPair.steer|block": "steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%",
"motors.SynchedMotorPair.tank|block": "tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%",
"motors.largeAB|block": "large A+B",
"motors.largeAD|block": "large A+D",
"motors.largeA|block": "large A",
@ -93,8 +92,7 @@
"{id:group}Counters": "Counters",
"{id:group}Light": "Light",
"{id:group}More": "More",
"{id:group}Motion": "Motion",
"{id:group}Move": "Move",
"{id:group}Screen": "Screen",
"{id:group}Sensors": "Sensors",
"{id:group}Sync Motion": "Sync Motion"
"{id:group}Sensors": "Sensors"
}

View File

@ -92,6 +92,7 @@ namespace brick {
//% blockNamespace=brick
//% weight=81 blockGap=8
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
isPressed() {
return this._isPressed
}
@ -107,6 +108,7 @@ namespace brick {
//% blockNamespace=brick
//% weight=80
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
wasPressed() {
const r = this._wasPressed
this._wasPressed = false
@ -125,6 +127,7 @@ namespace brick {
//% blockNamespace=brick
//% weight=99 blockGap=8
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
onEvent(ev: ButtonEvent, body: () => void) {
control.onEvent(this._id, ev, body)
}
@ -139,6 +142,7 @@ namespace brick {
//% blockNamespace=brick
//% weight=98 blockGap=8
//% group="Buttons"
//% button.fieldEditor="brickbuttons"
pauseUntil(ev: ButtonEvent) {
control.waitForEvent(this._id, ev);
}
@ -166,7 +170,6 @@ namespace brick {
// this needs to be done in query(), which is run without the main JS execution mutex
// otherwise, while(true){} will lock the device
if (ret & DAL.BUTTON_ID_ESCAPE) {
motors.stopAllMotors();
control.reset()
}
return ret

View File

@ -483,7 +483,15 @@ void runLMS() {
*/
}
void stopMotors() {
uint8_t cmd[2] = { 0xA3, 0x0F };
int fd = open("/dev/lms_pwm", O_RDWR);
write(fd, cmd, 2);
close(fd);
}
extern "C" void target_reset() {
stopMotors();
if (lmsPid)
runLMS();
else

View File

@ -1 +1,14 @@
namespace motors {
/**
* A speed picker
* @param speed the speed, eg: 50
*/
//% blockId=motorSpeedPicker block="%speed" shim=TD_ID
//% speed.fieldEditor="speed" colorSecondary="#FFFFFF"
//% weight=0 blockHidden=1 speed.fieldOptions.decompileLiterals=1
export function __speedPicker(speed: number): number {
return speed;
}
}

View File

@ -96,7 +96,7 @@ namespace motors {
return b
}
function outputToName(out: Output): string {
export function outputToName(out: Output): string {
let r = "";
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i)) {
@ -112,7 +112,7 @@ namespace motors {
*/
//% blockId=motorStopAll block="stop all motors"
//% weight=5
//% group="Motion"
//% group="Move"
export function stopAllMotors() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
writePWM(b)
@ -121,7 +121,7 @@ namespace motors {
/**
* Resets all motors
*/
//% group="Motion"
//% group="Move"
export function resetAllMotors() {
reset(Output.ALL)
}
@ -164,7 +164,7 @@ namespace motors {
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
//% brake.fieldEditor=toggleonoff
//% weight=60 blockGap=8
//% group="Motion"
//% group="Move"
setBrake(brake: boolean) {
this.init();
this._brake = brake;
@ -176,7 +176,7 @@ namespace motors {
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
//% reversed.fieldEditor=toggleonoff
//% weight=59
//% group="Motion"
//% group="Move"
setReversed(reversed: boolean) {
this.init();
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
@ -202,35 +202,16 @@ namespace motors {
reset(this._port);
}
/**
* 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|%"
//% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8
//% speed.min=-100 speed.max=100
//% group="Motion"
setSpeed(speed: number) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) // always stop
this.stop();
else
this._setSpeed(speed);
}
/**
* Sets the motor speed for limited time or distance
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param value (optional) measured distance or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorMove block="set %motor|speed to %speed|%|for %value|%unit"
//% weight=98 blockGap=8
//% speed.min=-100 speed.max=100
//% group="Motion"
setSpeedFor(speed: number, value: number, unit: MoveUnit) {
//% blockId=motorSetSpeed block="set %motor|speed to %speed=motorSpeedPicker|%"
//% weight=100 blockGap=8
//% group="Move"
setSpeed(speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
@ -278,8 +259,8 @@ namespace motors {
* @param timeOut optional maximum pausing time in milliseconds
*/
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
//% weight=97
//% group="Motion"
//% weight=90
//% group="Move"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
}
@ -457,7 +438,6 @@ namespace motors {
});
}
/**
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
* Use the Move Tank block for robot vehicles that have two Large Motors,
@ -466,32 +446,14 @@ namespace motors {
* to make your robot turn.
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
* @param value (optional) move duration or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorPairTank block="tank %motors|%speedLeft|%|%speedRight|%"
//% weight=20 blockGap=8
//% group="Sync Motion"
tank(speedLeft: number, speedRight: number) {
this.tankFor(speedLeft, speedRight, 0, MoveUnit.Degrees);
}
/**
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
* Use the Move Tank block for robot vehicles that have two Large Motors,
* with one motor driving the left side of the vehicle and the other the right side.
* You can make the two motors go at different speeds or in different directions
* to make your robot turn.
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
* @param value the amount of movement, eg: 2
* @param unit the unit of the value
*/
//% blockId=motorPairTankFor block="tank %motors|%speedLeft|%|%speedRight|%|for %value|%unit"
//% weight=19
//% speedLeft.min=-100 speedLeft=100
//% speedRight.min=-100 speedRight=100
//% blockId=motorPairTank block="tank %motors|%speedLeft=motorSpeedPicker|%|%speedRight=motorSpeedPicker|%"
//% weight=96 blockGap=8
//% inlineInputMode=inline
//% group="Sync Motion"
tankFor(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) {
//% group="Move"
tank(speedLeft: number, speedRight: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speedLeft = Math.clamp(-100, 100, speedLeft >> 0);
@ -502,39 +464,22 @@ namespace motors {
? (100 - speedRight / speedLeft * 100)
: (speedLeft / speedRight * 100 - 100);
this.steerFor(turnRatio, speed, value, unit);
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
*/
//% blockId=motorPairSteer block="steer %chassis|turn ratio %turnRatio|speed %speed|%"
//% weight=7 blockGap=8
//% turnRatio.min=-200 turnRatio=200
//% inlineInputMode=inline
//% group="Sync Motion"
steer(turnRatio: number, speed: number) {
this.steer(turnRatio, speed);
this.steer(turnRatio, speed, value, unit);
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param value (optional) move duration or rotation
* @param unit (optional) unit of the value
*/
//% blockId=motorPairSteerFor block="steer %chassis|turn ratio %turnRatio|speed %speed|%|for %value|%unit"
//% weight=6 blockGap=8
//% blockId=motorPairSteer block="steer %chassis|turn ratio %turnRatio|speed %speed=motorSpeedPicker|%"
//% weight=95
//% turnRatio.min=-200 turnRatio=200
//% inlineInputMode=inline
//% group="Sync Motion"
steerFor(turnRatio: number, speed: number, value: number, unit: MoveUnit) {
//% group="Move"
steer(turnRatio: number, speed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {

View File

@ -58,29 +58,7 @@ namespace brick {
}
}
export function microbitFont() {
return {
charWidth: 6,
charHeight: 5,
firstChar: 32,
// source https://github.com/lancaster-university/microbit-dal/blob/master/source/core/MicroBitFont.cpp
data: hex`
0000000000 0202020002 0a0a000000 0a1f0a1f0a 0e130e190e 1309041219 0609060916 0202000000 0402020204
0204040402 000a040a00 00040e0400 0000000402 00000e0000 0000000200 1008040201 0609090906 040604040e
070806010f 0f08040906 0c0a091f08 1f010f100f 08040e110e 1f08040201 0e110e110e 0e110e0402 0002000200
0004000402 0804020408 000e000e00 0204080402 0e110c0004 0e11151906 06090f0909 0709070907 0e0101010e
0709090907 0f0107010f 0f01070101 0e0119110e 09090f0909 0702020207 1f08080906 0905030509 010101010f
111b151111 1113151911 0609090906 0709070101 060909060c 0709070911 0e01060807 1f04040404 0909090906
1111110a04 1111151b11 0909060909 110a040404 0f0402010f 0e0202020e 0102040810 0e0808080e 040a000000
000000001f 0204000000 000e09091e 0101070907 000e01010e 08080e090e 060907010e 0c02070202 0e090e0806
0101070909 0200020202 0800080806 0105030509 020202020c 001b151111 0007090909 0006090906 0007090701
000e090e08 000e010101 000c020403 02020e021c 000909091e 0011110a04 001111151b 0009060609 00110a0403
000f04020f 0c0406040c 0202020202 0302060203 0000061800
`
}
}
export function setPixel(on: boolean, x: number, y: number) {
function setPixel(on: boolean, x: number, y: number) {
x |= 0
y |= 0
if (0 <= x && x < DAL.LCD_WIDTH && 0 <= y && y < DAL.LCD_HEIGHT)
@ -90,18 +68,43 @@ namespace brick {
/**
* Show text on the screen at a specific line.
* @param text the text to print on the screen, eg: "Hello world"
* @param line the line number to print the text at, eg: 0
* @param line the line number to print the text at, eg: 1
*/
//% blockId=screen_print block="print %text| at line %line"
//% blockId=screen_print block="show string %text|at line %line"
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
//% line.min=0 line.max=9
export function printLine(text: string, line: number) {
//% line.min=1 line.max=10
export function showString(text: string, line: number) {
const NUM_LINES = 9;
const offset = 5;
const y = offset + (Math.clamp(0, NUM_LINES, line) / (NUM_LINES + 2)) * DAL.LCD_HEIGHT;
const y = offset + (Math.clamp(0, NUM_LINES, line - 1) / (NUM_LINES + 2)) * DAL.LCD_HEIGHT;
brick.print(text, offset, y);
}
/**
* Shows a number on the screen
* @param value the numeric value
* @param line the line number to print the text at, eg: 1
*/
//% blockId=screenShowNumber block="show number %name|at line %line"
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
//% line.min=1 line.max=10
export function showNumber(value: number, line: number) {
showString("" + value, line);
}
/**
* Shows a name, value pair on the screen
* @param value the numeric value
* @param line the line number to print the text at, eg: 1
*/
//% blockId=screenShowValue block="show value %name|= %text|at line %line"
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
//% line.min=1 line.max=10
export function showValue(name: string, value: number, line: number) {
value = Math.round(value * 1000) / 1000;
showString((name ? name + ": " : "") + value, line);
}
export function print(text: string, x: number, y: number, mode = Draw.Normal) {
x |= 0
y |= 0
@ -138,12 +141,9 @@ namespace brick {
*/
//% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=100 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) {
export function showImage(image: Image) {
if (!image) return;
image.draw(0, 0, Draw.Normal);
delay = Math.max(0, delay);
if (delay > 0)
loops.pause(delay);
}
/**
@ -168,7 +168,7 @@ namespace brick {
screen.clear();
}
export function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) {
function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) {
x |= 0;
y |= 0;
w |= 0;
@ -213,30 +213,32 @@ namespace brick {
//% blockId=brickPrintPorts block="print ports"
//% weight=1 group="Screen"
export function printPorts() {
const col = 44;
clearScreen();
function scale(x: number) {
if (Math.abs(x) > 1000) return Math.round(x / 100) / 10 + "k";
return ("" + (x >> 0));
}
// motors
const datas = motors.getAllMotorData();
for(let i = 0; i < datas.length; ++i) {
const data = datas[i];
if (!data.actualSpeed && !data.count) continue;
const x = i * 52;
print(`${data.actualSpeed}%`, x, brick.LINE_HEIGHT)
print(`${data.count}>`, x, 2 * brick.LINE_HEIGHT)
console.logValue(`speed.` + "ABCD"[i], data.actualSpeed);
console.logValue(`angle.` + "ABCD"[i], data.count);
const x = i * col;
print(`${scale(data.actualSpeed)}%`, x, brick.LINE_HEIGHT)
print(`${scale(data.count)}>`, x, 2 * brick.LINE_HEIGHT)
print(`${scale(data.tachoCount)}|`, x, 3 * brick.LINE_HEIGHT)
}
// sensors
const sis = sensors.internal.getActiveSensors();
for(let i =0; i < sis.length; ++i) {
const si = sis[i];
const x = (si.port() - 1) * 52;
const x = (si.port() - 1) * col;
const v = si._query();
print(`${v}`, x, 9 * brick.LINE_HEIGHT)
console.logValue(`sensor.` + si.port(), v);
print(`${scale(v)}`, x, 9 * brick.LINE_HEIGHT)
}
}
}

View File

@ -14,7 +14,7 @@ namespace sensors {
}
//% color="#A5CA18" weight=90 icon="\uf10d"
//% groups='["Motion", "Counters", "Sync Motion"]'
//% groups='["Move", "Counters"]'
//% labelLineWidth=0
namespace motors {
}

1
libs/matrix/README.md Normal file
View File

@ -0,0 +1 @@
# Matrix

View File

@ -0,0 +1,15 @@
{
"matrix.Matrix": "A 2D matrix",
"matrix.Matrix.add": "Returns a new matrix as the sum of both matrices",
"matrix.Matrix.cholesky": "Performs a Cholesky factorized for a symmetric and positive definite",
"matrix.Matrix.clone": "Clones the matrix",
"matrix.Matrix.get": "Gets a value from the matrix",
"matrix.Matrix.get|param|row": "@param col ",
"matrix.Matrix.identity": "Creates an identity matrix",
"matrix.Matrix.logToConsole": "Renders the matrix to the console",
"matrix.Matrix.multiply": "Multiplies the current matrix with the other matrix and returns a new matrix",
"matrix.Matrix.scale": "Returns a new matrix with scaled values",
"matrix.Matrix.set": "Sets a value in the array",
"matrix.Matrix.set|param|row": "@param col ",
"matrix.Matrix.transpose": "Returns a transposed matrix"
}

View File

@ -0,0 +1,4 @@
{
"matrix|block": "matrix",
"{id:category}Matrix": "Matrix"
}

186
libs/matrix/matrix.ts Normal file
View File

@ -0,0 +1,186 @@
namespace matrix {
function pre(check: boolean) {
if (!check)
control.reset();
}
/**
* A 2D matrix
*/
export class Matrix {
private _rows: number;
private _cols: number;
private _values: number[];
constructor(rows: number, cols: number, values: number[] = undefined) {
pre(rows > 0);
pre(cols > 0);
this._rows = rows;
this._cols = cols;
const n = this._rows * this._cols;
this._values = values || [];
// fill gaps
while (this._values.length < n)
this._values.push(0);
}
/**
* Creates an identity matrix
* @param size
*/
static identity(size: number): Matrix {
const m = new Matrix(size, size);
for (let i = 0; i < size; ++i)
m._values[i * size] = 1;
return m;
}
/**
* Sets a value in the array
* @param row
* @param col
* @param val
*/
set(row: number, col: number, val: number) {
pre(row == row >> 0 && row >= 0 && row < this._rows && col == col >> 0 && col >= 0 && col < this._cols);
this._values[row * this._cols + col] = val;
}
/**
* Gets a value from the matrix
* @param row
* @param col
*/
get(row: number, col: number): number {
pre(row == row >> 0 && row >= 0 && row < this._rows && col == col >> 0 && col >= 0 && col < this._cols);
return this._values[row * this._cols + col];
}
/**
* Gets the number of rows
*/
get rows(): number {
return this._rows;
}
/**
* Gets the number of colums
*/
get cols(): number {
return this._cols;
}
/**
* Gets the raw storage buffer
*/
get values(): number[] {
return this._values;
}
/**
* Returns a new matrix as the sum of both matrices
* @param other
*/
add(other: Matrix): Matrix {
pre(this._rows != other._rows || this._cols != other._cols)
const n = this._rows * this._cols;
const r: number[] = [];
for (let i = 0; i < n; ++i) {
r[i] = this._values[i] + other._values[i];
}
return new Matrix(this._rows, this._cols, r);
}
/**
* Returns a new matrix with scaled values
* @param factor
*/
scale(factor: number): Matrix {
const n = this._rows * this._cols;
const r: number[] = [];
for (let i = 0; i < n; ++i) {
r[i] = this._values[i] * factor;
}
return new Matrix(this._rows, this._cols, r);
}
/**
* Multiplies the current matrix with the other matrix and returns a new matrix
* @param other
*/
multiply(other: Matrix): Matrix {
pre(this._cols == other._rows);
const r: number[] = [];
for (let i = 0; i < this._rows; ++i) {
for (let j = 0; j < other._cols; ++j) {
let s = 0;
for (let k = 0; k < this._cols; ++k) {
s += this._values[i * this._cols + k] * other._values[k * other._cols + j];
}
r[i * other._cols + j];
}
}
return new Matrix(this._rows, other._cols, r);
}
/**
* Returns a transposed matrix
*/
transpose(): Matrix {
const R = new Matrix(this._cols, this._rows);
const r: number[] = R._values;
for (let i = 0; i < this._rows; ++i) {
for (let j = 0; j < this._cols; ++j) {
r[i + j * this._rows] = this._values[i * this._cols + j];
}
}
return R;
}
/**
* Clones the matrix
*/
clone(): Matrix {
const r = new Matrix(this._rows, this._cols, this._values.slice(0));
return r;
}
/**
* Performs a Cholesky factorized for a symmetric and positive definite
*
*/
cholesky(): Matrix {
pre(this._rows == this._cols);
const l = this.clone();
const n = this._rows;
const L = l._values;
for (let j = 0; j < n; j++) {
const jj = L[j * n + j] = Math.sqrt(L[j * n + j]);
for (let i = j + 1; i < n; ++i)
L[i * n + j] /= jj;
for (let k = j + 1; k < n; k++)
for (let i = k; i < n; i++)
L[i * n + j] -= L[i * n + j] * L[k * n + j];
}
return l;
}
/**
* Renders the matrix to the console
*/
logToConsole(): void {
let k = 0;
for(let i = 0; i < this._rows; ++i) {
let s = ""
for(let j = 0; j < this._cols; ++j) {
if (j > 0)
s += " "
s += Math.round((this._values[k++] * 100) / 100);
}
console.log(s)
}
}
}
}

15
libs/matrix/pxt.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "matrix",
"description": "Matrix algebra",
"files": [
"README.md",
"matrix.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

1
libs/matrix/test.ts Normal file
View File

@ -0,0 +1 @@
// add tests here

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.55",
"version": "0.0.57",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [

View File

@ -18,7 +18,8 @@
"libs/chassis",
"libs/ev3",
"libs/tests",
"libs/behaviors"
"libs/behaviors",
"libs/matrix"
],
"simulator": {
"autoRun": true,

View File

@ -74,6 +74,19 @@ span.blocklyTreeLabel {
border-radius: 0px !important;
}
.blocklyDropDownArrow.arrowTop {
border-top-left-radius: 0px !important;
}
.blocklyToggleRect {
rx: 0 !important;
ry: 0 !important;
}
.blocklyDropDownButton {
border-radius: 0px !important;
}
/* Mobile */
@media only screen and (max-width: @largestMobileScreen) {
#blocklyTrashIcon {