Compare commits

...

14 Commits

Author SHA1 Message Date
9187c47e09 0.0.78 2018-01-30 22:22:44 -08:00
fcf91caeb4 Zoom screen (#285)
* always start with full brick layout

* adding 'on start' to template

* render entire board when selected

* zoom brick when clicking on screen

* resize when zooming

* toggle zooming of the brick

* inject close icon when selected

* fix toggling
2018-01-30 22:22:21 -08:00
822227eb48 Brick ref topics 02 (#283)
* Add brick button topics

* Add the rest of the brick api
2018-01-30 20:58:18 -08:00
8a331648d6 Field editor fixes in Firefox (#284)
* fixing speed field picker

* switching to open sans

* alignment-baseline not support in FF
2018-01-30 20:40:41 -08:00
4f70d341e4 Start on the 'brick' api topics (#280)
* Start on the 'brick' api topics

* Add the delay to clear screen example

* Better output for clearsceen example
2018-01-30 17:02:22 -08:00
e06659ab4c removing group icons (#282) 2018-01-30 16:40:08 -08:00
437c36b983 0.0.77 2018-01-30 16:16:07 -08:00
3d73f193a8 fix for #260 (#279) 2018-01-30 16:14:54 -08:00
a71dee2923 rebuild for sampling (#261)
* rebuild for sampling

* bump pxt
2018-01-30 16:01:12 -08:00
9ef5b8d4ad 0.0.76 2018-01-30 11:05:29 -08:00
8aa47f3d1e updated chassis (#250) 2018-01-30 08:49:10 -08:00
02b0716043 0.0.75 2018-01-30 08:28:12 -08:00
188d5b3aa7 threshold query api 2018-01-30 08:27:23 -08:00
16c67f0e30 Refactoring datalog in common packages (#249)
* using datalog from common packages

* upgrading common package link

* updated block signatures

* more docs
2018-01-29 19:46:54 -08:00
54 changed files with 805 additions and 245 deletions

View File

@ -41,3 +41,17 @@
## Reference #reference
* [Reference](/reference)
* [Brick](/reference/brick)
* [show string](/reference/brick/show-string)
* [show number](/reference/brick/show-number)
* [show value](/reference/brick/show-value)
* [show mood](/reference/brick/show-mood)
* [show image](/reference/brick/show-image)
* [clear screen](/reference/brick/clear-screen)
* [print ports](/reference/brick/print-ports)
* [on event](/reference/brick/button/on-event)
* [is pressed](/reference/brick/button/is-pressed)
* [was pressed](/reference/brick/button/was-pressed)
* [pause until](/reference/brick/button/pause-until)
* [set light](/reference/brick/set-light)
* [battery level](/reference/brick/battery-level)

View File

@ -1,8 +1,11 @@
# Reference
```namespaces
brick.showMood(moods.sleeping);
```
## See Also
[brick](/reference/brick),
[touch sensor](/reference/sensors/touch-sensor),
[color sensor](/reference/sensors/color-sensor)

31
docs/reference/brick.md Normal file
View File

@ -0,0 +1,31 @@
# Brick
## Screen
```cards
brick.showMood(moods.sleeping);
brick.showImage(images.expressionsBigSmile);
brick.showString("Hello world!", 1);
brick.showNumber(0, 1);
brick.showValue("item", 0, 1);
brick.clearScreen();
brick.printPorts();
```
## Buttons
```cards
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
});
brick.buttonEnter.pauseUntil(ButtonEvent.Click);
brick.buttonEnter.isPressed()
brick.buttonEnter.wasPressed()
brick.setLight(BrickLight.Red);
```
## Other
```cards
brick.batteryLevel()
```

View File

@ -0,0 +1,33 @@
# battery Level
Return the current battery level.
```sig
brick.batteryLevel();
```
## Returns
* a [number](/types/number) which is the current charge level of the brick's battery. This is a percentage of total charge left in the battery.
## Example
Show the battery level percentage on the screen. Also, show a green light if the battery level is above 15%. If the battery level is below 15% but above 5%, show a orange light. But, if the battery level is below 5%, show a pulsing red light.
```blocks
let battery = 0;
loops.forever(function() {
brick.showString("Battery level:", 1)
brick.showNumber(battery, 2)
battery = brick.batteryLevel();
if (battery > 15)
{
brick.setLight(BrickLight.Green);
} else if (battery > 5) {
brick.setLight(BrickLight.Orange);
} else {
brick.setLight(BrickLight.RedPulse)
}
loops.pause(30000)
})
```

View File

@ -0,0 +1,53 @@
# is Pressed
Check if a button is being pressed or not.
```sig
brick.buttonEnter.isPressed()
```
## ~hint
**Touch sensors**
Your @boardname@ has touch sensors that work like buttons. Instead of saying `enter` or `left` as the source button, use a touch sensor block with a sensor name like `touch 1`.
```block
if (sensors.touch1.isPressed()) {
console.log("Hey, I feel pressed.");
}
```
Read about [touch sensors](/reference/sensors/touch-sensor) and using them as touch buttons.
## ~
## Returns
* a [boolean](types/boolean): `true` if the button is pressed, `false` if the button is not pressed
## Example
Set the brick light to green when the `down` is pressed. When the button is not pressed, the brick light is red.
```blocks
let isRed = false;
loops.forever(function() {
if (brick.buttonLeft.isPressed()) {
brick.setLight(BrickLight.Green);
isRed = false;
} else {
if (!isRed) {
brick.setLight(BrickLight.Red);
isRed = true;
}
}
})
```
## See also
[was pressed](/reference/brick/button/was-pressed),
[on event](/reference/brick/button/on-event)
[Touch sensors](/reference/sensors/touch-sensor)

View File

@ -0,0 +1,57 @@
# on Event
Run some code when a button is clicked, pressed down, or released.
```sig
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
});
```
## ~hint
**Touch sensors**
Your @boardname@ has touch sensors that work like buttons. Instead of saying `enter` or `left` as the source button, use a touch sensor block with a sensor name like `touch 1`.
```block
sensors.touch1.onEvent(TouchSensorEvent.Pressed, function () {
brick.setLight(BrickLight.Orange);
});
```
Read about [touch sensors](/reference/sensors/touch-sensor) and using them as touch buttons.
## ~
## Parameters
* **ev**: the button action to run some code for. The button actions (events) are:
> * ``click``: button was clicked (pressed and released)
> * ``up``: button is released from just being pressed
> * ``down``: button is just pressed down
* **body**: the code you want to run when something happens with a button
## Example
Check for event on the ENTER button. Put a message on the screen when the button is pressed, clicked, or released.
```blocks
brick.showString("ENTER is: UP", 1);
brick.buttonEnter.onEvent(ButtonEvent.Up, function () {
brick.showString("ENTER is: UP ", 1);
});
brick.buttonEnter.onEvent(ButtonEvent.Down, function () {
brick.showString("ENTER is: DOWN ", 1);
});
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
brick.showString("ENTER was: CLICKED", 1);
});
```
### See also
[is pressed](/reference/brick/button/is-pressed),
[was pressed](/reference/brick/button/was-pressed),
[Touch sensor](/reference/sensors/touch-sensor)

View File

@ -0,0 +1,37 @@
# pause Until
Causes your program to wait until an event at a button happens.
```sig
brick.buttonEnter.pauseUntil(ButtonEvent.Click);
```
## Parameters
* **ev**: the button action to wait for. The button actions (events) are:
> * ``click``: button was clicked (pressed and released)
> * ``up``: button is released from just being pressed
> * ``down``: button is just pressed down
## Example
Wait for the `up` button to go up before continuing with displaying a message on the screen.
```blocks
let waitTime = 0;
brick.showString("We're going to wait", 1);
brick.showString("for you to press and", 2);
brick.showString("release the UP button", 3);
waitTime = control.millis();
brick.buttonUp.pauseUntil(ButtonEvent.Click);
brick.clearScreen();
if (control.millis() - waitTime > 5000) {
brick.showString("Ok, that took awhile!", 1)
} else {
brick.showString("Ah, you let go!", 1)
}
```
## See also
[on event](/reference/brick/button/on-event)

View File

@ -0,0 +1,50 @@
# was Pressed
Check if a button was pressed earlier.
```sig
brick.buttonEnter.wasPressed()
```
The fact that a button was pressed earlier is remembered. Once **was pressed** is used, this fact is forgotten and the result is `false` the next time you check with **was pressed** button _state_ is reset). But, if you press the button again before you check with **was pressed**, it will tell you `true`.
## ~hint
**Touch sensors**
Your @boardname@ has touch sensors that work like buttons. Instead of saying `enter` or `left` as the source button, use a touch sensor block with a sensor name like `touch 1`.
```block
if (sensors.touch1.wasPressed()) {
console.log("Hey, I was pressed.");
}
```
Read about [touch sensors](/reference/sensors/touch-sensor) and using them as touch buttons.
## ~
## Returns
* a [boolean](types/boolean): `true` if the button was pressed before, `false` if the button was not pressed before
## Example
Set the brick light to green if the `right` button was pressed before the `left` button. If not, the brick light is turned off when the `left` button is pressed.
```blocks
brick.buttonLeft.onEvent(ButtonEvent.Click, function() {
if (brick.buttonRight.wasPressed()) {
brick.setLight(BrickLight.Green)
} else {
brick.setLight(BrickLight.Off)
}
})
```
## See also
[is pressed](/reference/brick/button/is-pressed),
[on event](/reference/brick/button/on-event)
[Touch sensors](/reference/sensors/touch-sensor)

View File

@ -0,0 +1,22 @@
# clear Screen
Clear any text or numbers displayed on the screen. The screen will be blank.
```sig
brick.clearScreen();
```
## Example
Clear the screen after displaying the message.
```blocks
brick.showString("This message will", 1);
brick.showString("self-destruct in:", 2);
brick.showString("seconds", 5);
for (let i = 0; i < 10; i++) {
brick.showNumber(10 - i, 4);
loops.pause(1000);
}
brick.clearScreen();
```

View File

@ -0,0 +1,19 @@
# print Ports
Print the status of the ports on the screen.
```sig
brick.printPorts();
```
## Example
Show the port status.
```blocks
brick.printPorts();
```
## See also
[show string](/reference/brick/show-string), [show value](/reference/brick/show-value)

View File

@ -0,0 +1,37 @@
# set Light
Set the light on the brick to a solid or flashing color.
```sig
brick.setLight(BrickLight.Red);
```
## Parameters
* **pattern**: the color or color pattern for the brick light to show. The brick light can have these color patterns:
>* `off`: brick light is off
>* `green`: solid green
>* `red`: solid red
>* `orange`: solid orange
>* `green flash`: flashing green
>* `red flash`: flashing red
>* `orange flash`: flashing orange
>* `green pulse`: pulsing green
>* `red pulse`: pulsing red
>* `orange pulse`: pulsing orange
## Example
Repeatedly show a different color pattern for the brick light.
```blocks
loops.forever(function () {
brick.setLight(BrickLight.Orange)
loops.pause(1000)
brick.setLight(BrickLight.GreenFlash)
loops.pause(2000)
brick.setLight(BrickLight.RedPulse)
loops.pause(2000)
brick.setLight(BrickLight.Off)
loops.pause(500)
})
```

View File

@ -0,0 +1,24 @@
# show Image
Show an image on the brick's display.
```sig
brick.showImage(images.expressionsBigSmile);
```
You can choose one of several images to show on the display.
## Parameters
**image**: A image to show on the brick's display. Use the image picker to choose the image you want to show.
## Example
Show a sleeping image on the brick's display.
```blocks
brick.showImage(images.expressionsZzz)
```
## See also
[show image](/reference/brick/show-mood)

View File

@ -0,0 +1,24 @@
# show Number
Show a number on the screen at the line you select.
```sig
brick.showNumber(0, 1);
```
## Parameters
* **value**: a [number](/types/number) to show on the brick's screen.
* **line**: The line number on the screen where the value is displayed. The line numbers for the screen start with line `1`.
## Example
Show the number `1000` on the screen.
```blocks
brick.showNumber(1000, 1);
```
## See also
[show string](/reference/brick/show-string), [show value](/reference/brick/show-value)

View File

@ -0,0 +1,29 @@
# show String
Show some text on a the screen at the line you select.
```sig
brick.showString("Hello world", 1)
```
## Parameters
* **text**: a [string](/types/string) to show on the brick's screen.
* **line**: the line [number](/types/number) on the screen where the text is displayed. The line numbers for the screen start with line `1`.
## Example
Show a greeting on the screen. Then, respond with another message when ENTER is pressed.
```blocks
brick.showString("Hello, I dare you to", 1);
brick.showString("press ENTER...", 2);
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
brick.showString("Hey! Don't push my", 4);
brick.showString("buttons.", 5);
});
```
## See also
[show number](/reference/brick/show-number)

View File

@ -0,0 +1,29 @@
# show Value
Show a name-value-pair on the screen at the line you select.
```sig
brick.showNumber("item", 0, 1);
```
Name-value-pairs are used to report data values to the screen. If you want to show the current temperature on the screen, you might use `"temp"` as the data name for the the value.
## Parameters
* **name**: a [string](/types/string) which is the name of the data value.
* **value**: a [number](/types/number) to show on the brick's screen.
* **line**: The line number on the screen where the value is displayed. The line numbers for the screen start with line `1`.
## Example
Show the current amount of ambient light detected by sensor 2.
```blocks
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
brick.showValue("color", sensors.color2.light(LightIntensityMode.Ambient), 1)
})
```
## See also
[show number](/reference/brick/show-number)

View File

@ -40,7 +40,9 @@ export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCust
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"
viewBox: "0 0 200 100",
width: "200",
height: "100"
});
labelContainer.appendChild(this.speedSVG);
@ -58,7 +60,7 @@ export class FieldSpeed extends Blockly.FieldSlider implements Blockly.FieldCust
this.reporter = pxsim.svg.child(this.speedSVG, "text", {
'x': 100, 'y': 80,
'text-anchor': 'middle', 'alignment-baseline': 'middle',
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;

View File

@ -56,7 +56,7 @@ export class FieldTurnRatio extends Blockly.FieldSlider implements Blockly.Field
}, marker);
this.reporter_ = pxsim.svg.child(svg, "text", {
'x': FieldTurnRatio.HALF, 'y': 96,
'text-anchor': 'middle', 'alignment-baseline': 'middle',
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;

View File

@ -1,3 +1,5 @@
<xml xmlns="http://www.w3.org/1999/xhtml">
<block type="forever"></block>
<variables></variables>
<block type="pxt-on-start" x="0" y="0"></block>
<block type="forever" x="176" y="0"></block>
</xml>

View File

@ -1,11 +1,10 @@
{
"chassis": "A differential drive robot",
"chassis.Chassis": "A differential drive robot",
"chassis.Chassis.drive": "Makes a differential drive robot move with a given speed (cm/s) and rotation rate (deg/s)\nusing a unicycle model.",
"chassis.Chassis.drive|param|rotationSpeed": "rotation of the robot around the center point, eg: 30",
"chassis.Chassis.drive|param|speed": "speed of the center point between motors, eg: 10",
"chassis.Chassis.drive|param|value": "the amount of movement, eg: 2",
"chassis.Chassis.setBaseLength": "Sets the base length in centimeters",
"chassis.Chassis.setMotors": "Sets the motors used by the chassis, default is B+C",
"chassis.Chassis.setProperty": "Sets a property of the robot",
"chassis.Chassis.setProperty|param|property": "the property to set",
"chassis.Chassis.setProperty|param|value": "the value to set"
"chassis.Chassis.setWheelRadius": "Sets the wheel radius in centimeters"
}

View File

@ -1,9 +1,8 @@
{
"ChassisProperty.BaseLength|block": "base length (cm)",
"ChassisProperty.WheelRadius|block": "wheel radius (cm)",
"chassis.Chassis.drive|block": "drive %chassis|at %speed|cm/s|turning %rotationSpeed|deg/s",
"chassis.Chassis.setBaseLength|block": "set %chassis|base length to %cm|(cm)",
"chassis.Chassis.setMotors|block": "set %chassis|motors to %motors",
"chassis.Chassis.setProperty|block": "set %chassis|%property|to %value",
"chassis.Chassis.setWheelRadius|block": "set %chassis|wheel radius to %cm|(cm)",
"chassis|block": "chassis",
"{id:category}Chassis": "Chassis"
}

View File

@ -1,10 +1,7 @@
enum ChassisProperty {
//% block="wheel radius (cm)"
WheelRadius,
//% block="base length (cm)"
BaseLength
}
/**
* A differential drive robot
*/
//% weight=50 color=#cf00cf
namespace chassis {
/**
* A differential drive robot
@ -29,13 +26,17 @@ namespace chassis {
* using a unicycle model.
* @param speed speed of the center point between motors, eg: 10
* @param rotationSpeed rotation of the robot around the center point, eg: 30
* @param value the amount of movement, eg: 2
* @param unit
*/
* @param distance
**/
//% blockId=motorDrive block="drive %chassis|at %speed|cm/s|turning %rotationSpeed|deg/s"
//% inlineInputMode=inline
//% weight=95 blockGap=8
drive(speed: number, rotationSpeed: number, value: number = 0, unit: MoveUnit = MoveUnit.MilliSeconds) {
drive(speed: number, rotationSpeed: number, distance: number = 0) {
if (!speed) {
this.motors.stop();
return;
}
// speed is expressed in %
const R = this.wheelRadius; // cm
const L = this.baseLength; // cm
@ -52,26 +53,30 @@ namespace chassis {
const sr = vr / maxw * 100; // %
const sl = vl / maxw * 100; // %
this.motors.tank(sr, sl, value, unit)
// cm / (cm/s) = s
const seconds = distance / speed;
this.motors.tank(sr, sl, seconds, MoveUnit.Seconds)
}
/**
* Sets a property of the robot
* @param property the property to set
* @param value the value to set
* Sets the wheel radius in centimeters
* @param cm
*/
//% blockId=chassisSetProperty block="set %chassis|%property|to %value"
//% blockGap=8
//% weight=10
setProperty(property: ChassisProperty, value: number) {
switch (property) {
case ChassisProperty.WheelRadius:
this.wheelRadius = Math.max(0.1, value); break;
case ChassisProperty.BaseLength:
this.baseLength = Math.max(0.1, value); break;
}
//% blockId=chassisSetWheelRadius block="set %chassis|wheel radius to %cm|(cm)"
setWheelRadius(cm: number) {
this.wheelRadius = cm;
}
/**
* Sets the base length in centimeters
* @param cm
*/
//% blockId=chassisSetBaseLength block="set %chassis|base length to %cm|(cm)"
setBaseLength(cm: number) {
this.baseLength = cm;
}
/**
* Sets the motors used by the chassis, default is B+C
* @param motors
@ -81,6 +86,10 @@ namespace chassis {
setMotors(motors: motors.SynchedMotorPair) {
this.motors = motors;
}
toString(): string {
return `chassis base ${this.baseLength}, wheel ${this.wheelRadius}`;
}
}
//% fixedInstance whenUsed

View File

@ -15,5 +15,8 @@
"sensors.ColorSensor.pauseForLight": "Waits for the given color to be detected",
"sensors.ColorSensor.setThreshold": "Sets a threshold value",
"sensors.ColorSensor.setThreshold|param|condition": "the dark or bright light condition",
"sensors.ColorSensor.setThreshold|param|value": "the value threshold"
"sensors.ColorSensor.setThreshold|param|value": "the value threshold",
"sensors.ColorSensor.threshold": "Gets the threshold value",
"sensors.ColorSensor.threshold|param|condition": "the light condition",
"sensors.color": "Returns a color that the sensor can detect"
}

View File

@ -21,10 +21,12 @@
"sensors.ColorSensor.pauseForColor|block": "pause %sensor|for color %color",
"sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|%condition",
"sensors.ColorSensor.setThreshold|block": "set %sensor|%condition|to %value",
"sensors.ColorSensor.threshold|block": "%sensor|%condition",
"sensors.color1|block": "color 1",
"sensors.color2|block": "color 2",
"sensors.color3|block": "color 3",
"sensors.color4|block": "color 4",
"sensors.color|block": "color %color",
"sensors|block": "sensors",
"{id:category}Sensors": "Sensors",
"{id:group}Color Sensor": "Color Sensor",

View File

@ -19,21 +19,21 @@ enum LightIntensityMode {
}
const enum ColorSensorColor {
//% block="none"
//% block="none" blockIdentity=sensors.color
None,
//% block="black"
//% block="black" blockIdentity=sensors.color
Black,
//% block="blue"
//% block="blue" blockIdentity=sensors.color
Blue,
//% block="green"
//% block="green" blockIdentity=sensors.color
Green,
//% block="yellow"
//% block="yellow" blockIdentity=sensors.color
Yellow,
//% block="red"
//% block="red" blockIdentity=sensors.color
Red,
//% block="white"
//% block="white" blockIdentity=sensors.color
White,
//% block="brown"
//% block="brown" blockIdentity=sensors.color
Brown,
}
@ -157,6 +157,7 @@ namespace sensors {
//% sensor.fieldEditor="ports"
//% weight=98
//% group="Color Sensor"
//% blockGap=8
color(): ColorSensorColor {
this.setMode(ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0)
@ -241,6 +242,17 @@ namespace sensors {
this.thresholdDetector.setHighThreshold(value);
}
/**
* Gets the threshold value
* @param condition the light condition
*/
//% blockId=colorGetThreshold block="%sensor|%condition"
//% group="Threshold" blockGap=8 weight=89
//% sensor.fieldEditor="ports"
threshold(condition: LightCondition): number {
return this.thresholdDetector.threshold(<ThresholdState><number>LightCondition.Dark);
}
/**
* Collects measurement of the light condition and adjusts the threshold to 10% / 90%.
*/
@ -252,45 +264,57 @@ namespace sensors {
this.light(mode); // trigger a read
pauseUntil(() => this.isActive()); // ensure sensor is live
let vold = 0;
let vcount = 0;
let min = 200;
let max = -200;
let k = 0;
while(k++ < 1000 && vcount < 50) {
let max = -200;
let k = 0;
while (k++ < 1000 && vcount < 50) {
let v = this.light(mode);
min = Math.min(min, v);
max = Math.max(max, v);
// detect if nothing has changed and stop calibration
if (Math.abs(v - vold) <= 2)
vcount ++;
vcount++;
else {
vold = v;
vcount = 1;
}
// wait a bit
loops.pause(50);
loops.pause(50);
}
// apply tolerance
const minDist = 10;
min = Math.max(minDist / 2, Math.min(min + deviation / 2, max - deviation / 2 - minDist / 2));
max = Math.min(100 - minDist / 2, Math.max(min + minDist, max - deviation / 2));
// apply thresholds
this.thresholdDetector.setLowThreshold(min);
this.thresholdDetector.setHighThreshold(max);
this.calibrating = false;
}
}
/**
* Returns a color that the sensor can detect
*/
//% shim=TD_ID
//% blockId=colorSensorColor block="color %color"
//% group="Color Sensor"
//% weight=97
export function color(color: ColorSensorColor): ColorSensorColor {
return color;
}
//% whenUsed block="color 3" weight=95 fixedInstance jres=icons.port3
export const color3: ColorSensor = new ColorSensor(3)
//% whenUsed block="color 1" weight=90 fixedInstance jres=icons.port1
export const color1: ColorSensor = new ColorSensor(1)

View File

@ -29,11 +29,11 @@
"brick.buttonLeft": "Left button on the EV3 Brick.",
"brick.buttonRight": "Right button on the EV3 Brick.",
"brick.buttonUp": "Up button on the EV3 Brick.",
"brick.clearScreen": "Clears the screen",
"brick.printPorts": "Prints the port states on the screen",
"brick.clearScreen": "Clear the screen",
"brick.printPorts": "Print the port states on the screen",
"brick.setLight": "Set lights.",
"brick.setLight|param|pattern": "the lights pattern to use. eg: BrickLight.Orange",
"brick.showImage": "Shows an image on screen",
"brick.showImage": "Show an image on the 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",

View File

@ -5,6 +5,7 @@ namespace brick {
*/
//% blockId=brickBatteryLevel block="battery level"
//% group="More"
//% help=brick/battery-level
export function batteryLevel(): number {
const info = sensors.internal.getBatteryInfo();
return info.current;

View File

@ -75,7 +75,7 @@ namespace brick {
* Check if button is currently pressed or not.
* @param button the button to query the request
*/
//% help=input/button/is-pressed
//% help=brick/button/is-pressed
//% block="%button|is pressed"
//% blockId=buttonIsPressed
//% parts="brick"
@ -91,7 +91,7 @@ namespace brick {
* See if the button was pressed again since the last time you checked.
* @param button the button to query the request
*/
//% help=input/button/was-pressed
//% help=brick/button/was-pressed
//% block="%button|was pressed"
//% blockId=buttonWasPressed
//% parts="brick"
@ -111,7 +111,7 @@ namespace brick {
* @param event the kind of button gesture that needs to be detected
* @param body code to run when the event is raised
*/
//% help=input/button/on-event
//% help=brick/button/on-event
//% blockId=buttonEvent block="on %button|%event"
//% parts="brick"
//% blockNamespace=brick
@ -126,7 +126,7 @@ namespace brick {
* Waits until the event is raised
* @param ev the event to wait for
*/
//% help=input/button/pause-until
//% help=brick/button/pause-until
//% blockId=buttonWaitUntil block="pause until %button|%event"
//% parts="brick"
//% blockNamespace=brick
@ -251,6 +251,7 @@ namespace brick {
*/
//% blockId=setLights block="set light to %pattern"
//% weight=100 group="Buttons"
//% help=brick/set-light
export function setLight(pattern: BrickLight): void {
if (currPattern === pattern)
return

View File

@ -432,11 +432,11 @@ namespace sensors {
export class ThresholdDetector {
public id: number;
private min: number;
private max: number;
private lowThreshold: number;
private highThreshold: number;
private level: number;
public min: number;
public max: number;
public lowThreshold: number;
public highThreshold: number;
public level: number;
public state: ThresholdState;
constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) {
@ -467,6 +467,14 @@ namespace sensors {
}
}
public threshold(t: ThresholdState): number {
switch(t) {
case ThresholdState.High: return this.highThreshold;
case ThresholdState.Low: return this.lowThreshold;
default: return (this.max - this.min) / 2;
}
}
public setLowThreshold(value: number) {
this.lowThreshold = this.clampValue(value);
this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);

View File

@ -72,6 +72,7 @@ namespace brick {
*/
//% blockId=screen_print block="show string %text|at line %line"
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
//% help=brick/show-string
//% line.min=1 line.max=10
export function showString(text: string, line: number) {
const NUM_LINES = 9;
@ -87,6 +88,7 @@ namespace brick {
*/
//% blockId=screenShowNumber block="show number %name|at line %line"
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
//% help=brick/show-number
//% line.min=1 line.max=10
export function showNumber(value: number, line: number) {
showString("" + value, line);
@ -99,6 +101,7 @@ namespace brick {
*/
//% blockId=screenShowValue block="show value %name|= %text|at line %line"
//% weight=96 group="Screen" inlineInputMode="inline" blockGap=8
//% help=brick/show-value
//% line.min=1 line.max=10
export function showValue(name: string, value: number, line: number) {
value = Math.round(value * 1000) / 1000;
@ -136,11 +139,12 @@ namespace brick {
}
/**
* Shows an image on screen
* Show an image on the screen
* @param image image to draw
*/
//% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=100 group="Screen" blockGap=8
//% help=brick/show-image
export function showImage(image: Image) {
if (!image) return;
image.draw(0, 0, Draw.Normal);
@ -160,10 +164,11 @@ namespace brick {
}
/**
* Clears the screen
* Clear the screen
*/
//% blockId=screen_clear_screen block="clear screen"
//% weight=90 group="Screen"
//% help=brick/clear-screen
export function clearScreen() {
screen.clear();
}
@ -208,9 +213,10 @@ namespace brick {
}
/**
* Prints the port states on the screen
* Print the port states on the screen
*/
//% blockId=brickPrintPorts block="print ports"
//% help=brick/print-ports
//% weight=1 group="Screen"
export function printPorts() {
const col = 44;

View File

@ -1,3 +0,0 @@
# Datalog
A tiny libraty to create CSV datalog files.

View File

@ -1,11 +1,17 @@
{
"datalog": "A data logging framework",
"datalog.DatalogStorage": "A storage for datalog data",
"datalog.DatalogStorage.appendHeaders": "Appends the headers in datalog",
"datalog.DatalogStorage.appendRow": "Appends a row of data",
"datalog.DatalogStorage.flush": "Flushes any buffered data",
"datalog.DatalogStorage.init": "Initializes the storage",
"datalog.addRow": "Starts a row of data",
"datalog.addValue": "Adds a cell to the row of data",
"datalog.addValue|param|name": "name of the cell, eg: \"x\"",
"datalog.addValue|param|value": "value of the cell, eg: 0",
"datalog.flush": "Commits any buffered row to disk",
"datalog.setEnabled": "Turns on or off datalogging",
"datalog.setFile": "Starts a new data logger for the given file",
"datalog.setFile|param|filename": "the filename, eg: \"datalog.csv\"",
"datalog.setSampleInterval": "Sets the minimum number of milli seconds between rows",
"datalog.setSampleInterval|param|millis": "milliseconds between each sample, eg: 50",
"datalog.setStorage": "* @param storage custom storage solution"
}

View File

@ -1,7 +1,8 @@
{
"datalog.addRow|block": "datalog add row",
"datalog.addValue|block": "datalog add %name|=%value",
"datalog.setEnabled|block": "datalog %enabled",
"datalog.setEnabled|block": "datalog %enabled=toggleOnOff",
"datalog.setSampleInterval|block": "set datalog sampling interval to %millis|(ms)",
"datalog|block": "datalog",
"{id:category}Datalog": "Datalog"
}

View File

@ -1,122 +0,0 @@
//% weight=100 color=#0fbc11 icon=""
namespace datalog {
let _headers: string[] = undefined;
let _headersLength: number;
let _values: number[];
let _buffer: string = "";
let _start: number;
let _filename = "datalog.csv";
let _storage: storage.Storage = storage.temporary;
let _enabled = true;
function clear() {
_headers = undefined;
_values = undefined;
_buffer = "";
}
function init() {
if (!_headers) {
_headers = [];
_headersLength = 0;
_start = control.millis();
_storage.remove(_filename);
}
_values = [];
}
function commit() {
// write row if any data
if (_values && _values.length > 0) {
// write headers for the first row
if (!_headersLength) {
_storage.appendCSVHeaders(_filename, _headers);
_headersLength = _storage.size(_filename);
}
// commit row data
_buffer += storage.toCSV(_values, _storage.csvSeparator);
// buffered writes
if (_buffer.length > 1024)
flush();
}
// clear values
_values = undefined;
}
/**
* Starts a row of data
*/
//% weight=100
//% blockId=datalogAddRow block="datalog add row"
export function addRow(): void {
if (!_enabled) return;
commit();
init();
const s = (control.millis() - _start) / 1000;
addValue("time (s)", s);
}
/**
* Adds a cell to the row of data
* @param name name of the cell, eg: "x"
* @param value value of the cell, eg: 0
*/
//% weight=99
//% blockId=datalogAddValue block="datalog add %name|=%value"
export function addValue(name: string, value: number) {
if (!_values) return;
let i = _headers.indexOf(name);
if (i < 0) {
_headers.push(name);
i = _headers.length - 1;
}
_values[i] = value;
}
/**
* Starts a new data logger for the given file
* @param filename the filename, eg: "datalog.csv"
*/
//%
export function setFile(filename: string) {
flush();
_filename = filename;
clear();
}
/**
*
* @param storage custom storage solution
*/
//%
export function setStorage(storage: storage.Storage) {
flush();
_storage = storage;
clear();
}
/**
* Commits any buffered row to disk
*/
//%
export function flush() {
if (_buffer) {
const b = _buffer;
_buffer = "";
_storage.append(_filename, b);
}
}
/**
* Turns on or off datalogging
* @param enabled
*/
//% blockId=datalogEnabled block="datalog %enabled"
//% enabled.fieldEditor=fieldonoff
export function setEnabled(enabled: boolean) {
flush();
_enabled = enabled;
}
}

View File

@ -1,16 +1,7 @@
{
"name": "datalog",
"description": "Tiny data logging framework",
"files": [
"README.md",
"datalog.ts"
],
"testFiles": [
"test.ts"
],
"public": true,
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/datalog",
"dependencies": {
"core": "file:../core",
"storage": "file:../storage"
"core": "file:../core",
"storage": "file:../storage"
}
}

View File

@ -0,0 +1,57 @@
namespace datalog.ev3 {
/**
* A datalog storage for the EV3
*/
export class EV3DatalogStorage extends DatalogStorage {
private _filename: string;
private _buffer: string;
private _storage: storage.Storage;
/**
* Creates a new storage for the datalog
* @param storage
* @param filename
*/
constructor(storage: storage.Storage, filename: string) {
super();
this._filename = filename;
this._storage = storage;
}
/**
* Initializes the storage
*/
init(): void {
this._storage.remove(this._filename);
this._buffer = "";
}
/**
* Appends the headers in datalog
*/
appendHeaders(headers: string[]): void {
this._storage.appendCSVHeaders(this._filename, headers);
}
/**
* Appends a row of data
*/
appendRow(values: number[]): void {
// commit row data
this._buffer += storage.toCSV(values, this._storage.csvSeparator);
// buffered writes
if (this._buffer.length > 512)
this.flush();
}
/**
* Flushes any buffered data
*/
flush(): void {
if (this._buffer) {
const b = this._buffer;
this._buffer = "";
this._storage.append(this._filename, b);
}
}
}
// automatic hook up
datalog.setStorage(new datalog.ev3.EV3DatalogStorage(storage.temporary, "datalog.csv"));
}

View File

@ -1,6 +0,0 @@
loops.forever(function () {
datalog.addRow()
datalog.addValue("x", Math.random())
datalog.addValue("y", Math.random())
})

View File

@ -9,7 +9,6 @@ namespace brick {
//% color="#C8509B" weight=95 icon="\uf10f"
//% labelLineWidth=0
//% groups='["Touch Sensor", "Color Sensor", "Ultrasonic Sensor", "Gyro Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Threshold"]'
//% groupIcons='["\uf101","\uf103","\uf102","","","\uf104"]'
namespace sensors {
}

View File

@ -7,6 +7,8 @@
"sensors.InfraredSensor.setThreshold": "Sets a threshold value",
"sensors.InfraredSensor.setThreshold|param|condition": "the dark or bright light condition",
"sensors.InfraredSensor.setThreshold|param|value": "the value threshold",
"sensors.InfraredSensor.threshold": "Gets the threshold value",
"sensors.InfraredSensor.threshold|param|condition": "the proximity condition",
"sensors.RemoteInfraredBeaconButton.isPressed": "Check if a remote button is currently pressed or not.",
"sensors.RemoteInfraredBeaconButton.onEvent": "Do something when a button or sensor is clicked, up or down",
"sensors.RemoteInfraredBeaconButton.onEvent|param|body": "code to run when the event is raised",

View File

@ -6,6 +6,7 @@
"sensors.InfraredSensor.proximity|block": "%sensor|proximity",
"sensors.InfraredSensor.remoteCommand|block": "%sensor|remote command",
"sensors.InfraredSensor.setThreshold|block": "set %sensor|%condition|to %value",
"sensors.InfraredSensor.threshold|block": "%sensor|%condition",
"sensors.RemoteInfraredBeaconButton.isPressed|block": "%button|is pressed",
"sensors.RemoteInfraredBeaconButton.onEvent|block": "on %button|%event",
"sensors.RemoteInfraredBeaconButton.wasPressed|block": "%button|was pressed",

View File

@ -258,7 +258,7 @@ namespace sensors {
* @param value the value threshold
*/
//% blockId=irSetThreshold block="set %sensor|%condition|to %value"
//% group="Threshold" blockGap=8
//% group="Threshold" blockGap=8 weight=49
//% value.min=0 value.max=100
setThreshold(condition: InfraredSensorEvent, value: number) {
if (condition == InfraredSensorEvent.ObjectNear)
@ -266,6 +266,17 @@ namespace sensors {
else
this.proximityThreshold.setHighThreshold(value);
}
/**
* Gets the threshold value
* @param condition the proximity condition
*/
//% blockId=irGetThreshold block="%sensor|%condition"
//% group="Threshold" blockGap=8 weight=49
//% sensor.fieldEditor="ports"
threshold(condition: InfraredSensorEvent): number {
return this.proximityThreshold.threshold(<ThresholdState><number>LightCondition.Dark);
}
}
//% fixedInstance whenUsed block="infrared 1" jres=icons.port1

View File

@ -1,7 +1,7 @@
{
"brick.Mood": "A mood",
"brick.Mood.show": "Shows the mood on the EV3",
"brick.showMood": "Shows a mood",
"brick.showMood": "Show a mood on the brick's screen",
"moods.angry": "An angry mood",
"moods.awake": "A awake mood",
"moods.dizzy": "A dizzy mood",

View File

@ -0,0 +1,37 @@
# show Mood
Show a mood on the brick. A mood will have a image on the display along with a sound and solid or flashing light.
```sig
brick.showMood(moods.sleeping)
```
You can choose one of several moods to show on the display. Use a mood to help show what your @boardname@ is doing at the moment.
## Parameters
**mood**: A mood to show on the brick. Choose one of these moods:
>* ``sleeping``
* ``awake``
* ``tired``
* ``angry``
* ``sad``
* ``dizzy``
* ``knockedOut``
* ``middleLeft``
* ``middleRight``
* ``love``
* ``winking``
* ``neutral``
## Example
Show a ``winking`` mood on the brick.
```blocks
brick.showMood(moods.winking)
```
## See also
[show image](/reference/brick/show-image)

View File

@ -1,9 +1,10 @@
namespace brick {
/**
* Shows a mood
* Show a mood on the brick's screen
*/
//% weight=90
//% blockId=moodShow block="show mood %mood=mood_image_picker"
//% help=brick/show-mood
//% weight=101 group="Screen" blockGap=8
export function showMood(mood: Mood) {
if(mood)

View File

@ -5,5 +5,7 @@
"sensors.UltraSonicSensor.pauseUntil": "Waits for the event to occur",
"sensors.UltraSonicSensor.setThreshold": "Sets a threshold value",
"sensors.UltraSonicSensor.setThreshold|param|condition": "the dark or bright light condition",
"sensors.UltraSonicSensor.setThreshold|param|value": "the value threshold"
"sensors.UltraSonicSensor.setThreshold|param|value": "the value threshold",
"sensors.UltraSonicSensor.threshold": "Gets the threshold value",
"sensors.UltraSonicSensor.threshold|param|condition": "the proximity condition"
}

View File

@ -6,6 +6,7 @@
"sensors.UltraSonicSensor.onEvent|block": "on %sensor|%event",
"sensors.UltraSonicSensor.pauseUntil|block": "pause until %sensor| %event",
"sensors.UltraSonicSensor.setThreshold|block": "set %sensor|%condition|to %value",
"sensors.UltraSonicSensor.threshold|block": "%sensor|%condition",
"sensors.ultrasonic1|block": "ultrasonic 1",
"sensors.ultrasonic2|block": "ultrasonic 2",
"sensors.ultrasonic3|block": "ultrasonic 3",

View File

@ -93,20 +93,36 @@ namespace sensors {
* @param value the value threshold
*/
//% blockId=ultrasonicSetThreshold block="set %sensor|%condition|to %value"
//% group="Threshold" blockGap=8
//% group="Threshold" blockGap=8 weight=80
//% value.min=0 value.max=255
setThreshold(condition: UltrasonicSensorEvent, value: number) {
switch(condition) {
setThreshold(condition: UltrasonicSensorEvent, value: number) {
switch (condition) {
case UltrasonicSensorEvent.ObjectNear: this.promixityThreshold.setLowThreshold(value); break;
case UltrasonicSensorEvent.ObjectFar: this.promixityThreshold.setHighThreshold(value); break;
case UltrasonicSensorEvent.ObjectDetected: this.movementThreshold = value; break;
}
}
/**
* Gets the threshold value
* @param condition the proximity condition
*/
//% blockId=ultrasonicGetThreshold block="%sensor|%condition"
//% group="Threshold" blockGap=8 weight=79
//% sensor.fieldEditor="ports"
threshold(condition: UltrasonicSensorEvent): number {
switch (condition) {
case UltrasonicSensorEvent.ObjectNear: this.promixityThreshold.threshold(ThresholdState.Low); break;
case UltrasonicSensorEvent.ObjectFar: this.promixityThreshold.threshold(ThresholdState.Low); break;
case UltrasonicSensorEvent.ObjectDetected: this.movementThreshold; break;
}
return 0;
}
}
//% fixedInstance whenUsed block="ultrasonic 4" jres=icons.port4
export const ultrasonic4: UltraSonicSensor = new UltraSonicSensor(4)
//% fixedInstance whenUsed block="ultrasonic 1" jres=icons.port1
export const ultrasonic1: UltraSonicSensor = new UltraSonicSensor(1)

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.74",
"version": "0.0.78",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.74",
"version": "0.0.78",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -44,8 +44,8 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.17.11",
"pxt-core": "3.0.26"
"pxt-common-packages": "0.17.13",
"pxt-core": "3.0.29"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -298,7 +298,14 @@ namespace pxsim.visuals {
this.layoutView.inject(this.element);
// Add EV3 module element
this.layoutView.setBrick(new BrickView(-1));
const brickCloseIcon = this.getCloseIconView();
brickCloseIcon.registerClick(ev => {
this.layoutView.unselectBrick();
this.resize();
});
const brick =new BrickView(-1);
brick.setSelected(EV3View.isPreviousBrickSelected());
this.layoutView.setBrick(brick, brickCloseIcon);
this.resize();
@ -329,6 +336,12 @@ namespace pxsim.visuals {
this.screenCanvas = document.createElement("canvas");
this.screenCanvas.id = "board-screen-canvas";
this.screenCanvas.style.position = "absolute";
this.screenCanvas.addEventListener(pxsim.pointerEvents.up, ev => {
this.layoutView.selectBrick();
this.resize();
})
this.screenCanvas.style.cursor = "pointer";
/*
this.screenCanvas.style.cursor = "crosshair";
this.screenCanvas.onmousemove = (e: MouseEvent) => {
const x = e.clientX;
@ -340,6 +353,7 @@ namespace pxsim.visuals {
this.screenCanvas.onmouseleave = () => {
this.updateXY(SCREEN_WIDTH, SCREEN_HEIGHT);
}
*/
this.screenCanvas.width = SCREEN_WIDTH;
this.screenCanvas.height = SCREEN_HEIGHT;
@ -360,10 +374,12 @@ namespace pxsim.visuals {
// Save previous inputs for the next cycle
EV3View.previousSelectedInputs = ev3board().getInputNodes().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1)
EV3View.previousSeletedOutputs = ev3board().getMotors().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1);
EV3View.previousSelectedBrick = this.layoutView.getBrick().getSelected();
}
private static previousSelectedInputs: number[];
private static previousSeletedOutputs: number[];
private static previousSelectedBrick: boolean;
private static isPreviousInputSelected(index: number, id: number) {
if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) {
@ -381,6 +397,12 @@ namespace pxsim.visuals {
return false;
}
private static isPreviousBrickSelected() {
const b = EV3View.previousSelectedBrick;
EV3View.previousSelectedBrick = false;
return !!b;
}
private begin() {
this.running = true;
this.updateState();

View File

@ -23,7 +23,7 @@ namespace pxsim.visuals {
this.reporter = pxsim.svg.child(this.group, "text", {
'x': this.getWidth() / 2, 'y': this.getHeight() / 2,
'text-anchor': 'middle', 'alignment-baseline': 'middle',
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number' }) as SVGTextElement;

View File

@ -27,7 +27,7 @@ namespace pxsim.visuals {
this.reporter = pxsim.svg.child(this.group, "text", {
'x': this.getInnerWidth() / 2, 'y': this.getInnerHeight() / 2,
'text-anchor': 'middle', 'alignment-baseline': 'middle',
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;

View File

@ -37,6 +37,8 @@ namespace pxsim.visuals {
private outputWires: WireView[] = [];
private brick: BrickView;
private brickCloseIcon: View = undefined;
private offsets: number[];
private contentGroup: SVGGElement;
private scrollGroup: SVGGElement;
@ -67,16 +69,32 @@ namespace pxsim.visuals {
this.position();
}
public setBrick(brick: BrickView) {
public setBrick(brick: BrickView, brickCloseIcon: View) {
this.brick = brick;
this.brick.inject(this.scrollGroup);
this.position();
this.brickCloseIcon = brickCloseIcon;
this.addView(this.brickCloseIcon);
this.brickCloseIcon.setVisible(this.brick.getSelected());
this.position();
}
public getBrick() {
return this.brick;
}
public unselectBrick() {
this.brick.setSelected(false);
this.brickCloseIcon.setVisible(false);
this.position();
}
public selectBrick() {
this.brick.setSelected(true);
this.brickCloseIcon.setVisible(true);
this.position();
}
public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
if (this.inputs[port] != view || this.inputControls[port] != control) {
if (this.inputs[port]) {
@ -224,17 +242,23 @@ namespace pxsim.visuals {
const noConnections = this.outputs.concat(this.inputs).filter(m => m.getId() != NodeType.Port).length == 0;
if (noConnections) {
// No connections render the entire board
this.brick.resize(contentWidth, contentHeight);
this.brick.translate(0, 0);
// render the full brick layout, even when there are not connection
// otherwise, it creates flickering of the simulator.
if (this.brick.getSelected()) {
// render output button
const closeIconWidth = this.brickCloseIcon.getWidth();
const closeIconHeight = this.brickCloseIcon.getHeight();
this.brickCloseIcon.translate(contentWidth / 2 - closeIconWidth / 2, 0);
// render the entire board
this.brick.resize(contentWidth, contentHeight - closeIconHeight * 2);
this.brick.translate(0, closeIconHeight * 2);
// Hide all other connections
this.outputs.concat(this.inputs).forEach(m => m.setVisible(false));
return;
} else {
this.outputs.concat(this.inputs).forEach(m => m.setVisible(true));
}
this.outputs.concat(this.inputs).forEach(m => m.setVisible(true));
const moduleHeight = this.getModuleHeight();

View File

@ -206,8 +206,10 @@ namespace pxsim.visuals {
}
public setSelected(selected: boolean) {
this.selected = selected;
this.setChangedState();
if (this.selected != selected) {
this.selected = selected;
this.setChangedState();
}
}
protected setChangedState() {

View File

@ -2,10 +2,10 @@
User Global Variables
*******************************/
@importGoogleFonts: false;
@importGoogleFonts: true;
@headerFont : Roboto,"Arial Narrow","Helvetica Neue",Helvetica,Arial,sans-serif;
@pageFont : Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;
@headerFont : "Open Sans","Arial Narrow","Helvetica Neue",Helvetica,Arial,sans-serif;
@pageFont : "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
@emSize : 14px;
@fontSize : 13px;