V4 updates for beta testing (#147)
* 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
* Fix Sound and replace AnalogPin.P0
* remove approved extensions
* V4 Updates from remote Repo
* locales
* add storage functions
* fix storage functions
* fix int/float values
* decrease decimal precision
* reorder blocks
* Update BLE Settings and Storage Blocks
* Fetch MicroBit changes up to v4.0.18
* Update timing for LED Matrix usage
* use 32kb ram (mini v2)
* resize gatt table
* Revert "use 32kb ram (mini v2)"
This reverts commit 4b15592f0f
.
* fix missleading indentation
* add support for 32kb and 16kb ram
* only MIT extensions in preferredRepos
* remove extensions without MIT License file
* add updated extensions
* add extensions with MIT license
Co-authored-by: Juri <gitkraken@juriwolf.de>
Co-authored-by: Juri <info@juriwolf.de>
This commit is contained in:
7
sim/compat.ts
Normal file
7
sim/compat.ts
Normal file
@ -0,0 +1,7 @@
|
||||
namespace pxsim {
|
||||
export interface CommonBoard extends CoreBoard
|
||||
, EventBusBoard {
|
||||
bus: EventBus;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
}
|
||||
}
|
@ -4,7 +4,11 @@
|
||||
|
||||
namespace pxsim {
|
||||
export class DalBoard extends CoreBoard
|
||||
implements RadioBoard, LightBoard {
|
||||
implements CommonBoard
|
||||
, RadioBoard
|
||||
, LightBoard
|
||||
, MicrophoneBoard
|
||||
, ControlMessageBoard {
|
||||
// state & update logic for component services
|
||||
ledMatrixState: LedMatrixState;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
@ -20,17 +24,24 @@ namespace pxsim {
|
||||
rgbLedState: number;
|
||||
speakerState: SpeakerState;
|
||||
fileSystem: FileSystemState;
|
||||
logoTouch: Button;
|
||||
speakerEnabled: boolean = true;
|
||||
controlMessageState: ControlMessageState;
|
||||
|
||||
// visual
|
||||
viewHost: visuals.BoardHost;
|
||||
view: SVGElement;
|
||||
|
||||
// board hardware version
|
||||
hardwareVersion = 1;
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
// components
|
||||
this.lightState = {};
|
||||
this.fileSystem = new FileSystemState();
|
||||
this.controlMessageState = new ControlMessageState(this);
|
||||
this.builtinParts["ledmatrix"] = this.ledMatrixState = new LedMatrixState(runtime);
|
||||
this.builtinParts["buttonpair"] = this.buttonPairState = new ButtonPairState({
|
||||
ID_BUTTON_A: DAL.MICROBIT_ID_BUTTON_A,
|
||||
@ -41,8 +52,10 @@ namespace pxsim {
|
||||
});
|
||||
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
|
||||
pins: [
|
||||
DAL.MICROBIT_ID_IO_P12,
|
||||
DAL.MICROBIT_ID_IO_P0,
|
||||
DAL.MICROBIT_ID_IO_P1,
|
||||
DAL.MICROBIT_ID_IO_P16,
|
||||
DAL.MICROBIT_ID_IO_P2,
|
||||
DAL.MICROBIT_ID_IO_P3,
|
||||
DAL.MICROBIT_ID_IO_P4,
|
||||
@ -52,12 +65,10 @@ namespace pxsim {
|
||||
DAL.MICROBIT_ID_IO_P8,
|
||||
DAL.MICROBIT_ID_IO_P9,
|
||||
DAL.MICROBIT_ID_IO_P10,
|
||||
DAL.MICROBIT_ID_IO_P11,
|
||||
DAL.MICROBIT_ID_IO_P12,
|
||||
DAL.MICROBIT_ID_IO_P11,
|
||||
DAL.MICROBIT_ID_IO_P13,
|
||||
DAL.MICROBIT_ID_IO_P14,
|
||||
DAL.MICROBIT_ID_IO_P15,
|
||||
DAL.MICROBIT_ID_IO_P16,
|
||||
0,
|
||||
0,
|
||||
DAL.MICROBIT_ID_IO_P19,
|
||||
@ -71,13 +82,13 @@ namespace pxsim {
|
||||
"P3": DAL.MICROBIT_ID_IO_P16
|
||||
}
|
||||
});
|
||||
this.builtinParts["radio"] = this.radioState = new RadioState(runtime, {
|
||||
this.builtinParts["radio"] = this.radioState = new RadioState(runtime, this, {
|
||||
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["microphone"] = this.microphoneState = new AnalogSensorState(3001 /* DEVICE_ID_MICROPHONE */, 0, 255, 75, 180);
|
||||
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
|
||||
this.builtinParts["serial"] = this.serialState = new SerialState();
|
||||
this.builtinParts["serial"] = this.serialState = new SerialState(runtime, this);
|
||||
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
|
||||
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
|
||||
this.builtinParts["compass"] = this.compassState = new CompassState();
|
||||
@ -97,25 +108,14 @@ namespace pxsim {
|
||||
this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
|
||||
}
|
||||
|
||||
receiveMessage(msg: SimulatorMessage) {
|
||||
if (!runtime || runtime.dead) return;
|
||||
|
||||
switch (msg.type || "") {
|
||||
case "eventbus":
|
||||
const ev = <SimulatorEventBusMessage>msg;
|
||||
this.bus.queue(ev.id, ev.eventid, ev.value);
|
||||
break;
|
||||
case "serial":
|
||||
const data = (<SimulatorSerialMessage>msg).data || "";
|
||||
this.serialState.receiveData(data);
|
||||
break;
|
||||
case "radiopacket":
|
||||
const packet = <SimulatorRadioPacketMessage>msg;
|
||||
this.radioState.receivePacket(packet);
|
||||
break;
|
||||
ensureHardwareVersion(version: number) {
|
||||
if (version > this.hardwareVersion) {
|
||||
this.hardwareVersion = version;
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
initAsync(msg: SimulatorRunMessage): Promise<void> {
|
||||
super.initAsync(msg);
|
||||
|
||||
@ -124,6 +124,21 @@ namespace pxsim {
|
||||
const cmpDefs = msg.partDefinitions || {};
|
||||
const fnArgs = msg.fnArgs;
|
||||
|
||||
const v2Parts: pxt.Map<boolean> = {
|
||||
"microphone": true,
|
||||
"logotouch": true,
|
||||
"builtinspeaker": true,
|
||||
"v2": true
|
||||
};
|
||||
if (msg.builtinParts) {
|
||||
const v2PartsUsed = msg.builtinParts.filter(k => v2Parts[k])
|
||||
if (v2PartsUsed.length) {
|
||||
console.log(`detected v2 feature`, v2PartsUsed);
|
||||
cmpsList.push(...v2PartsUsed);
|
||||
this.hardwareVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const opts: visuals.BoardHostOpts = {
|
||||
state: this,
|
||||
boardDef: boardDef,
|
||||
@ -134,6 +149,7 @@ namespace pxsim {
|
||||
maxHeight: "100%",
|
||||
highContrast: msg.highContrast
|
||||
};
|
||||
|
||||
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
|
||||
visual: boardDef.visual,
|
||||
boardDef: boardDef,
|
||||
|
1
sim/public/icons/jacdac.svg
Normal file
1
sim/public/icons/jacdac.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#009900" stroke="none" d="M13.922 2.495V19.3a2.35 2.35 0 11-3.845 0V2.495c0-.826.67-1.495 1.495-1.495h.855c.825 0 1.495.67 1.495 1.495zm-1.923 16.723a1.431 1.431 0 100 2.863 1.431 1.431 0 000-2.863zm5.426.001a1.431 1.431 0 100 2.863 1.431 1.431 0 000-2.863zm1.922-14.587V19.3a2.35 2.35 0 11-3.844 0V4.632c0-.826.67-1.495 1.495-1.495h.854c.826 0 1.495.67 1.495 1.495zM7.002 1.002c.826 0 1.495.669 1.495 1.494V19.3a2.35 2.35 0 11-3.848.006l.004-.006V2.496C4.653 1.67 5.323 1 6.148 1zM6.575 19.22a1.431 1.431 0 100 2.862 1.431 1.431 0 000-2.862z"/></svg>
|
After Width: | Height: | Size: 646 B |
@ -1,9 +1,19 @@
|
||||
CACHE MANIFEST
|
||||
|
||||
CACHE:
|
||||
/cdn/bluebird.min.js
|
||||
/cdn/pxtsim.js
|
||||
/sim/sim.js
|
||||
|
||||
/sim/sounds/giggle.wav
|
||||
/sim/sounds/happy.wav
|
||||
/sim/sounds/hello.wav
|
||||
/sim/sounds/mysterious.wav
|
||||
/sim/sounds/sad.wav
|
||||
/sim/sounds/slide.wav
|
||||
/sim/sounds/soaring.wav
|
||||
/sim/sounds/spring.wav
|
||||
/sim/sounds/twinkle.wav
|
||||
/sim/sounds/yawn.wav
|
||||
|
||||
NETWORK:
|
||||
*
|
||||
|
@ -19,10 +19,22 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="/cdn/bluebird.min.js"></script>
|
||||
<script src="/cdn/pxtsim.js"></script>
|
||||
<script src="/sim/sim.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
pxsim.soundExpressionFiles = {
|
||||
"giggle": "/sim/sounds/giggle.wav",
|
||||
"happy": "/sim/sounds/happy.wav",
|
||||
"hello": "/sim/sounds/hello.wav",
|
||||
"mysterious": "/sim/sounds/mysterious.wav",
|
||||
"sad": "/sim/sounds/sad.wav",
|
||||
"slide": "/sim/sounds/slide.wav",
|
||||
"soaring": "/sim/sounds/soaring.wav",
|
||||
"spring": "/sim/sounds/spring.wav",
|
||||
"twinkle": "/sim/sounds/twinkle.wav",
|
||||
"yawn": "/sim/sounds/yawn.wav"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
BIN
sim/public/sounds/giggle.wav
Normal file
BIN
sim/public/sounds/giggle.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/happy.wav
Normal file
BIN
sim/public/sounds/happy.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/hello.wav
Normal file
BIN
sim/public/sounds/hello.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/mysterious.wav
Normal file
BIN
sim/public/sounds/mysterious.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/sad.wav
Normal file
BIN
sim/public/sounds/sad.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/slide.wav
Normal file
BIN
sim/public/sounds/slide.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/soaring.wav
Normal file
BIN
sim/public/sounds/soaring.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/spring.wav
Normal file
BIN
sim/public/sounds/spring.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/twinkle.wav
Normal file
BIN
sim/public/sounds/twinkle.wav
Normal file
Binary file not shown.
BIN
sim/public/sounds/yawn.wav
Normal file
BIN
sim/public/sounds/yawn.wav
Normal file
Binary file not shown.
@ -1,4 +1,14 @@
|
||||
namespace pxsim.input {
|
||||
export function onButtonEvent(button: number, buttonEvent: number, handler: RefAction): void {
|
||||
let b = board().buttonPairState;
|
||||
if (button == b.props.ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
b.usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
pxtcore.registerWithDal(button, buttonEvent, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onButtonPressed(button: number, handler: RefAction): void {
|
||||
let b = board().buttonPairState;
|
||||
if (button == b.props.ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
|
@ -8,8 +8,22 @@ namespace pxsim.input {
|
||||
return b.heading;
|
||||
}
|
||||
|
||||
export function assumeCalibrationCompass(){
|
||||
}
|
||||
|
||||
export function clearCalibrationCompass(){
|
||||
}
|
||||
|
||||
export function isCalibratedCompass(): boolean {
|
||||
// let b = board().compassState;
|
||||
// return b.isCalibrated;
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
export function magneticForce(): number {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +1,27 @@
|
||||
namespace pxsim.input {
|
||||
export function onPinTouchEvent(pinId: number, pinEvent: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, pinEvent, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onPinPressed(pinId: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_CLICK, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onPinReleased(pinId: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_UP, handler);
|
||||
}
|
||||
|
||||
@ -95,51 +105,68 @@ namespace pxsim.pins {
|
||||
}
|
||||
|
||||
export function analogSetPitchPin(pinId: number) {
|
||||
const b = board();
|
||||
if (!b) return;
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
board().edgeConnectorState.pins.filter(p => !!p).forEach(p => p.pitch = false);
|
||||
const ec = b.edgeConnectorState
|
||||
ec.pins.filter(p => !!p).forEach(p => p.pitch = false);
|
||||
pin.pitch = true;
|
||||
}
|
||||
|
||||
export function setSoundOutputPinEnabled(enabled: boolean) {
|
||||
const b = board();
|
||||
if (!b) return;
|
||||
const ec = b.edgeConnectorState
|
||||
ec.pitchEnabled = !enabled;
|
||||
}
|
||||
|
||||
export function analogSetPitchVolume(volume: number) {
|
||||
const ec = board().edgeConnectorState;
|
||||
ec.pitchVolume = Math.max(0, Math.min(0xff, volume | 0));
|
||||
AudioContextManager.setCurrentToneGain((ec.pitchVolume / 0xff) / 10);
|
||||
}
|
||||
|
||||
export function analogPitchVolume() {
|
||||
const ec = board().edgeConnectorState;
|
||||
return ec.pitchVolume;
|
||||
}
|
||||
|
||||
|
||||
export function analogPitch(frequency: number, ms: number) {
|
||||
// update analog output
|
||||
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 pin = ec.pitchEnabled && (pins.filter(pin => !!pin && pin.pitch)[0] || pins[0]);
|
||||
const pitchVolume = ec.pitchVolume | 0;
|
||||
pin.mode = PinFlags.Analog | PinFlags.Output;
|
||||
if (frequency <= 0 || pitchVolume <= 0) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
} else {
|
||||
const v = 1 << (pitchVolume >> 5);
|
||||
pin.value = v;
|
||||
pin.period = 1000000 / frequency;
|
||||
if (pin) {
|
||||
pin.mode = PinFlags.Analog | PinFlags.Output;
|
||||
if (frequency <= 0 || pitchVolume <= 0) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
} else {
|
||||
const v = 1 << (pitchVolume >> 5);
|
||||
pin.value = v;
|
||||
pin.period = 1000000 / frequency;
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
|
||||
let cb = getResume();
|
||||
const v = pitchVolume / 0xff;
|
||||
AudioContextManager.tone(frequency, v);
|
||||
if (pin) {
|
||||
const v = pitchVolume / 0xff;
|
||||
AudioContextManager.tone(frequency, v / 10);
|
||||
}
|
||||
if (ms <= 0) cb();
|
||||
else {
|
||||
setTimeout(() => {
|
||||
AudioContextManager.stop();
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
pin.mode = PinFlags.Unused;
|
||||
if (pin) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
pin.mode = PinFlags.Unused;
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
cb()
|
||||
}, ms);
|
||||
@ -152,4 +179,18 @@ namespace pxsim.pins {
|
||||
const ec = b.edgeConnectorState;
|
||||
// TODO support buttons here
|
||||
}
|
||||
}
|
||||
namespace pxsim.music {
|
||||
export function setVolume(volume: number): void {
|
||||
pxsim.pins.analogSetPitchVolume(volume);
|
||||
}
|
||||
export function volume(): number {
|
||||
return pxsim.pins.analogPitchVolume();
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pins {
|
||||
export function setAudioPin(pinId: number) {
|
||||
pxsim.pins.analogSetPitchPin(pinId);
|
||||
}
|
||||
}
|
@ -87,6 +87,7 @@ namespace pxsim {
|
||||
export class EdgeConnectorState {
|
||||
pins: Pin[];
|
||||
pitchVolume: number;
|
||||
pitchEnabled = true;
|
||||
|
||||
constructor(public props: EdgeConnectorProps) {
|
||||
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
|
||||
|
28
sim/state/microphone.ts
Normal file
28
sim/state/microphone.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// move to common packages eventually
|
||||
namespace pxsim.input {
|
||||
|
||||
export function soundLevel(): number {
|
||||
const b = microphoneState();
|
||||
if (!b) return 0;
|
||||
b.setUsed();
|
||||
return b.getLevel();
|
||||
}
|
||||
|
||||
export function onSound(sound: number /* SoundThreshold */, body: RefAction) {
|
||||
const b = microphoneState();
|
||||
if (!b) return;
|
||||
b.setUsed();
|
||||
pxtcore.registerWithDal(b.id, sound, body);
|
||||
}
|
||||
|
||||
export function setSoundThreshold(sound: number, threshold: number){
|
||||
const b = microphoneState();
|
||||
if (!b) return;
|
||||
b.setUsed();
|
||||
if (sound === 2 /* SoundThreshold.Loud */)
|
||||
b.setHighThreshold(threshold);
|
||||
else
|
||||
b.setLowThreshold(threshold);
|
||||
}
|
||||
|
||||
}
|
@ -35,46 +35,6 @@ namespace pxsim.basic {
|
||||
namespace pxsim.control {
|
||||
export var inBackground = thread.runInBackground;
|
||||
|
||||
export function createBuffer(sz: number) {
|
||||
return pxsim.BufferMethods.createBuffer(sz)
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
const cb = getResume();
|
||||
pxsim.runtime.restart();
|
||||
}
|
||||
|
||||
export function waitMicros(micros: number) {
|
||||
// TODO
|
||||
}
|
||||
export function waitForEvent(id: number, evid: number) {
|
||||
const cb = getResume();
|
||||
board().bus.wait(id, evid, cb);
|
||||
}
|
||||
|
||||
export function millis(): number {
|
||||
return runtime.runningTime();
|
||||
}
|
||||
|
||||
export function micros(): number {
|
||||
return runtime.runningTimeUs();
|
||||
}
|
||||
|
||||
|
||||
export function deviceName(): string {
|
||||
let b = board();
|
||||
return b && b.id
|
||||
? b.id.slice(0, 4)
|
||||
: "abcd";
|
||||
}
|
||||
|
||||
export function deviceSerialNumber(): number {
|
||||
let b = board();
|
||||
return parseInt(b && b.id
|
||||
? b.id.slice(1)
|
||||
: "42");
|
||||
}
|
||||
|
||||
export function onEvent(id: number, evid: number, handler: RefAction) {
|
||||
if (id == DAL.MICROBIT_ID_BUTTON_AB) {
|
||||
const b = board().buttonPairState;
|
||||
@ -86,11 +46,6 @@ namespace pxsim.control {
|
||||
pxtcore.registerWithDal(id, evid, handler)
|
||||
}
|
||||
|
||||
export function raiseEvent(id: number, evid: number, mode: number) {
|
||||
// TODO mode?
|
||||
board().bus.queue(id, evid)
|
||||
}
|
||||
|
||||
export function eventTimestamp() {
|
||||
return board().bus.getLastEventTime()
|
||||
}
|
||||
@ -100,12 +55,6 @@ namespace pxsim.control {
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pxtcore {
|
||||
export function registerWithDal(id: number, evid: number, handler: RefAction) {
|
||||
board().bus.listen(id, evid, handler);
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.input {
|
||||
export function calibrateCompass() {
|
||||
// device calibrates...
|
||||
@ -168,6 +117,12 @@ namespace pxsim.pins {
|
||||
|
||||
export function setEvents(name: number, event: number) {
|
||||
}
|
||||
|
||||
export function setMatrixWidth(pin: number, width: number) {
|
||||
const lp = neopixelState(pin);
|
||||
if (!lp) return;
|
||||
lp.width = width;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.devices {
|
||||
@ -246,13 +201,24 @@ namespace pxsim.bluetooth {
|
||||
}
|
||||
|
||||
namespace pxsim.light {
|
||||
|
||||
export function sendWS2812Buffer(buffer: RefBuffer, pin: number) {
|
||||
pxsim.sendBufferAsm(buffer, pin)
|
||||
}
|
||||
|
||||
export function sendWS2812BufferWithBrightness(buffer: RefBuffer, pin: number, brightness: number) {
|
||||
const clone = new RefBuffer(new Uint8Array(buffer.data))
|
||||
const data = clone.data;
|
||||
for(let i =0; i < data.length; ++i) {
|
||||
data[i] = (data[i] * brightness) >> 8;
|
||||
}
|
||||
pxsim.sendBufferAsm(clone, pin)
|
||||
}
|
||||
|
||||
export function setMode(pin: number, mode: number) {
|
||||
const lp = neopixelState(pin);
|
||||
if (!lp) return;
|
||||
lp.mode = mode & 0xff;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,17 @@ namespace pxsim {
|
||||
export class SerialState {
|
||||
serialIn: string[] = [];
|
||||
|
||||
constructor(private readonly runtime: Runtime, private readonly board: BaseBoard) {
|
||||
this.board.addMessageListener(this.handleMessage.bind(this))
|
||||
}
|
||||
|
||||
private handleMessage(msg: SimulatorMessage) {
|
||||
if (msg.type === "serial") {
|
||||
const data = (<SimulatorSerialMessage>msg).data || "";
|
||||
this.receiveData(data);
|
||||
}
|
||||
}
|
||||
|
||||
public receiveData(data: string) {
|
||||
this.serialIn.push();
|
||||
}
|
||||
@ -28,12 +39,6 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.control {
|
||||
export function __log(s: string) {
|
||||
board().writeSerial(s + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.serial {
|
||||
export function writeString(s: string) {
|
||||
board().writeSerial(s);
|
||||
|
60
sim/state/soundexpression.ts
Normal file
60
sim/state/soundexpression.ts
Normal file
@ -0,0 +1,60 @@
|
||||
namespace pxsim.music {
|
||||
function loadWavAsync(path: string): Promise<Uint8Array> {
|
||||
return new Promise<Uint8Array>((resolve, reject) => {
|
||||
let httprequest = new XMLHttpRequest();
|
||||
httprequest.responseType = "arraybuffer";
|
||||
httprequest.onreadystatechange = function () {
|
||||
if (httprequest.readyState == XMLHttpRequest.DONE) {
|
||||
if (httprequest.status == 200) {
|
||||
const r = httprequest.response;
|
||||
resolve(new Uint8Array(httprequest.response));
|
||||
}
|
||||
else {
|
||||
reject(httprequest.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
httprequest.open("GET", path, true);
|
||||
httprequest.send();
|
||||
})
|
||||
}
|
||||
const wavPromises: Map<Promise<Uint8Array>> = {}
|
||||
//%
|
||||
export function __playSoundExpression(notes: string, waitTillDone: boolean): void {
|
||||
const cb = getResume();
|
||||
const b = board();
|
||||
// v2 only...
|
||||
b.ensureHardwareVersion(2);
|
||||
|
||||
// load wav file
|
||||
let p: Promise<Uint8Array>;
|
||||
// defined in sim.html
|
||||
const path = (<any>pxsim).soundExpressionFiles[notes];
|
||||
if (path) {
|
||||
p = wavPromises[notes] || (wavPromises[notes] = loadWavAsync(path));
|
||||
} else
|
||||
p = Promise.resolve(undefined);
|
||||
|
||||
p.then(data => {
|
||||
// failed to load data
|
||||
if (data) {
|
||||
// finally play
|
||||
const buf = new RefBuffer(data);
|
||||
const pp = AudioContextManager.playBufferAsync(buf)
|
||||
if (waitTillDone)
|
||||
// wait until sound is done
|
||||
return pp;
|
||||
}
|
||||
// don't wait
|
||||
cb();
|
||||
return Promise.resolve();
|
||||
}).catch((e) => {
|
||||
console.log(e)
|
||||
cb();
|
||||
})
|
||||
}
|
||||
|
||||
export function __stopSoundExpressions() {
|
||||
AudioContextManager.stopAll();
|
||||
}
|
||||
}
|
36
sim/state/storage.ts
Normal file
36
sim/state/storage.ts
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
namespace pxsim.storage {
|
||||
|
||||
export function putValue(key: string, value: string) : void {
|
||||
sessionStorage.setItem('simulatorValue_'+key, value);
|
||||
}
|
||||
|
||||
export function putValueInt(key: string, value: number) : void {
|
||||
sessionStorage.setItem('simulatorValue_'+key, value+"");
|
||||
}
|
||||
|
||||
export function getValue(key: string) : string {
|
||||
if(sessionStorage.getItem('simulatorValue_'+key)) {
|
||||
return sessionStorage.getItem('simulatorValue_'+key);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function getValueInt(key: string) : number {
|
||||
if(sessionStorage.getItem('simulatorValue_'+key)) {
|
||||
return parseFloat(sessionStorage.getItem('simulatorValue_'+key));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function removeStr(key: string) : void {
|
||||
sessionStorage.removeItem('simulatorValue_'+key);
|
||||
}
|
||||
|
||||
export function removeInt(key: string) : void {
|
||||
sessionStorage.removeItem('simulatorValue_'+key);
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es2017",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"declaration": true,
|
||||
"out": "../built/sim.js",
|
||||
"rootDir": ".",
|
||||
"newLine": "LF",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": false,
|
||||
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
|
||||
"types": ["bluebird"],
|
||||
"typeRoots": ["../node_modules/@types"]
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost",
|
||||
"es2017"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"state/*.ts",
|
||||
"visuals/*.ts",
|
||||
"../node_modules/pxt-common-packages/libs/base/sim/control.ts",
|
||||
"../node_modules/pxt-common-packages/libs/base/sim/controlmessage.ts",
|
||||
"../node_modules/pxt-common-packages/libs/core/sim/analogSensor.ts",
|
||||
"../node_modules/pxt-common-packages/libs/microphone/sim/state.ts",
|
||||
"../node_modules/pxt-common-packages/libs/core/sim/neopixel.ts",
|
||||
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts"
|
||||
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts",
|
||||
"../node_modules/pxt-common-packages/libs/settings/sim/*.ts"
|
||||
]
|
||||
}
|
||||
|
@ -8,10 +8,15 @@ namespace pxsim.visuals {
|
||||
-webkit-filter: grayscale(1);
|
||||
filter: grayscale(1);
|
||||
}
|
||||
.sim-button-group {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sim-button {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sim-board, .sim-display, sim-button {
|
||||
fill: #111;
|
||||
}
|
||||
.sim-button-outer:hover {
|
||||
stroke:grey;
|
||||
stroke-width: 3px;
|
||||
@ -59,20 +64,21 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
.sim-text {
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:14px;
|
||||
fill:#fff;
|
||||
pointer-events: none; user-select: none;
|
||||
}
|
||||
.sim-text.inverted {
|
||||
fill:#000;
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:14px;
|
||||
fill:#fff;
|
||||
pointer-events: none; user-select: none;
|
||||
}
|
||||
|
||||
.sim-text-pin {
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:20px;
|
||||
fill:#3cb5b5;
|
||||
pointer-events: none;
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
pointer-events: none; user-select: none;
|
||||
fill:#3cb5b5;
|
||||
font-size:24px;
|
||||
stroke:#fff;
|
||||
stroke-alignment: outside;
|
||||
paint-order: stroke;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.sim-thermometer {
|
||||
@ -84,6 +90,22 @@ namespace pxsim.visuals {
|
||||
r:8px;
|
||||
}
|
||||
|
||||
.inverted {
|
||||
fill:#000;
|
||||
stroke:#fff;
|
||||
stroke-alignment: outside;
|
||||
paint-order: stroke;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
.big {
|
||||
font-size:24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.centered {
|
||||
transform: translateX(-1.5ch);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* animations */
|
||||
.sim-theme-glow {
|
||||
animation-name: sim-theme-glow-animation;
|
||||
@ -686,6 +708,8 @@ namespace pxsim.visuals {
|
||||
virtualButtonDown?: string;
|
||||
lightLevelOn?: string;
|
||||
lightLevelOff?: string;
|
||||
soundLevelOn?: string;
|
||||
soundLevelOff?: string;
|
||||
}
|
||||
|
||||
export var themes: IBoardTheme[] = ["#3ADCFE"].map(accent => {
|
||||
@ -702,8 +726,10 @@ namespace pxsim.visuals {
|
||||
virtualButtonDown: "#FFA500",
|
||||
virtualButtonOuter: "#333",
|
||||
virtualButtonUp: "#fff",
|
||||
lightLevelOn: "yellow",
|
||||
lightLevelOff: "#555"
|
||||
lightLevelOn: "#555",
|
||||
lightLevelOff: "yellow",
|
||||
soundLevelOn: "#3ADCFE",
|
||||
soundLevelOff: "#555"
|
||||
}
|
||||
});
|
||||
|
||||
@ -723,14 +749,24 @@ namespace pxsim.visuals {
|
||||
private style: SVGStyleElement;
|
||||
private defs: SVGDefsElement;
|
||||
private g: SVGGElement;
|
||||
|
||||
private pkg: SVGPathElement;
|
||||
private logos: SVGElement[];
|
||||
private headg: SVGGElement;
|
||||
private head: SVGGElement;
|
||||
private headParts: SVGElement;
|
||||
private headInitialized = false;
|
||||
private heads: SVGElement[];
|
||||
private headText: SVGTextElement;
|
||||
private display: SVGElement;
|
||||
private buttons: SVGElement[];
|
||||
private buttonsOuter: SVGElement[];
|
||||
private buttonABText: SVGTextElement;
|
||||
private pins: SVGElement[];
|
||||
private pinGradients: SVGLinearGradientElement[];
|
||||
private pinTexts:{ [key: number]: SVGTextElement };
|
||||
private ledsOuter: SVGElement[];
|
||||
private leds: SVGElement[];
|
||||
private microphoneLed: SVGElement;
|
||||
private systemLed: SVGCircleElement;
|
||||
private antenna: SVGPolylineElement;
|
||||
private rssi: SVGTextElement;
|
||||
@ -740,8 +776,14 @@ namespace pxsim.visuals {
|
||||
private thermometerGradient: SVGLinearGradientElement;
|
||||
private thermometer: SVGRectElement;
|
||||
private thermometerText: SVGTextElement;
|
||||
private soundLevelGradient: SVGLinearGradientElement;
|
||||
private soundLevel: SVGRectElement;
|
||||
private soundLevelText: SVGTextElement;
|
||||
private soundLevelIcon: SVGTextElement;
|
||||
private shakeButton: SVGElement;
|
||||
private shakeText: SVGTextElement;
|
||||
public board: pxsim.DalBoard;
|
||||
private domHardwareVersion = 1;
|
||||
private rgbLed: SVGElement;
|
||||
private pinNmToCoord: Map<Coord> = {
|
||||
"EXT_PWR": [
|
||||
@ -1008,7 +1050,7 @@ namespace pxsim.visuals {
|
||||
|
||||
this.pinGradients.forEach(lg => svg.setGradientColors(lg, theme.pin, theme.pinActive));
|
||||
svg.setGradientColors(this.lightLevelGradient, theme.lightLevelOn, theme.lightLevelOff);
|
||||
|
||||
svg.setGradientColors(this.soundLevelGradient, theme.soundLevelOff, theme.soundLevelOn);
|
||||
svg.setGradientColors(this.thermometerGradient, theme.ledOff, theme.ledOn);
|
||||
}
|
||||
|
||||
@ -1017,6 +1059,18 @@ namespace pxsim.visuals {
|
||||
if (!state) return;
|
||||
let theme = this.props.theme;
|
||||
|
||||
this.updateMicrophone();
|
||||
this.updatePins();
|
||||
this.updateTilt();
|
||||
this.updateHeading();
|
||||
this.updateLightLevel();
|
||||
this.updateTemperature();
|
||||
this.updateButtonAB();
|
||||
this.updateGestures();
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
this.updateRSSI();
|
||||
|
||||
let bpState = state.buttonPairState;
|
||||
let buttons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
|
||||
buttons.forEach((btn, index) => {
|
||||
@ -1046,16 +1100,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
})
|
||||
}
|
||||
this.updatePins();
|
||||
this.updateTilt();
|
||||
this.updateHeading();
|
||||
this.updateLightLevel();
|
||||
this.updateTemperature();
|
||||
this.updateButtonAB();
|
||||
this.updateGestures();
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
this.updateRSSI();
|
||||
|
||||
|
||||
if (!runtime || runtime.dead) U.addClass(this.element, "grayscale");
|
||||
else U.removeClass(this.element, "grayscale");
|
||||
@ -1090,7 +1135,7 @@ namespace pxsim.visuals {
|
||||
private updateGestures() {
|
||||
let state = this.board;
|
||||
if (state.accelerometerState.useShake && !this.shakeButton) {
|
||||
let shake = this.mkBtn(26, MB_HEIGHT - 45);
|
||||
let shake = this.mkBtn(280, MB_HEIGHT - 45);
|
||||
this.shakeButton = shake.inner;
|
||||
svg.fill(this.shakeButton, this.props.theme.virtualButtonUp)
|
||||
svg.buttonEvents(shake.outer,
|
||||
@ -1103,11 +1148,17 @@ namespace pxsim.visuals {
|
||||
this.board.bus.queue(DAL.MICROBIT_ID_GESTURE, 11); // GESTURE_SHAKE
|
||||
}
|
||||
)
|
||||
let shakeText = svg.child(shake.outer, "text", { x: 15, y: MB_HEIGHT - 10, class: "sim-text inverted" }) as SVGTextElement;
|
||||
let shakeText = svg.child(shake.outer, "text", { x: 280, y: MB_HEIGHT - 5, class: "sim-text big inverted centered" }) as SVGTextElement;
|
||||
shakeText.textContent = "SHAKE"
|
||||
}
|
||||
}
|
||||
|
||||
private updateMicrophone() {
|
||||
const b = board();
|
||||
if (!b || !b.microphoneState.sensorUsed) return;
|
||||
this.updateSoundLevel();
|
||||
}
|
||||
|
||||
private updateButtonAB() {
|
||||
let state = this.board;
|
||||
if (state.buttonPairState.usesButtonAB && (<any>this.buttons[2]).style.visibility != "visible") {
|
||||
@ -1158,7 +1209,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
else if (pin.mode & PinFlags.Touch) {
|
||||
v = pin.touched ? "0%" : "100%";
|
||||
if (text) text.textContent = "";
|
||||
if (text) text.textContent = v;
|
||||
} else {
|
||||
v = "100%";
|
||||
if (text) text.textContent = "";
|
||||
@ -1185,65 +1236,218 @@ namespace pxsim.visuals {
|
||||
if (!this.thermometer) {
|
||||
let gid = "gradient-thermometer";
|
||||
this.thermometerGradient = svg.linearGradient(this.defs, gid);
|
||||
const ty = MB_HEIGHT - 180;
|
||||
const ty = MB_HEIGHT - 200;
|
||||
this.thermometer = <SVGRectElement>svg.child(this.g, "rect", {
|
||||
class: "sim-thermometer",
|
||||
x: 28,
|
||||
x: 0,
|
||||
y: ty,
|
||||
width: 10,
|
||||
height: 80,
|
||||
rx: 5, ry: 5,
|
||||
width: 30,
|
||||
height: 160,
|
||||
rx: 5,
|
||||
ry: 5,
|
||||
fill: `url(#${gid})`
|
||||
});
|
||||
this.thermometerText = svg.child(this.g, "text", {
|
||||
class: 'sim-text',
|
||||
x: 48, y: ty + 78
|
||||
class: 'sim-text big inverted centered',
|
||||
x: 15,
|
||||
y: ty + 190
|
||||
}) as SVGTextElement;
|
||||
this.updateTheme();
|
||||
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(this.thermometer,
|
||||
// move
|
||||
(ev) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty - 5) / 70))
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty) / 160))
|
||||
state.thermometerState.temperature = Math.floor(tmax - t * (tmax - tmin));
|
||||
this.updateTemperature();
|
||||
}, ev => { }, ev => { })
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
(ev) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.thermometerState.temperature--;
|
||||
if(state.thermometerState.temperature < tmin) state.thermometerState.temperature = tmin;
|
||||
this.updateTemperature();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.thermometerState.temperature++
|
||||
if(state.thermometerState.temperature > tmax) state.thermometerState.temperature = tmax;
|
||||
this.updateTemperature();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
accessibility.makeFocusable(this.thermometer);
|
||||
accessibility.setAria(this.thermometer, "slider", pxsim.localization.lf("Temperature Level"));
|
||||
this.thermometer.setAttribute("aria-valuemin", tmin + "");
|
||||
this.thermometer.setAttribute("aria-valuemax", tmax + "");
|
||||
this.thermometer.setAttribute("aria-orientation", "vertical");
|
||||
this.thermometer.setAttribute("aria-valuenow", state.thermometerState.temperature + "");
|
||||
this.thermometer.setAttribute("aria-valuetext", state.thermometerState.temperature + "");
|
||||
|
||||
let t = Math.max(tmin, Math.min(tmax, state.thermometerState.temperature))
|
||||
let per = Math.floor((state.thermometerState.temperature - tmin) / (tmax - tmin) * 100)
|
||||
svg.setGradientValue(this.thermometerGradient, 100 - per + "%");
|
||||
this.thermometerText.textContent = t + "°C";
|
||||
this.thermometer.setAttribute("aria-valuenow", t.toString());
|
||||
this.thermometer.setAttribute("aria-valuetext", t + "°C");
|
||||
accessibility.setLiveContent(t + "°C");
|
||||
}
|
||||
|
||||
private updateSoundLevel() {
|
||||
let state = this.board;
|
||||
if (!state || !state.microphoneState.sensorUsed) return;
|
||||
|
||||
const tmin = 0 // state.microphoneState.min;
|
||||
const tmax = 255 //state.microphoneState.max;
|
||||
if (!this.soundLevel) {
|
||||
const level = state.microphoneState.getLevel();
|
||||
let gid = "gradient-soundlevel";
|
||||
this.soundLevelGradient = svg.linearGradient(this.defs, gid);
|
||||
const ty = MB_HEIGHT - 200;
|
||||
this.soundLevel = <SVGRectElement>svg.child(this.g, "rect", {
|
||||
class: "sim-thermometer",
|
||||
x: 490,
|
||||
y: ty,
|
||||
width: 30,
|
||||
height: 160,
|
||||
rx: 5,
|
||||
ry: 5,
|
||||
fill: `url(#${gid})`
|
||||
});
|
||||
this.soundLevelText = svg.child(this.g, "text", {
|
||||
class: 'sim-text big inverted centered',
|
||||
x: 505,
|
||||
y: ty + 190
|
||||
}) as SVGTextElement;
|
||||
this.soundLevelIcon = svg.child(this.g, "svg", {
|
||||
x: 495,
|
||||
y: 400,
|
||||
viewbox: "0 0 20 29",
|
||||
role: "img",
|
||||
}) as SVGTextElement;
|
||||
this.soundLevelIcon.setAttribute("aria-hidden", "true");
|
||||
this.soundLevelIcon.setAttribute("focusable", "false");
|
||||
this.soundLevelIcon.setAttribute("style", "pointer-events: none; opacity: 0.8; width: 20px;");
|
||||
svg.child(this.soundLevelIcon, "path", {
|
||||
fill: "white",
|
||||
d: "M 10 19.9375 C 13.011719 19.9375 15.453125 17.503906 15.453125 14.5 L 15.453125 5.4375 C 15.453125 2.433594 13.011719 0 10 0 C 6.988281 0 4.546875 2.433594 4.546875 5.4375 L 4.546875 14.5 C 4.546875 17.503906 6.988281 19.9375 10 19.9375 Z M 19.089844 10.875 L 18.183594 10.875 C 17.679688 10.875 17.273438 11.28125 17.273438 11.78125 L 17.273438 14.5 C 17.273438 18.738281 13.609375 22.136719 9.273438 21.714844 C 5.496094 21.347656 2.726562 17.960938 2.726562 14.175781 L 2.726562 11.78125 C 2.726562 11.28125 2.320312 10.875 1.816406 10.875 L 0.910156 10.875 C 0.40625 10.875 0 11.28125 0 11.78125 L 0 14.054688 C 0 19.132812 3.632812 23.660156 8.636719 24.347656 L 8.636719 26.28125 L 5.453125 26.28125 C 4.953125 26.28125 4.546875 26.6875 4.546875 27.1875 L 4.546875 28.09375 C 4.546875 28.59375 4.953125 29 5.453125 29 L 14.546875 29 C 15.046875 29 15.453125 28.59375 15.453125 28.09375 L 15.453125 27.1875 C 15.453125 26.6875 15.046875 26.28125 14.546875 26.28125 L 11.363281 26.28125 L 11.363281 24.367188 C 16.234375 23.703125 20 19.535156 20 14.5 L 20 11.78125 C 20 11.28125 19.59375 10.875 19.089844 10.875 Z M 19.089844 10.875 "
|
||||
});
|
||||
if (this.props.runtime)
|
||||
this.props.runtime.environmentGlobals[pxsim.localization.lf("sound level")] = state.microphoneState.getLevel();
|
||||
this.updateTheme();
|
||||
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(this.soundLevel,
|
||||
// move
|
||||
(ev) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty) / 160)) * tmax
|
||||
console.log(tmax - t);
|
||||
state.microphoneState.setLevel( Math.floor(tmax - t));
|
||||
// state.microphoneState.setLevel(Math.floor(tmin + t * (tmax - tmin)));
|
||||
this.updateMicrophone();
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
(ev) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.microphoneState.setLevel(state.microphoneState.getLevel() - 1);
|
||||
if(state.microphoneState.getLevel() < tmin) state.microphoneState.setLevel(tmin);
|
||||
this.updateMicrophone();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.microphoneState.setLevel(state.microphoneState.getLevel() + 1);
|
||||
if(state.microphoneState.getLevel() > tmax) state.microphoneState.setLevel(tmax);
|
||||
this.updateMicrophone();
|
||||
}
|
||||
})
|
||||
|
||||
accessibility.makeFocusable(this.soundLevel);
|
||||
accessibility.setAria(this.soundLevel, "slider", pxsim.localization.lf("Sound Level"));
|
||||
this.soundLevel.setAttribute("aria-valuemin", tmin + "");
|
||||
this.soundLevel.setAttribute("aria-valuemax", tmax + "");
|
||||
this.soundLevel.setAttribute("aria-orientation", "vertical");
|
||||
this.soundLevel.setAttribute("aria-valuenow", level + "");
|
||||
this.soundLevel.setAttribute("aria-valuetext", level + "");
|
||||
}
|
||||
|
||||
let t = Math.max(tmin, Math.min(tmax, state.microphoneState.getLevel()))
|
||||
let per = Math.floor((state.microphoneState.getLevel() - tmin) / (tmax - tmin) * 100)
|
||||
svg.setGradientValue(this.soundLevelGradient, (100 - per) + "%");
|
||||
this.soundLevelText.textContent = t + "";
|
||||
this.soundLevel.setAttribute("aria-valuenow", t.toString());
|
||||
this.soundLevel.setAttribute("aria-valuetext", t + "");
|
||||
accessibility.setLiveContent(t + "");
|
||||
}
|
||||
|
||||
private updateHeading() {
|
||||
let xc = 258;
|
||||
const valMin = 0;
|
||||
const valMax = 360;
|
||||
let xc = 501.2;
|
||||
let yc = 75;
|
||||
let state = this.board;
|
||||
if (!state || !state.compassState.usesHeading) return;
|
||||
/*
|
||||
// /*
|
||||
if (!this.headInitialized) {
|
||||
let p = this.head.firstChild.nextSibling as SVGPathElement;
|
||||
p.setAttribute("d", "m269.9,50.134647l0,0l-39.5,0l0,0c-14.1,0.1 -24.6,10.7 -24.6,24.8c0,13.9 10.4,24.4 24.3,24.7l0,0l39.6,0c14.2,0 40.36034,-22.97069 40.36034,-24.85394c0,-1.88326 -26.06034,-24.54606 -40.16034,-24.64606m-0.2,39l0,0l-39.3,0c-7.7,-0.1 -14,-6.4 -14,-14.2c0,-7.8 6.4,-14.2 14.2,-14.2l39.1,0c7.8,0 14.2,6.4 14.2,14.2c0,7.9 -6.4,14.2 -14.2,14.2l0,0l0,0z");
|
||||
let p = this.heads[1];
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#DDDDDD55;stroke:#3A3A3A;", cx: "501.2", cy: "75", r: "55" });
|
||||
<SVGGElement>svg.child(p, "polyline", {style: "fill:#008EEF;stroke:#3A3A3A;", points: "517.7,75 501.1,140.2 484.6,75" });
|
||||
<SVGGElement>svg.child(p, "polyline", {style: "fill:#FF3951;stroke:#3A3A3A;", points: "484.6,75 501.1,9.5 517.7,75" });
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#748476;stroke:#3A3A3A;", cx: "501.1", cy: "75", r: "16.5" });
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#CCDBCE;", cx: "501.1", cy: "75", r: "10" });
|
||||
// p.setAttribute("d", "m269.9,50.134647l0,0l-39.5,0l0,0c-14.1,0.1 -24.6,10.7 -24.6,24.8c0,13.9 10.4,24.4 24.3,24.7l0,0l39.6,0c14.2,0 40.36034,-22.97069 40.36034,-24.85394c0,-1.88326 -26.06034,-24.54606 -40.16034,-24.64606m-0.2,39l0,0l-39.3,0c-7.7,-0.1 -14,-6.4 -14,-14.2c0,-7.8 6.4,-14.2 14.2,-14.2l39.1,0c7.8,0 14.2,6.4 14.2,14.2c0,7.9 -6.4,14.2 -14.2,14.2l0,0l0,0z");
|
||||
this.updateTheme();
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(
|
||||
this.head,
|
||||
svg.buttonEvents(this.head,
|
||||
// move
|
||||
(ev: MouseEvent) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
state.compassState.heading = Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI + 90);
|
||||
if (state.compassState.heading < 0) state.compassState.heading += 360;
|
||||
state.compassState.heading = valMax - (Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI) + 90) - valMax;
|
||||
if (state.compassState.heading < valMin) state.compassState.heading += valMax;
|
||||
this.updateHeading();
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
ev => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.compassState.heading--;
|
||||
if(state.compassState.heading < valMin) state.compassState.heading += valMax;
|
||||
this.updateHeading();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.compassState.heading++;
|
||||
if(state.compassState.heading >= valMax) state.compassState.heading -= valMax;;
|
||||
this.updateHeading();
|
||||
}
|
||||
});
|
||||
this.headInitialized = true;
|
||||
}
|
||||
|
||||
accessibility.makeFocusable(this.head);
|
||||
accessibility.setAria(this.head, "slider", pxsim.localization.lf("Heading"));
|
||||
this.head.setAttribute("aria-valuemin", valMin + "");
|
||||
this.head.setAttribute("aria-valuemax", valMax + "");
|
||||
this.head.setAttribute("aria-orientation", "vertical");
|
||||
this.head.setAttribute("aria-valuenow", state.compassState.heading + "");
|
||||
this.head.setAttribute("aria-valuetext", state.compassState.heading + "");
|
||||
|
||||
let txt = state.compassState.heading.toString() + "°";
|
||||
if (txt != this.headText.textContent) {
|
||||
svg.rotateElement(this.head, xc, yc, state.compassState.heading + 180);
|
||||
svg.rotateElement(this.head, xc, yc, valMax - state.compassState.heading - 90);
|
||||
this.headText.textContent = txt;
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
private lastFlashTime: number = 0;
|
||||
@ -1282,46 +1486,77 @@ namespace pxsim.visuals {
|
||||
|
||||
private updateLightLevel() {
|
||||
let state = this.board;
|
||||
const valMin = 0;
|
||||
const valMax = 255;
|
||||
if (!state || !state.lightSensorState.usesLightLevel) return;
|
||||
|
||||
if (!this.lightLevelButton) {
|
||||
let gid = "gradient-light-level";
|
||||
this.lightLevelGradient = svg.linearGradient(this.defs, gid)
|
||||
const cx = 30;
|
||||
const cy = 45;
|
||||
const r = 20;
|
||||
const cx = 25;
|
||||
const cy = 75;
|
||||
const r = 55;
|
||||
|
||||
this.lightLevelButton = svg.child(this.g, "circle", {
|
||||
cx: `${cx}px`, cy: `${cy}px`, r: `${r}px`,
|
||||
class: 'sim-light-level-button',
|
||||
fill: `url(#${gid})`
|
||||
}) as SVGCircleElement;
|
||||
let pt = this.element.createSVGPoint();
|
||||
|
||||
svg.buttonEvents(this.lightLevelButton,
|
||||
// move
|
||||
(ev) => {
|
||||
let pos = svg.cursorPoint(pt, this.element, ev);
|
||||
let rs = r / 2;
|
||||
let level = Math.max(0, Math.min(255, Math.floor((pos.y - (cy - rs)) / (2 * rs) * 255)));
|
||||
if (level != this.board.lightSensorState.lightLevel) {
|
||||
this.board.lightSensorState.lightLevel = level;
|
||||
let level = valMax - Math.max(valMin, Math.min(valMax, Math.floor((pos.y - (cy - r)) / (2 * r) * valMax)));
|
||||
|
||||
if (level != state.lightSensorState.lightLevel) {
|
||||
state.lightSensorState.lightLevel = level;
|
||||
this.applyLightLevel();
|
||||
}
|
||||
}, ev => { },
|
||||
ev => { })
|
||||
this.lightLevelText = svg.child(this.g, "text", { x: cx - r - 7, y: cy + r + 8, text: '', class: 'sim-text inverted' }) as SVGTextElement;
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
ev => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.lightSensorState.lightLevel--;
|
||||
if(state.lightSensorState.lightLevel < valMin) state.lightSensorState.lightLevel = valMin;
|
||||
this.applyLightLevel();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.lightSensorState.lightLevel++
|
||||
if(state.lightSensorState.lightLevel > valMax) state.lightSensorState.lightLevel = valMax;
|
||||
this.applyLightLevel();
|
||||
}
|
||||
})
|
||||
this.lightLevelText = svg.child(this.g, "text", { x: cx , y: cy + r + 35, text: '', class: 'sim-text inverted big centered' }) as SVGTextElement;
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor(state.lightSensorState.lightLevel * 100 / 255))) + '%')
|
||||
accessibility.makeFocusable(this.lightLevelButton);
|
||||
accessibility.setAria(this.lightLevelButton, "slider", pxsim.localization.lf("Light Level"));
|
||||
this.lightLevelButton.setAttribute("aria-valuemin", valMin + "");
|
||||
this.lightLevelButton.setAttribute("aria-valuemax", valMax + "");
|
||||
this.lightLevelButton.setAttribute("aria-orientation", "vertical");
|
||||
this.lightLevelButton.setAttribute("aria-valuenow", state.lightSensorState.lightLevel + "");
|
||||
this.lightLevelButton.setAttribute("aria-valuetext", state.lightSensorState.lightLevel + "");
|
||||
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor((255 - state.lightSensorState.lightLevel) * 100 / 255))) + '%')
|
||||
this.lightLevelText.textContent = state.lightSensorState.lightLevel.toString();
|
||||
}
|
||||
|
||||
private applyLightLevel() {
|
||||
let lv = this.board.lightSensorState.lightLevel;
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor(lv * 100 / 255))) + '%')
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor((255 - lv) * 100 / 255))) + '%')
|
||||
this.lightLevelText.textContent = lv.toString();
|
||||
}
|
||||
|
||||
private updateTilt() {
|
||||
return;
|
||||
if (this.props.disableTilt) return;
|
||||
let state = this.board;
|
||||
if (!state || !state.accelerometerState.accelerometer.isActive) return;
|
||||
@ -1347,6 +1582,7 @@ namespace pxsim.visuals {
|
||||
"y": "0px",
|
||||
"width": MB_WIDTH + "px",
|
||||
"height": MB_HEIGHT + "px",
|
||||
"fill": "rgba(0,0,0,0)"
|
||||
});
|
||||
this.style = <SVGStyleElement>svg.child(this.element, "style", {});
|
||||
this.style.textContent = MB_STYLE;
|
||||
@ -1356,6 +1592,15 @@ namespace pxsim.visuals {
|
||||
this.element.appendChild(this.g);
|
||||
|
||||
// filters
|
||||
let ledglow = svg.child(this.defs, "filter", { id: "ledglow", x: "-75%", y: "-75%", width: "300%", height: "300%" });
|
||||
svg.child(ledglow, "feMorphology", { operator: "dilate", radius: "4", in: "SourceAlpha", result: "thicken" });
|
||||
svg.child(ledglow, "feGaussianBlur", { stdDeviation: "5", in: "thicken", result: "blurred" });
|
||||
svg.child(ledglow, "feFlood", { "flood-color": "rgb(255, 17, 77)", result: "glowColor" });
|
||||
svg.child(ledglow, "feComposite", { in: "glowColor", in2: "blurred", operator: "in", result: "ledglow_colored" });
|
||||
let ledglowMerge = svg.child(ledglow, "feMerge", {});
|
||||
svg.child(ledglowMerge, "feMergeNode", { in: "ledglow_colored" });
|
||||
svg.child(ledglowMerge, "feMergeNode", { in: "SourceGraphic" });
|
||||
|
||||
let glow = svg.child(this.defs, "filter", { id: "filterglow", x: "-5%", y: "-5%", width: "120%", height: "120%" });
|
||||
svg.child(glow, "feGaussianBlur", { stdDeviation: "5", result: "glow" });
|
||||
let merge = svg.child(glow, "feMerge", {});
|
||||
@ -1368,24 +1613,42 @@ namespace pxsim.visuals {
|
||||
const top = Number(this.element.getElementById("LED_0_0").getAttribute("y"));
|
||||
const ledoffw = Number(this.element.getElementById("LED_1_0").getAttribute("x"))-left;
|
||||
const ledoffh = Number(this.element.getElementById("LED_0_1").getAttribute("y"))-top;
|
||||
const ledw = 5.1;
|
||||
const ledh = 12.9;
|
||||
// const ledw = 5.1;
|
||||
// const ledh = 12.9;
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
let ledtop = i * ledoffh + top;
|
||||
for (let j = 0; j < 5; ++j) {
|
||||
let ledleft = j * ledoffw + left;
|
||||
let k = i * 5 + j;
|
||||
this.ledsOuter.push(svg.child(this.g, "rect", { class: "sim-led-back", x: ledleft, y: ledtop, width: ledw, height: ledh }));
|
||||
this.leds.push(svg.child(this.g, "rect", { class: "sim-led", x: ledleft - 1, y: ledtop - 1, width: ledw + 2, height: ledh + 2, rx: 2, ry: 2, title: `(${j},${i})` }));
|
||||
this.ledsOuter.push(svg.child(this.g, "rect", { class: "sim-led-back", x: ledleft, y: ledtop, width: 10, height: 20, rx: 2, ry: 2 }));
|
||||
let led = svg.child(this.g, "rect", { class: "sim-led", x: ledleft - 2, y: ledtop - 2, width: 14, height: 24, rx: 3, ry: 3, title: `(${j},${i})` });
|
||||
svg.filter(led, `url(#ledglow)`)
|
||||
this.leds.push(led);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// head
|
||||
// this.headg = <SVGGElement>svg.child(this.g, "g", { style: "transform: translate(100px, 0px);" });
|
||||
this.head = <SVGGElement>svg.child(this.g, "g", { class: "sim-head" });
|
||||
svg.child(this.head, "circle", { cx: 501.2, cy: 75, r: 100, fill: "transparent" })
|
||||
this.headParts = <SVGGElement>svg.child(this.head, "g", { class: "sim-button-outer sim-button-group" });
|
||||
this.heads = []
|
||||
// background
|
||||
this.heads.push(svg.path(this.headParts, "sim-button",""));
|
||||
// shapes
|
||||
this.heads.push(<SVGGElement>svg.child(this.headParts, "g", { class: "sim-theme" }));
|
||||
// this.heads.push(svg.path(this.headParts, "sim-theme", "M230.6,69.7c-2.9,0-5.3,2.4-5.3,5.3c0,2.9,2.4,5.3,5.3,5.3c2.9,0,5.3-2.4,5.3-5.3C235.9,72.1,233.5,69.7,230.6,69.7"));
|
||||
// this.heads.push(svg.path(this.headParts, "sim-theme", "M269.7,80.3c2.9,0,5.3-2.4,5.3-5.3c0-2.9-2.4-5.3-5.3-5.3c-2.9,0-5.3,2.4-5.3,5.3C264.4,77.9,266.8,80.3,269.7,80.3"));
|
||||
this.headText = <SVGTextElement>svg.child(this.g, "text", { x: 500, y: 165, class: "sim-text inverted big centered" })
|
||||
|
||||
// https://www.microbit.co.uk/device/pins
|
||||
// P0, P1, P2
|
||||
this.pins = pinNames.map(n => {
|
||||
let p = this.element.getElementById(n) as SVGElement;
|
||||
if(!p) console.log("missing "+n);
|
||||
U.addClass(p, "sim-pin");
|
||||
// console.log(p);
|
||||
return p;
|
||||
});
|
||||
|
||||
@ -1398,11 +1661,18 @@ namespace pxsim.visuals {
|
||||
return lg;
|
||||
})
|
||||
|
||||
// this.pinTexts = [
|
||||
// [-20, 340],
|
||||
// [50, 495],
|
||||
// [450, 495],
|
||||
// [500, 340]
|
||||
// ].map(p => <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: p[0], y: p[1] }));
|
||||
|
||||
this.pinTexts = {
|
||||
[DigitalPin.P0]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: -20, y: 340 }),
|
||||
[DigitalPin.P1]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 50, y: 495 }),
|
||||
[DigitalPin.P2]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 450, y: 495 }),
|
||||
[DigitalPin.P3]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 500, y: 340 })
|
||||
[DigitalPin.P0]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 20, y: 315 }),
|
||||
[DigitalPin.P1]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 135, y: 530 }),
|
||||
[DigitalPin.P2]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 395, y: 530 }),
|
||||
[DigitalPin.P3]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 535, y: 315 })
|
||||
}
|
||||
|
||||
// BTN A, B
|
||||
@ -1421,8 +1691,8 @@ namespace pxsim.visuals {
|
||||
return button;
|
||||
}
|
||||
|
||||
let ab = outerBtn(69, MB_HEIGHT - 45);
|
||||
let abtext = svg.child(ab.outer, "text", { x: 67, y: MB_HEIGHT - 10, class: "sim-text inverted" }) as SVGTextElement;
|
||||
let ab = outerBtn(210, MB_HEIGHT - 45);
|
||||
let abtext = svg.child(ab.outer, "text", { x: 210, y: MB_HEIGHT - 5, class: "sim-text big inverted centered" }) as SVGTextElement;
|
||||
abtext.textContent = "A+B";
|
||||
(<any>this.buttonsOuter[2]).style.visibility = "hidden";
|
||||
(<any>this.buttons[2]).style.visibility = "hidden";
|
||||
@ -1453,15 +1723,31 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
private attachEvents() {
|
||||
this.attachIFrameEvents();
|
||||
this.attachAccelerometerEvents();
|
||||
this.attachPinsIOEvents();
|
||||
this.attachPinsTouchEvents();
|
||||
this.attachABEvents();
|
||||
this.attachAPlusBEvents();
|
||||
}
|
||||
|
||||
private attachIFrameEvents() {
|
||||
Runtime.messagePosted = (msg) => {
|
||||
switch (msg.type || "") {
|
||||
case "serial": this.flashSystemLed(); break;
|
||||
case "radiopacket": this.flashAntenna(); break;
|
||||
case "eventbus":
|
||||
if ((<pxsim.SimulatorEventBusMessage>msg).id == DAL.MES_BROADCAST_GENERAL_ID)
|
||||
this.flashAntenna();
|
||||
break;
|
||||
}
|
||||
}
|
||||
let tiltDecayer = 0;
|
||||
}
|
||||
|
||||
private attachAccelerometerEvents() {
|
||||
let tiltDecayer: any = undefined;
|
||||
this.element.addEventListener(pointerEvents.move, (ev: MouseEvent) => {
|
||||
let state = this.board;
|
||||
const state = this.board;
|
||||
if (!state.accelerometerState.accelerometer.isActive) return;
|
||||
|
||||
if (tiltDecayer) {
|
||||
@ -1469,14 +1755,19 @@ namespace pxsim.visuals {
|
||||
tiltDecayer = 0;
|
||||
}
|
||||
|
||||
let bbox = this.element.getBoundingClientRect();
|
||||
let ax = (ev.clientX - bbox.width / 2) / (bbox.width / 3);
|
||||
let ay = (ev.clientY - bbox.height / 2) / (bbox.height / 3);
|
||||
const bbox = this.element.getBoundingClientRect();
|
||||
|
||||
let x = - Math.max(- 1023, Math.min(1023, Math.floor(ax * 1023)));
|
||||
let y = - Math.max(- 1023, Math.min(1023, Math.floor(ay * 1023)));
|
||||
let z2 = 1023 * 1023 - x * x - y * y;
|
||||
let z = Math.floor((z2 > 0 ? -1 : 1) * Math.sqrt(Math.abs(z2)));
|
||||
// ev.clientX and ev.clientY are not defined on mobile iOS
|
||||
const xPos = ev.clientX != null ? ev.clientX : ev.pageX;
|
||||
const yPos = ev.clientY != null ? ev.clientY : ev.pageY;
|
||||
|
||||
const ax = (xPos - bbox.width / 2) / (bbox.width / 3);
|
||||
const ay = (yPos - bbox.height / 2) / (bbox.height / 3);
|
||||
|
||||
const x = - Math.max(- 1023, Math.min(1023, Math.floor(ax * 1023)));
|
||||
const y = - Math.max(- 1023, Math.min(1023, Math.floor(ay * 1023)));
|
||||
const z2 = 1023 * 1023 - x * x - y * y;
|
||||
const z = Math.floor((z2 > 0 ? -1 : 1) * Math.sqrt(Math.abs(z2)));
|
||||
|
||||
state.accelerometerState.accelerometer.update(x, y, z);
|
||||
this.updateTilt();
|
||||
@ -1504,10 +1795,15 @@ namespace pxsim.visuals {
|
||||
}, 50)
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
this.pins.forEach((pin, index) => {
|
||||
private attachPinsIOEvents() {
|
||||
this.pins.slice(2, 6).forEach((pin, index) => {
|
||||
// var index = i + 2;
|
||||
if (!this.board.edgeConnectorState.pins[index]) return;
|
||||
let pt = this.element.createSVGPoint();
|
||||
let xpos = (index === 0 || index === 3) ? 300 : 520;
|
||||
let vMax = (index === 0 || index === 3) ? 1 : 1032;
|
||||
svg.buttonEvents(pin,
|
||||
// move
|
||||
ev => {
|
||||
@ -1516,8 +1812,8 @@ namespace pxsim.visuals {
|
||||
let svgpin = this.pins[index];
|
||||
if (pin.mode & PinFlags.Input) {
|
||||
let cursor = svg.cursorPoint(pt, this.element, ev);
|
||||
let v = (400 - cursor.y) / 40 * 1023
|
||||
pin.value = Math.max(0, Math.min(1023, Math.floor(v)));
|
||||
let v = (xpos - cursor.y) / 70 * (vMax + 1);
|
||||
pin.value = Math.max(0, Math.min(vMax, Math.floor(v)));
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
},
|
||||
@ -1529,8 +1825,8 @@ namespace pxsim.visuals {
|
||||
U.addClass(svgpin, "touched");
|
||||
if (pin.mode & PinFlags.Input) {
|
||||
let cursor = svg.cursorPoint(pt, this.element, ev);
|
||||
let v = (400 - cursor.y) / 40 * 1023
|
||||
pin.value = Math.max(0, Math.min(1023, Math.floor(v)));
|
||||
let v = (xpos - cursor.y) / 70 * (vMax + 1);
|
||||
pin.value = Math.max(0, Math.min(vMax, Math.floor(v)));
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
},
|
||||
@ -1542,82 +1838,154 @@ namespace pxsim.visuals {
|
||||
U.removeClass(svgpin, "touched");
|
||||
this.updatePin(pin, index);
|
||||
return false;
|
||||
},
|
||||
// keydown
|
||||
(ev: KeyboardEvent) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
let state = this.board;
|
||||
let pin = state.edgeConnectorState.pins[index];
|
||||
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
pin.value -= 10;
|
||||
if (pin.value < 0) {
|
||||
pin.value = 1023;
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
pin.value += 10;
|
||||
if (pin.value > 1023) {
|
||||
pin.value = 0;
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
}
|
||||
});
|
||||
})
|
||||
this.pins.slice(0, 2).forEach((btn, index) => {
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = true;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
})
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let bpState = this.board.buttonPairState;
|
||||
let stateButtons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
|
||||
this.buttonsOuter.slice(0, 2).forEach((btn, index) => {
|
||||
private attachPinsTouchEvents() {
|
||||
this.pins.slice(2, 6).forEach((btn, i) => {
|
||||
var index = i + 2;
|
||||
let state = this.board;
|
||||
let pressedTime: number;
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[index].pressed = true;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
// console.log(`down ${state.edgeConnectorState.pins[i].id}`)
|
||||
state.edgeConnectorState.pins[i].touched = true;
|
||||
this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonUps[index]);
|
||||
})
|
||||
// btn.addEventListener(pointerEvents.leave, ev => {
|
||||
// let state = this.board;
|
||||
// state.edgeConnectorState.pins[i].touched = false;
|
||||
// this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
// })
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
// console.log(`up ${state.edgeConnectorState.pins[i].id}, index ${index}`)
|
||||
state.edgeConnectorState.pins[i].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME) {
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
// console.log(`& long click`)
|
||||
} else {
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
// console.log(`& click`)
|
||||
}
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(btn, undefined, () => {
|
||||
let state = this.board;
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
private attachABEvents() {
|
||||
const bpState = this.board.buttonPairState;
|
||||
const stateButtons: Button[] = [bpState.aBtn, bpState.bBtn];
|
||||
const elButtonOuters = this.buttonsOuter.slice(0, 2);
|
||||
const elButtons = this.buttons.slice(0, 2);
|
||||
|
||||
elButtonOuters.forEach((btn, index) => {
|
||||
let pressedTime: number;
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
// console.log(`down ${stateButtons[index].id}`)
|
||||
stateButtons[index].pressed = true;
|
||||
svg.fill(elButtons[index], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonUps[index]);
|
||||
svg.fill(elButtons[index], this.props.theme.buttonUps[0]);
|
||||
})
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(elButtons[index], this.props.theme.buttonUps[0]);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME)
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
else
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(btn, undefined, () => {
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
private attachAPlusBEvents() {
|
||||
const bpState = this.board.buttonPairState;
|
||||
let pressedTime: number;
|
||||
// A+B
|
||||
pointerEvents.down.forEach(evid => this.buttonsOuter[2].addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = true;
|
||||
stateButtons[1].pressed = true;
|
||||
stateButtons[2].pressed = true;
|
||||
bpState.aBtn.pressed = true;
|
||||
bpState.bBtn.pressed = true;
|
||||
bpState.abBtn.pressed = true;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonDown);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonDown);
|
||||
svg.fill(this.buttons[2], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
this.buttonsOuter[2].addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = false;
|
||||
stateButtons[1].pressed = false;
|
||||
stateButtons[2].pressed = false;
|
||||
bpState.aBtn.pressed = false;
|
||||
bpState.bBtn.pressed = false;
|
||||
bpState.abBtn.pressed = false;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonUps[0]);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonUps[1]);
|
||||
svg.fill(this.buttons[2], this.props.theme.virtualButtonUp);
|
||||
})
|
||||
this.buttonsOuter[2].addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = false;
|
||||
stateButtons[1].pressed = false;
|
||||
stateButtons[2].pressed = false;
|
||||
bpState.aBtn.pressed = false;
|
||||
bpState.bBtn.pressed = false;
|
||||
bpState.abBtn.pressed = false;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonUps[0]);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonUps[1]);
|
||||
svg.fill(this.buttons[2], this.props.theme.virtualButtonUp);
|
||||
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME)
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
else
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(this.buttonsOuter[2], undefined, () => {
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user