Bump V3.0.22 (#110)

* change simulator svg

* change radio image

* Remove google fonts cdn

* change color of 'advanced' button

* font fix

* font fix 2

* display fix

* change fullsceen simulator bg

* Continuous servo

* handle continuous state

* adding shims

* update rendering for continuous servos

* fixing sim

* fix sig

* typo

* fix sim

* bump pxt

* bump pxt

* rerun travis

* Input blocks revision

- add Button and Pin event types
- merge onPinPressed & onPinReleased in new onPinEvent function
- create new onButtonEvent function

* update input blocks in docs and tests

* remove device_pin_release block

* Hide DAL.x behind Enum

* bring back deprecated blocks, but hide them

* shims and locales files

* fix input.input. typing

* remove buildpr

* bump V3

* update simulator aspect ratio

* add Loudness Block

* revoke loudness block

* Adds soundLevel

To be replaced by pxt-common-packages when DAL is updated.

* Remove P0 & P3 from AnalogPin

Co-authored-by: Juri <gitkraken@juriwolf.de>
This commit is contained in:
Amerlander
2020-09-08 11:04:25 +02:00
committed by GitHub
parent 98d8b2977b
commit 918af4f3ac
233 changed files with 9391 additions and 2739 deletions

View File

@ -1,8 +1,10 @@
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../libs/core/dal.d.ts"/>
/// <reference path="../libs/core/enums.d.ts"/>
namespace pxsim {
export class DalBoard extends CoreBoard
implements RadioBoard {
implements RadioBoard, LightBoard {
// state & update logic for component services
ledMatrixState: LedMatrixState;
edgeConnectorState: EdgeConnectorState;
@ -13,8 +15,8 @@ namespace pxsim {
lightSensorState: LightSensorState;
buttonPairState: ButtonPairState;
radioState: RadioState;
// TODO: not singletons
neopixelState: NeoPixelState;
microphoneState: AnalogSensorState;
lightState: pxt.Map<CommonNeoPixelState>;
rgbLedState: number;
speakerState: SpeakerState;
fileSystem: FileSystemState;
@ -27,6 +29,7 @@ namespace pxsim {
super()
// components
this.lightState = {};
this.fileSystem = new FileSystemState();
this.builtinParts["ledmatrix"] = this.ledMatrixState = new LedMatrixState(runtime);
this.builtinParts["buttonpair"] = this.buttonPairState = new ButtonPairState({
@ -72,23 +75,25 @@ namespace pxsim {
ID_RADIO: DAL.MICROBIT_ID_RADIO,
RADIO_EVT_DATAGRAM: DAL.MICROBIT_RADIO_EVT_DATAGRAM
});
this.builtinParts["microphone"] = this.microphoneState = new AnalogSensorState(3001 /* DEVICE_ID_MICROPHONE */, 52, 120, 75, 96);
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
this.builtinParts["serial"] = this.serialState = new SerialState();
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
this.builtinParts["compass"] = this.compassState = new CompassState();
this.builtinParts["neopixel"] = this.neopixelState = new NeoPixelState();
this.builtinParts["speaker"] = this.speakerState = new SpeakerState();
this.builtinParts["microservo"] = this.edgeConnectorState;
this.builtinVisuals["buttonpair"] = () => new visuals.ButtonPairView();
this.builtinVisuals["ledmatrix"] = () => new visuals.LedMatrixView();
this.builtinVisuals["neopixel"] = () => new visuals.NeoPixelView();
this.builtinVisuals["microservo"] = () => new visuals.MicroServoView();
this.builtinParts["neopixel"] = (pin: Pin) => { return this.neopixelState(pin.id); };
this.builtinVisuals["neopixel"] = () => new visuals.NeoPixelView(pxsim.parsePinString);
this.builtinPartVisuals["neopixel"] = (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy);
this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy);
this.builtinPartVisuals["ledmatrix"] = (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8);
this.builtinPartVisuals["neopixel"] = (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy);
this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
}
@ -114,8 +119,6 @@ namespace pxsim {
initAsync(msg: SimulatorRunMessage): Promise<void> {
super.initAsync(msg);
const options = (msg.options || {}) as RuntimeOptions;
const boardDef = msg.boardDefinition;
const cmpsList = msg.parts;
const cmpDefs = msg.partDefinitions || {};
@ -143,6 +146,19 @@ namespace pxsim {
return Promise.resolve();
}
tryGetNeopixelState(pinId: number): CommonNeoPixelState {
return this.lightState[pinId];
}
neopixelState(pinId: number): CommonNeoPixelState {
if (pinId === undefined) {
pinId = DAL.MICROBIT_ID_IO_P0;
}
let state = this.lightState[pinId];
if (!state) state = this.lightState[pinId] = new CommonNeoPixelState();
return state;
}
screenshotAsync(width?: number): Promise<ImageData> {
return this.viewHost.screenshotAsync(width);
}
@ -180,4 +196,15 @@ namespace pxsim {
export function board(): DalBoard {
return runtime.board as DalBoard;
}
}
export function parsePinString(gpioPin: string): Pin {
if (gpioPin == "*")
return board().edgeConnectorState.getPin(DAL.MICROBIT_ID_IO_P0);
const m = /^(Analog|Digital)Pin\.P(\d)+/.exec(gpioPin);
if (!m)
return undefined;
const pinNum = parseInt(m[2]);
return board().edgeConnectorState.pins[pinNum]
}
}

View File

@ -16,6 +16,7 @@ namespace pxsim.input {
export function isGesture(gesture: number): boolean {
const b = accForGesture(gesture);
b.accelerometer.activate();
return b.accelerometer.getGesture() == gesture;
}
@ -33,28 +34,25 @@ namespace pxsim.input {
acc.activate(AccelerometerFlag.Z);
return acc.getZ();
default:
acc.activate();
return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
acc.activate(AccelerometerFlag.Strength);
return acc.getStrength();
}
}
export function rotation(kind: number): number {
const b = board().accelerometerState;
const acc = b.accelerometer;
acc.activate();
const x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const y = acc.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const z = acc.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
const roll = Math.atan2(y, z);
const pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
let r = 0;
switch (kind) {
case 0: r = pitch; break;
case 1: r = roll; break;
case 0: {
acc.activate(AccelerometerFlag.Pitch);
return acc.getPitch();
}
case 1: {
acc.activate(AccelerometerFlag.Roll);
return acc.getRoll();
}
default: return 0;
}
return Math.floor(r / Math.PI * 180);
}
export function setAccelerometerRange(range: number) {
@ -117,8 +115,11 @@ namespace pxsim {
export enum AccelerometerFlag {
X = 1,
Y = 2,
Z = 4
Y = 1 << 1,
Z = 1 << 2,
Strength = 1 << 3,
Pitch = 1 << 4,
Roll = 1 << 5
}
export class Accelerometer {
@ -148,7 +149,7 @@ namespace pxsim {
this.isActive = true;
this.runtime.queueDisplayUpdate();
}
if (flags)
if (!!flags)
this.flags |= flags;
}
@ -169,7 +170,29 @@ namespace pxsim {
board().bus.queue(this.id, DAL.MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE)
}
public instantaneousAccelerationSquared() {
public getStrength() {
return Math.floor(Math.sqrt(this.instantaneousAccelerationSquared()));
}
updateEnvironmentGlobals() {
// update debugger
if (this.isActive) {
if (this.flags & AccelerometerFlag.X)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.x")] = this.sample.x;
if (this.flags & AccelerometerFlag.Y)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.y")] = this.sample.y;
if (this.flags & AccelerometerFlag.Z)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.z")] = this.sample.z;
if (this.flags & AccelerometerFlag.Strength)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.strength")] = Math.sqrt(this.instantaneousAccelerationSquared());
if (this.flags & AccelerometerFlag.Pitch)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.pitch")] = this.getPitch();
if (this.flags & AccelerometerFlag.Roll)
this.runtime.environmentGlobals[pxsim.localization.lf("acceleration.roll")] = this.getRoll();
}
}
private instantaneousAccelerationSquared() {
// Use pythagoras theorem to determine the combined force acting on the device.
return this.sample.x * this.sample.x + this.sample.y * this.sample.y + this.sample.z * this.sample.z;
}
@ -294,7 +317,6 @@ namespace pxsim {
* @endcode
*/
public getX(system: MicroBitCoordinateSystem = MicroBitCoordinateSystem.SIMPLE_CARTESIAN): number {
this.activate();
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
return -this.sample.x;
@ -319,7 +341,6 @@ namespace pxsim {
* @endcode
*/
public getY(system: MicroBitCoordinateSystem = MicroBitCoordinateSystem.SIMPLE_CARTESIAN): number {
this.activate();
switch (system) {
case MicroBitCoordinateSystem.SIMPLE_CARTESIAN:
return -this.sample.y;
@ -344,7 +365,6 @@ namespace pxsim {
* @endcode
*/
public getZ(system: MicroBitCoordinateSystem = MicroBitCoordinateSystem.SIMPLE_CARTESIAN): number {
this.activate();
switch (system) {
case MicroBitCoordinateSystem.NORTH_EAST_DOWN:
return -this.sample.z;
@ -365,7 +385,6 @@ namespace pxsim {
* @endcode
*/
public getPitch(): number {
this.activate();
return Math.floor((360 * this.getPitchRadians()) / (2 * Math.PI));
}
@ -384,7 +403,6 @@ namespace pxsim {
* @endcode
*/
public getRoll(): number {
this.activate();
return Math.floor((360 * this.getRollRadians()) / (2 * Math.PI));
}

View File

@ -47,7 +47,7 @@ namespace pxsim.pins {
export function setPull(pinId: number, pull: number) {
let pin = getPin(pinId);
if (!pin) return;
pin.pull = pull;
pin.setPull(pull);
}
export function analogReadPin(pinId: number): number {
@ -61,7 +61,7 @@ namespace pxsim.pins {
let pin = getPin(pinId);
if (!pin) return;
pin.mode = PinFlags.Analog | PinFlags.Output;
pin.value = value ? value : 0;
pin.value = value | 0;
runtime.queueDisplayUpdate();
}
@ -101,22 +101,38 @@ namespace pxsim.pins {
pin.pitch = true;
}
export function analogSetPitchVolume(volume: number) {
const ec = board().edgeConnectorState;
ec.pitchVolume = Math.max(0, Math.min(0xff, volume | 0));
}
export function analogPitchVolume() {
const ec = board().edgeConnectorState;
return ec.pitchVolume;
}
export function analogPitch(frequency: number, ms: number) {
// update analog output
let pins = board().edgeConnectorState.pins;
let pin = pins.filter(pin => !!pin && pin.pitch)[0] || pins[0];
const b = board();
if (!b) return;
const ec = b.edgeConnectorState;
const pins = ec.pins;
const pin = pins.filter(pin => !!pin && pin.pitch)[0] || pins[0];
const pitchVolume = ec.pitchVolume | 0;
pin.mode = PinFlags.Analog | PinFlags.Output;
if (frequency <= 0) {
if (frequency <= 0 || pitchVolume <= 0) {
pin.value = 0;
pin.period = 0;
} else {
pin.value = 512;
const v = 1 << (pitchVolume >> 5);
pin.value = v;
pin.period = 1000000 / frequency;
}
runtime.queueDisplayUpdate();
let cb = getResume();
AudioContextManager.tone(frequency, 1);
const v = pitchVolume / 0xff;
AudioContextManager.tone(frequency, v);
if (ms <= 0) cb();
else {
setTimeout(() => {
@ -129,4 +145,11 @@ namespace pxsim.pins {
}, ms);
}
}
export function pushButton(pinId: number) {
const b = board();
if (!b) return;
const ec = b.edgeConnectorState;
// TODO support buttons here
}
}

View File

@ -86,9 +86,11 @@ namespace pxsim {
export class EdgeConnectorState {
pins: Pin[];
pitchVolume: number;
constructor(public props: EdgeConnectorProps) {
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
this.pitchVolume = 0xff
}
public getPin(id: number) {

View File

@ -19,51 +19,52 @@ namespace pxsim {
}
export class Image extends RefObject {
public static height: number = 5;
public height: number;
public width: number;
public data: number[];
constructor(width: number, data: number[]) {
super();
this.width = width;
this.data = data;
this.height = (this.data.length / this.width) | 0;
}
public print() {
console.debug(`Image id:${this.id} size:${this.width}x${Image.height}`)
console.debug(`Image id:${this.id} size:${this.width}x${this.height}`)
}
public get(x: number, y: number): number {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return 0;
x = x | 0;
y = y | 0;
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return 0;
return this.data[y * this.width + x];
}
public set(x: number, y: number, v: number) {
x = x >> 0;
y = y >> 0;
if (x < 0 || x >= this.width || y < 0 || y >= 5) return;
x = x | 0;
y = y | 0;
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
this.data[y * this.width + x] = Math.max(0, Math.min(255, v));
}
public copyTo(xSrcIndex: number, length: number, target: Image, xTargetIndex: number): void {
xSrcIndex = xSrcIndex >> 0;
length = length >> 0;
xTargetIndex = xTargetIndex >> 0;
xSrcIndex = xSrcIndex | 0;
length = length | 0;
xTargetIndex = xTargetIndex | 0;
for (let x = 0; x < length; x++) {
for (let y = 0; y < 5; y++) {
for (let y = 0; y < this.height; y++) {
let value = this.get(xSrcIndex + x, y);
target.set(xTargetIndex + x, y, value);
}
}
}
public shiftLeft(cols: number) {
cols = cols >> 0;
cols = cols | 0;
for (let x = 0; x < this.width; ++x)
for (let y = 0; y < 5; ++y)
for (let y = 0; y < this.height; ++y)
this.set(x, y, x < this.width - cols ? this.get(x + cols, y) : 0);
}
public shiftRight(cols: number) {
cols = cols >> 0;
for (let x = this.width - 1; x >= 0; --x)
for (let y = 0; y < 5; ++y)
for (let y = 0; y < this.height; ++y)
this.set(x, y, x >= cols ? this.get(x - cols, y) : 0);
}
@ -85,7 +86,7 @@ namespace pxsim {
}
export function createImageFromBuffer(data: number[]): Image {
return new Image(data.length / 5, data);
return new Image((data.length / 5) | 0, data);
}
export function createImageFromString(text: string): Image {
@ -110,15 +111,14 @@ namespace pxsim {
return sprite;
}
export const FONT_DATA = [0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x0, 0x8, 0xa, 0x4a, 0x40, 0x0, 0x0, 0xa, 0x5f, 0xea, 0x5f, 0xea, 0xe, 0xd9, 0x2e, 0xd3, 0x6e, 0x19, 0x32, 0x44, 0x89, 0x33, 0xc, 0x92, 0x4c, 0x92, 0x4d, 0x8, 0x8, 0x0, 0x0, 0x0, 0x4, 0x88, 0x8, 0x8, 0x4, 0x8, 0x4, 0x84, 0x84, 0x88, 0x0, 0xa, 0x44, 0x8a, 0x40, 0x0, 0x4, 0x8e, 0xc4, 0x80, 0x0, 0x0, 0x0, 0x4, 0x88, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x22, 0x44, 0x88, 0x10, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x4, 0x8c, 0x84, 0x84, 0x8e, 0x1c, 0x82, 0x4c, 0x90, 0x1e, 0x1e, 0xc2, 0x44, 0x92, 0x4c, 0x6, 0xca, 0x52, 0x5f, 0xe2, 0x1f, 0xf0, 0x1e, 0xc1, 0x3e, 0x2, 0x44, 0x8e, 0xd1, 0x2e, 0x1f, 0xe2, 0x44, 0x88, 0x10, 0xe, 0xd1, 0x2e, 0xd1, 0x2e, 0xe, 0xd1, 0x2e, 0xc4, 0x88, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x4, 0x80, 0x4, 0x88, 0x2, 0x44, 0x88, 0x4, 0x82, 0x0, 0xe, 0xc0, 0xe, 0xc0, 0x8, 0x4, 0x82, 0x44, 0x88, 0xe, 0xd1, 0x26, 0xc0, 0x4, 0xe, 0xd1, 0x35, 0xb3, 0x6c, 0xc, 0x92, 0x5e, 0xd2, 0x52, 0x1c, 0x92, 0x5c, 0x92, 0x5c, 0xe, 0xd0, 0x10, 0x10, 0xe, 0x1c, 0x92, 0x52, 0x52, 0x5c, 0x1e, 0xd0, 0x1c, 0x90, 0x1e, 0x1e, 0xd0, 0x1c, 0x90, 0x10, 0xe, 0xd0, 0x13, 0x71, 0x2e, 0x12, 0x52, 0x5e, 0xd2, 0x52, 0x1c, 0x88, 0x8, 0x8, 0x1c, 0x1f, 0xe2, 0x42, 0x52, 0x4c, 0x12, 0x54, 0x98, 0x14, 0x92, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x11, 0x3b, 0x75, 0xb1, 0x31, 0x11, 0x39, 0x35, 0xb3, 0x71, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x1c, 0x92, 0x5c, 0x90, 0x10, 0xc, 0x92, 0x52, 0x4c, 0x86, 0x1c, 0x92, 0x5c, 0x92, 0x51, 0xe, 0xd0, 0xc, 0x82, 0x5c, 0x1f, 0xe4, 0x84, 0x84, 0x84, 0x12, 0x52, 0x52, 0x52, 0x4c, 0x11, 0x31, 0x31, 0x2a, 0x44, 0x11, 0x31, 0x35, 0xbb, 0x71, 0x12, 0x52, 0x4c, 0x92, 0x52, 0x11, 0x2a, 0x44, 0x84, 0x84, 0x1e, 0xc4, 0x88, 0x10, 0x1e, 0xe, 0xc8, 0x8, 0x8, 0xe, 0x10, 0x8, 0x4, 0x82, 0x41, 0xe, 0xc2, 0x42, 0x42, 0x4e, 0x4, 0x8a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x8, 0x4, 0x80, 0x0, 0x0, 0x0, 0xe, 0xd2, 0x52, 0x4f, 0x10, 0x10, 0x1c, 0x92, 0x5c, 0x0, 0xe, 0xd0, 0x10, 0xe, 0x2, 0x42, 0x4e, 0xd2, 0x4e, 0xc, 0x92, 0x5c, 0x90, 0xe, 0x6, 0xc8, 0x1c, 0x88, 0x8, 0xe, 0xd2, 0x4e, 0xc2, 0x4c, 0x10, 0x10, 0x1c, 0x92, 0x52, 0x8, 0x0, 0x8, 0x8, 0x8, 0x2, 0x40, 0x2, 0x42, 0x4c, 0x10, 0x14, 0x98, 0x14, 0x92, 0x8, 0x8, 0x8, 0x8, 0x6, 0x0, 0x1b, 0x75, 0xb1, 0x31, 0x0, 0x1c, 0x92, 0x52, 0x52, 0x0, 0xc, 0x92, 0x52, 0x4c, 0x0, 0x1c, 0x92, 0x5c, 0x90, 0x0, 0xe, 0xd2, 0x4e, 0xc2, 0x0, 0xe, 0xd0, 0x10, 0x10, 0x0, 0x6, 0xc8, 0x4, 0x98, 0x8, 0x8, 0xe, 0xc8, 0x7, 0x0, 0x12, 0x52, 0x52, 0x4f, 0x0, 0x11, 0x31, 0x2a, 0x44, 0x0, 0x11, 0x31, 0x35, 0xbb, 0x0, 0x12, 0x4c, 0x8c, 0x92, 0x0, 0x11, 0x2a, 0x44, 0x98, 0x0, 0x1e, 0xc4, 0x88, 0x1e, 0x6, 0xc4, 0x8c, 0x84, 0x86, 0x8, 0x8, 0x8, 0x8, 0x8, 0x18, 0x8, 0xc, 0x88, 0x18, 0x0, 0x0, 0xc, 0x83, 0x60];
export function createFont(): Image {
const data = [0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x0, 0x8, 0xa, 0x4a, 0x40, 0x0, 0x0, 0xa, 0x5f, 0xea, 0x5f, 0xea, 0xe, 0xd9, 0x2e, 0xd3, 0x6e, 0x19, 0x32, 0x44, 0x89, 0x33, 0xc, 0x92, 0x4c, 0x92, 0x4d, 0x8, 0x8, 0x0, 0x0, 0x0, 0x4, 0x88, 0x8, 0x8, 0x4, 0x8, 0x4, 0x84, 0x84, 0x88, 0x0, 0xa, 0x44, 0x8a, 0x40, 0x0, 0x4, 0x8e, 0xc4, 0x80, 0x0, 0x0, 0x0, 0x4, 0x88, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x22, 0x44, 0x88, 0x10, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x4, 0x8c, 0x84, 0x84, 0x8e, 0x1c, 0x82, 0x4c, 0x90, 0x1e, 0x1e, 0xc2, 0x44, 0x92, 0x4c, 0x6, 0xca, 0x52, 0x5f, 0xe2, 0x1f, 0xf0, 0x1e, 0xc1, 0x3e, 0x2, 0x44, 0x8e, 0xd1, 0x2e, 0x1f, 0xe2, 0x44, 0x88, 0x10, 0xe, 0xd1, 0x2e, 0xd1, 0x2e, 0xe, 0xd1, 0x2e, 0xc4, 0x88, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x4, 0x80, 0x4, 0x88, 0x2, 0x44, 0x88, 0x4, 0x82, 0x0, 0xe, 0xc0, 0xe, 0xc0, 0x8, 0x4, 0x82, 0x44, 0x88, 0xe, 0xd1, 0x26, 0xc0, 0x4, 0xe, 0xd1, 0x35, 0xb3, 0x6c, 0xc, 0x92, 0x5e, 0xd2, 0x52, 0x1c, 0x92, 0x5c, 0x92, 0x5c, 0xe, 0xd0, 0x10, 0x10, 0xe, 0x1c, 0x92, 0x52, 0x52, 0x5c, 0x1e, 0xd0, 0x1c, 0x90, 0x1e, 0x1e, 0xd0, 0x1c, 0x90, 0x10, 0xe, 0xd0, 0x13, 0x71, 0x2e, 0x12, 0x52, 0x5e, 0xd2, 0x52, 0x1c, 0x88, 0x8, 0x8, 0x1c, 0x1f, 0xe2, 0x42, 0x52, 0x4c, 0x12, 0x54, 0x98, 0x14, 0x92, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x11, 0x3b, 0x75, 0xb1, 0x31, 0x11, 0x39, 0x35, 0xb3, 0x71, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x1c, 0x92, 0x5c, 0x90, 0x10, 0xc, 0x92, 0x52, 0x4c, 0x86, 0x1c, 0x92, 0x5c, 0x92, 0x51, 0xe, 0xd0, 0xc, 0x82, 0x5c, 0x1f, 0xe4, 0x84, 0x84, 0x84, 0x12, 0x52, 0x52, 0x52, 0x4c, 0x11, 0x31, 0x31, 0x2a, 0x44, 0x11, 0x31, 0x35, 0xbb, 0x71, 0x12, 0x52, 0x4c, 0x92, 0x52, 0x11, 0x2a, 0x44, 0x84, 0x84, 0x1e, 0xc4, 0x88, 0x10, 0x1e, 0xe, 0xc8, 0x8, 0x8, 0xe, 0x10, 0x8, 0x4, 0x82, 0x41, 0xe, 0xc2, 0x42, 0x42, 0x4e, 0x4, 0x8a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x8, 0x4, 0x80, 0x0, 0x0, 0x0, 0xe, 0xd2, 0x52, 0x4f, 0x10, 0x10, 0x1c, 0x92, 0x5c, 0x0, 0xe, 0xd0, 0x10, 0xe, 0x2, 0x42, 0x4e, 0xd2, 0x4e, 0xc, 0x92, 0x5c, 0x90, 0xe, 0x6, 0xc8, 0x1c, 0x88, 0x8, 0xe, 0xd2, 0x4e, 0xc2, 0x4c, 0x10, 0x10, 0x1c, 0x92, 0x52, 0x8, 0x0, 0x8, 0x8, 0x8, 0x2, 0x40, 0x2, 0x42, 0x4c, 0x10, 0x14, 0x98, 0x14, 0x92, 0x8, 0x8, 0x8, 0x8, 0x6, 0x0, 0x1b, 0x75, 0xb1, 0x31, 0x0, 0x1c, 0x92, 0x52, 0x52, 0x0, 0xc, 0x92, 0x52, 0x4c, 0x0, 0x1c, 0x92, 0x5c, 0x90, 0x0, 0xe, 0xd2, 0x4e, 0xc2, 0x0, 0xe, 0xd0, 0x10, 0x10, 0x0, 0x6, 0xc8, 0x4, 0x98, 0x8, 0x8, 0xe, 0xc8, 0x7, 0x0, 0x12, 0x52, 0x52, 0x4f, 0x0, 0x11, 0x31, 0x2a, 0x44, 0x0, 0x11, 0x31, 0x35, 0xbb, 0x0, 0x12, 0x4c, 0x8c, 0x92, 0x0, 0x11, 0x2a, 0x44, 0x98, 0x0, 0x1e, 0xc4, 0x88, 0x1e, 0x6, 0xc4, 0x8c, 0x84, 0x86, 0x8, 0x8, 0x8, 0x8, 0x8, 0x18, 0x8, 0xc, 0x88, 0x18, 0x0, 0x0, 0xc, 0x83, 0x60];
let nb = data.length;
let nb = FONT_DATA.length;
let n = nb / 5;
let font = createInternalImage(nb);
for (let c = 0; c < n; c++) {
for (let row = 0; row < 5; row++) {
let char = data[c * 5 + row];
let char = FONT_DATA[c * 5 + row];
for (let col = 0; col < 5; col++) {
if ((char & (1 << col)) != 0)
font.set((c * 5 + 4) - col, row, 255);
@ -129,6 +129,24 @@ namespace pxsim {
}
}
namespace pxsim.fonts {
export function charCodeBuffer(charCode: number): RefBuffer {
if (charCode < DAL.MICROBIT_FONT_ASCII_START || charCode > DAL.MICROBIT_FONT_ASCII_END)
return undefined;
const b = board();
const led = b.ledMatrixState;
const font = led.font;
const h = font.height;
const w = font.width;
const buf = control.createBuffer(h);
const offset = (charCode - DAL.MICROBIT_FONT_ASCII_START) * h;
for (let row = 0; row < h; ++row)
buf.data[row] = FONT_DATA[offset + row];
return buf;
}
}
namespace pxsim.images {
export function createImage(img: Image) {
return img
@ -177,7 +195,7 @@ namespace pxsim.ImageMethods {
export function height(leds: Image): number {
pxtrt.nullCheck(leds)
return Image.height;
return leds.height;
}
export function width(leds: Image): number {
@ -186,11 +204,11 @@ namespace pxsim.ImageMethods {
}
export function plotFrame(leds: Image, frame: number) {
ImageMethods.plotImage(leds, frame * Image.height);
ImageMethods.plotImage(leds, frame * leds.height);
}
export function showFrame(leds: Image, frame: number, interval: number) {
ImageMethods.showImage(leds, frame * Image.height, interval);
ImageMethods.showImage(leds, frame * leds.height, interval);
}
export function pixel(leds: Image, x: number, y: number): number {
@ -268,7 +286,7 @@ namespace pxsim.ImageMethods {
namespace pxsim.basic {
export function showNumber(x: number, interval: number) {
interval = interval >> 0;
interval |= 0;
if (interval <= 0)
interval = 1;
let leds = createImageFromString(x.toString());
@ -277,7 +295,7 @@ namespace pxsim.basic {
}
export function showString(s: string, interval: number) {
interval = interval >> 0;
interval |= 0;
if (interval <= 0)
interval = 1;
if (s.length == 0) {
@ -291,6 +309,7 @@ namespace pxsim.basic {
}
export function showLeds(leds: Image, interval: number): void {
interval |= 0;
showAnimation(leds, interval);
}
@ -300,6 +319,7 @@ namespace pxsim.basic {
}
export function showAnimation(leds: Image, interval: number): void {
interval |= 0;
ImageMethods.scrollImage(leds, 5, interval);
}
@ -310,13 +330,17 @@ namespace pxsim.basic {
namespace pxsim.led {
export function plot(x: number, y: number) {
x |= 0;
y |= 0;
board().ledMatrixState.image.set(x, y, 0xff);
runtime.queueDisplayUpdate()
}
export function plotBrightness(x: number, y: number, brightness: number) {
x |= 0;
y |= 0;
const state = board().ledMatrixState;
brightness = brightness >> 0;
brightness |= 0;
brightness = Math.max(0, Math.min(led.brightness(), brightness));
if (brightness != 0 && brightness != 0xff && state.displayMode != DisplayMode.greyscale)
state.displayMode = DisplayMode.greyscale;
@ -325,12 +349,16 @@ namespace pxsim.led {
}
export function unplot(x: number, y: number) {
x |= 0;
y |= 0;
board().ledMatrixState.image.set(x, y, 0);
runtime.queueDisplayUpdate()
}
export function point(x: number, y: number): boolean {
return !!board().ledMatrixState.image.get(x, y);
export function pointBrightness(x: number, y: number): number {
x |= 0;
y |= 0;
return board().ledMatrixState.image.get(x, y);
}
export function brightness(): number {
@ -338,7 +366,7 @@ namespace pxsim.led {
}
export function setBrightness(value: number): void {
value = value >> 0;
value |= 0;
board().ledMatrixState.brigthness = Math.max(0, Math.min(255, value));
runtime.queueDisplayUpdate()
}

View File

@ -136,6 +136,10 @@ namespace pxsim.pins {
return 0;
}
export function spiTransfer(cmd: RefBuffer, resp: RefBuffer): void {
// TODO
}
export function spiFrequency(f: number): void {
// TODO
}
@ -219,7 +223,7 @@ namespace pxsim.bluetooth {
}
export function uartReadBuffer(): RefBuffer {
return pins.createBuffer(0);
return pins.createBuffer(0);
}
export function uartReadUntil(del: string): string {
@ -241,3 +245,14 @@ namespace pxsim.bluetooth {
export function setTransmitPower(power: number) { }
}
namespace pxsim.light {
export function sendWS2812Buffer(buffer: RefBuffer, pin: number) {
pxsim.sendBufferAsm(buffer, pin)
}
export function setMode(pin: number, mode: number) {
const lp = neopixelState(pin);
if (!lp) return;
lp.mode = mode & 0xff;
}
}

View File

@ -1,13 +0,0 @@
namespace pxsim {
export function sendBufferAsm(buffer: RefBuffer, pin: DigitalPin) {
let b = board();
if (b) {
let np = b.neopixelState;
if (np) {
let buf = buffer.data;
np.updateBuffer(buf as any, pin); // TODO this is wrong
runtime.queueDisplayUpdate();
}
}
}
}

View File

@ -73,8 +73,13 @@ namespace pxsim.serial {
}
export function readBuffer(length: number) {
length |= 0;
if (length <= 0)
length = 64;
return pins.createBuffer(length);
}
export function setBaudRate(rate: number) {
// TODO
}
}

View File

@ -16,6 +16,8 @@
"*.ts",
"state/*.ts",
"visuals/*.ts",
"../node_modules/pxt-common-packages/libs/core/sim/analogSensor.ts",
"../node_modules/pxt-common-packages/libs/core/sim/neopixel.ts",
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts"
]
}

View File

@ -3,8 +3,7 @@ namespace pxsim.visuals {
return new visuals.MicrobitBoardSvg({
runtime: runtime,
theme: visuals.randomTheme(),
disableTilt: false,
wireframe: opts.wireframe,
wireframe: opts.wireframe
});
}
}
}

View File

@ -140,9 +140,31 @@ namespace pxsim.visuals {
stroke: none;
fill: #777;
}
.sim-label, .sim-button-label {
fill: #000;
}
.sim-wireframe .sim-board {
stroke-width: 2px;
}
*:focus {
outline: none;
}
*:focus .sim-button-outer,
.sim-pin:focus,
.sim-thermometer:focus,
.sim-shake:focus,
.sim-light-level-button:focus {
stroke: #4D90FE;
stroke-width: 5px !important;
}
.no-drag, .sim-text, .sim-text-pin {
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
`;
const BOARD_SVG = `<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
@ -692,8 +714,8 @@ namespace pxsim.visuals {
export interface IBoardProps {
runtime?: pxsim.Runtime;
theme?: IBoardTheme;
disableTilt?: boolean;
wireframe?: boolean;
disableTilt?: boolean;
}
export class MicrobitBoardSvg implements BoardView {
@ -711,6 +733,7 @@ namespace pxsim.visuals {
private leds: SVGElement[];
private systemLed: SVGCircleElement;
private antenna: SVGPolylineElement;
private rssi: SVGTextElement;
private lightLevelButton: SVGCircleElement;
private lightLevelGradient: SVGLinearGradientElement;
private lightLevelText: SVGTextElement;
@ -925,6 +948,7 @@ namespace pxsim.visuals {
};
constructor(public props: IBoardProps) {
this.buildDom();
if (props && props.wireframe)
U.addClass(this.element, "sim-wireframe");
@ -968,7 +992,6 @@ namespace pxsim.visuals {
const r = p.getBoundingClientRect();
this.pinNmToCoord[nm] = [r.left + r.width / 2, r.top + r.height / 2];
});
console.log(JSON.stringify(this.pinNmToCoord, null, 2))
}
private updateTheme() {
@ -981,6 +1004,7 @@ namespace pxsim.visuals {
svg.fill(this.buttons[1], theme.buttonUps[1]);
svg.fill(this.buttonsOuter[2], theme.virtualButtonOuter);
svg.fill(this.buttons[2], theme.virtualButtonUp);
if (this.shakeButton) svg.fill(this.shakeButton, theme.virtualButtonUp);
this.pinGradients.forEach(lg => svg.setGradientColors(lg, theme.pin, theme.pinActive));
svg.setGradientColors(this.lightLevelGradient, theme.lightLevelOn, theme.lightLevelOff);
@ -1031,6 +1055,7 @@ namespace pxsim.visuals {
this.updateGestures();
this.updateRgbLed();
this.updateSpeaker();
this.updateRSSI();
if (!runtime || runtime.dead) U.addClass(this.element, "grayscale");
else U.removeClass(this.element, "grayscale");
@ -1092,6 +1117,33 @@ namespace pxsim.visuals {
}
}
private updateRSSI() {
let state = this.board;
if (!state) return;
const v = state.radioState.datagram.rssi;
if (v === undefined) return;
if (!this.rssi) {
let ax = 380;
let dax = 18;
let ayt = 10;
let ayb = 40;
const wh = dax * 5;
for (let i = 0; i < 4; ++i)
svg.child(this.g, "rect", { x: ax - 90 + i * 6, y: ayt + 28 - i * 4, width: 4, height: 2 + i * 4, fill: "#fff" })
this.rssi = svg.child(this.g, "text", { x: ax - 64, y: ayb, class: "sim-text" }) as SVGTextElement;
this.rssi.textContent = "";
}
const vt = v.toString();
if (vt !== this.rssi.textContent) {
this.rssi.textContent = v.toString();
this.antenna.setAttribute("aria-valuenow", this.rssi.textContent);
accessibility.setLiveContent(this.rssi.textContent);
}
}
private updatePin(pin: Pin, index: number) {
if (!pin) return;
let text = this.pinTexts[pin.id];
@ -1106,12 +1158,22 @@ namespace pxsim.visuals {
}
else if (pin.mode & PinFlags.Touch) {
v = pin.touched ? "0%" : "100%";
if (text) text.textContent = "TOUCHED";
if (text) text.textContent = "";
} else {
v = "100%";
if (text) text.textContent = "unused";
if (text) text.textContent = "";
}
if (v) svg.setGradientValue(this.pinGradients[index], v);
if (pin.mode !== PinFlags.Unused) {
accessibility.makeFocusable(this.pins[index]);
accessibility.setAria(this.pins[index], "slider", this.pins[index].firstChild.textContent);
this.pins[index].setAttribute("aria-valuemin", "0");
this.pins[index].setAttribute("aria-valuemax", pin.mode & PinFlags.Analog ? "1023" : "100");
this.pins[index].setAttribute("aria-orientation", "vertical");
this.pins[index].setAttribute("aria-valuenow", text ? text.textContent : v);
accessibility.setLiveContent(text ? text.textContent : v);
}
}
private updateTemperature() {

View File

@ -1,242 +0,0 @@
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../../libs/core/dal.d.ts"/>
/// <reference path="../../libs/core/enums.d.ts"/>
namespace pxsim.visuals {
const PIXEL_SPACING = PIN_DIST * 3;
const PIXEL_RADIUS = PIN_DIST;
const CANVAS_WIDTH = 1.2 * PIN_DIST;
const CANVAS_HEIGHT = 12 * PIN_DIST;
const CANVAS_VIEW_WIDTH = CANVAS_WIDTH;
const CANVAS_VIEW_HEIGHT = CANVAS_HEIGHT;
const CANVAS_VIEW_PADDING = PIN_DIST * 4;
const CANVAS_LEFT = 1.4 * PIN_DIST;
const CANVAS_TOP = PIN_DIST;
// For the instructions parts list
export function mkNeoPixelPart(xy: Coord = [0, 0]): SVGElAndSize {
const NP_PART_XOFF = -13.5;
const NP_PART_YOFF = -11;
const NP_PART_WIDTH = 87.5;
const NP_PART_HEIGHT = 190;
const NEOPIXEL_PART_IMG = `<svg viewBox="-5 -1 53 112" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com">
<rect x="2.5" width="38" height="100" style="fill: rgb(68, 68, 68);"/>
<rect x="11.748" y="3.2" width="1.391" height="2.553" style="fill: none; stroke-linejoin: round; stroke-width: 3; stroke: rgb(165, 103, 52);"/>
<rect x="20.75" y="3.2" width="1.391" height="2.553" style="fill: none; stroke-linejoin: round; stroke-width: 3; stroke: rgb(165, 103, 52);"/>
<rect x="29.75" y="3.2" width="1.391" height="2.553" style="fill: none; stroke-linejoin: round; stroke-width: 3; stroke: rgb(165, 103, 52);"/>
<g>
<rect x="9" y="16.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="22.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="28.563" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="11.607" y="14.833" width="19.787" height="18.697" style="fill: rgb(0, 0, 0);"/>
<ellipse style="fill: rgb(216, 216, 216);" cx="21.5" cy="24.181" rx="7" ry="7"/>
</g>
<path d="M -7.25 -103.2 L -2.5 -100.003 L -12 -100.003 L -7.25 -103.2 Z" style="fill: rgb(68, 68, 68);" transform="matrix(-1, 0, 0, -1, 0, 0)" bx:shape="triangle -12 -103.2 9.5 3.197 0.5 0 1@ad6f5cac"/>
<path d="M -16.75 -103.197 L -12 -100 L -21.5 -100 L -16.75 -103.197 Z" style="fill: rgb(68, 68, 68);" transform="matrix(-1, 0, 0, -1, 0, 0)" bx:shape="triangle -21.5 -103.197 9.5 3.197 0.5 0 1@07d73149"/>
<path d="M -26.25 -103.2 L -21.5 -100.003 L -31 -100.003 L -26.25 -103.2 Z" style="fill: rgb(68, 68, 68);" transform="matrix(-1, 0, 0, -1, 0, 0)" bx:shape="triangle -31 -103.2 9.5 3.197 0.5 0 1@54403e2d"/>
<path d="M -35.75 -103.197 L -31 -100 L -40.5 -100 L -35.75 -103.197 Z" style="fill: rgb(68, 68, 68);" transform="matrix(-1, 0, 0, -1, 0, 0)" bx:shape="triangle -40.5 -103.197 9.5 3.197 0.5 0 1@21c9b772"/>
<g transform="matrix(1, 0, 0, 1, 0.000002, 29.999994)">
<rect x="9" y="16.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="22.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="28.563" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="11.607" y="14.833" width="19.787" height="18.697" style="fill: rgb(0, 0, 0);"/>
<ellipse style="fill: rgb(216, 216, 216);" cx="21.5" cy="24.181" rx="7" ry="7"/>
</g>
<g transform="matrix(1, 0, 0, 1, 0.000005, 59.999992)">
<rect x="9" y="16.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="22.562" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="9" y="28.563" width="25" height="3.238" style="fill: rgb(216, 216, 216);"/>
<rect x="11.607" y="14.833" width="19.787" height="18.697" style="fill: rgb(0, 0, 0);"/>
<ellipse style="fill: rgb(216, 216, 216);" cx="21.5" cy="24.181" rx="7" ry="7"/>
</g>
</svg>`;
let [x, y] = xy;
let l = x + NP_PART_XOFF;
let t = y + NP_PART_YOFF;
let w = NP_PART_WIDTH;
let h = NP_PART_HEIGHT;
let img = <SVGImageElement>svg.elt("image");
svg.hydrate(img, {
class: "sim-neopixel-strip", x: l, y: t, width: w, height: h,
href: svg.toDataUri(NEOPIXEL_PART_IMG)
});
return { el: img, x: l, y: t, w: w, h: h };
}
export class NeoPixel {
public el: SVGElement;
public cy: number;
constructor(xy: Coord = [0, 0]) {
let el = <SVGElement>svg.elt("rect");
let r = PIXEL_RADIUS;
let [cx, cy] = xy;
let y = cy - r;
svg.hydrate(el, { x: "-50%", y: y, width: "100%", height: r * 2, class: "sim-neopixel" });
this.el = el;
this.cy = cy;
}
public setRgb(rgb: [number, number, number]) {
let hsl = visuals.rgbToHsl(rgb);
let [h, s, l] = hsl;
// at least 70% luminosity
l = Math.max(l, 60);
let fill = `hsl(${h}, ${s}%, ${l}%)`;
this.el.setAttribute("fill", fill);
}
}
export class NeoPixelCanvas {
public canvas: SVGSVGElement;
public pin: number;
public pixels: NeoPixel[];
private viewBox: [number, number, number, number];
private background: SVGRectElement;
constructor(pin: number) {
this.pixels = [];
this.pin = pin;
let el = <SVGSVGElement>svg.elt("svg");
svg.hydrate(el, {
"class": `sim-neopixel-canvas`,
"x": "0px",
"y": "0px",
"width": `${CANVAS_WIDTH}px`,
"height": `${CANVAS_HEIGHT}px`,
});
this.canvas = el;
this.background = <SVGRectElement>svg.child(el, "rect", { class: "sim-neopixel-background hidden" });
this.updateViewBox(-CANVAS_VIEW_WIDTH / 2, 0, CANVAS_VIEW_WIDTH, CANVAS_VIEW_HEIGHT);
}
private updateViewBox(x: number, y: number, w: number, h: number) {
this.viewBox = [x, y, w, h];
svg.hydrate(this.canvas, { "viewBox": `${x} ${y} ${w} ${h}` });
svg.hydrate(this.background, { "x": x, "y": y, "width": w, "height": h });
}
public update(colors: RGBW[]) {
if (!colors || colors.length <= 0)
return;
for (let i = 0; i < colors.length; i++) {
let pixel = this.pixels[i];
if (!pixel) {
let cxy: Coord = [0, CANVAS_VIEW_PADDING + i * PIXEL_SPACING];
pixel = this.pixels[i] = new NeoPixel(cxy);
svg.hydrate(pixel.el, { title: `offset: ${i}` });
this.canvas.appendChild(pixel.el);
}
let color = colors[i];
pixel.setRgb([color[0], color[1], color[2]]);
}
//show the canvas if it's hidden
U.removeClass(this.background, "hidden");
//resize if necessary
let [first, last] = [this.pixels[0], this.pixels[this.pixels.length - 1]]
let yDiff = last.cy - first.cy;
let newH = yDiff + CANVAS_VIEW_PADDING * 2;
let [oldX, oldY, oldW, oldH] = this.viewBox;
if (oldH < newH) {
let scalar = newH / oldH;
let newW = oldW * scalar;
this.updateViewBox(-newW / 2, oldY, newW, newH);
}
}
public setLoc(xy: Coord) {
let [x, y] = xy;
svg.hydrate(this.canvas, { x: x, y: y });
}
};
function digitalPinToPinNumber(gpioPin: string): number {
const MICROBIT_ID_IO_P0 = 7; //TODO: don't hardcode this, import enums.d.ts
if (gpioPin == "*") {
return MICROBIT_ID_IO_P0;
}
let pinSplit = gpioPin.split("DigitalPin.P");
U.assert(pinSplit.length === 2, "Unknown format for pin (for NeoPixel): " + gpioPin);
let pinNumStr = pinSplit[1];
let pinNum = Number(pinNumStr) + MICROBIT_ID_IO_P0;
return pinNum
}
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
const modeMap: Map<NeoPixelMode> = {
"NeoPixelMode.RGB": NeoPixelMode.RGB,
"NeoPixelMode.RGBW": NeoPixelMode.RGBW
};
return modeMap[modeStr] || NeoPixelMode.RGB;
}
export class NeoPixelView implements IBoardPart<NeoPixelState> {
public style: string = `
.sim-neopixel-canvas {
}
.sim-neopixel-canvas-parent:hover {
transform-origin: center;
transform: scale(4) translateY(-60px);
-moz-transform: scale(4) translateY(-220px);
}
.sim-neopixel-canvas .hidden {
visibility:hidden;
}
.sim-neopixel-background {
fill: rgba(255,255,255,0.9);
}
.sim-neopixel-strip {
}
`;
public element: SVGElement;
public overElement: SVGElement;
public defs: SVGElement[];
private state: NeoPixelState;
private canvas: NeoPixelCanvas;
private part: SVGElAndSize;
private stripGroup: SVGGElement;
private lastLocation: Coord;
private pin: number;
private mode: NeoPixelMode;
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, otherParams: Map<string>): void {
U.assert(!!otherParams["mode"], "NeoPixels assumes a RGB vs RGBW mode is passed to it");
U.assert(!!otherParams["pin"], "NeoPixels assumes a pin is passed to it");
let modeStr = otherParams["mode"];
this.mode = parseNeoPixelMode(modeStr);
this.state = state;
this.stripGroup = <SVGGElement>svg.elt("g");
this.element = this.stripGroup;
let pinStr = otherParams["pin"];
this.pin = digitalPinToPinNumber(pinStr);
this.lastLocation = [0, 0];
let part = mkNeoPixelPart();
this.part = part;
this.stripGroup.appendChild(part.el);
let canvas = new NeoPixelCanvas(this.pin);
this.canvas = canvas;
let canvasG = svg.elt("g", { class: "sim-neopixel-canvas-parent" });
this.overElement = canvasG;
canvasG.appendChild(canvas.canvas);
this.updateStripLoc();
}
public moveToCoord(xy: Coord): void {
let [x, y] = xy;
let loc: Coord = [x, y];
this.lastLocation = loc;
this.updateStripLoc();
}
private updateStripLoc() {
let [x, y] = this.lastLocation;
U.assert(typeof x === "number" && typeof y === "number", "invalid x,y for NeoPixel strip");
this.canvas.setLoc([x + CANVAS_LEFT, y + CANVAS_TOP]);
svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
}
public updateState(): void {
let colors = this.state.getColors(this.pin, this.mode);
this.canvas.update(colors);
}
public updateTheme(): void { }
}
}