UNPKG

pxt-microbit

Version:

micro:bit target for Microsoft MakeCode (PXT)

1,257 lines • 287 kB
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/> /// <reference path="../libs/core/dal.d.ts"/> /// <reference path="../libs/core/enums.d.ts"/> var pxsim; (function (pxsim) { class DalBoard extends pxsim.CoreBoard { constructor() { super(); this.speakerEnabled = true; // board hardware version this.hardwareVersion = 1; // components this.lightState = {}; this.fileSystem = new pxsim.FileSystemState(); this.controlMessageState = new pxsim.ControlMessageState(this); this.builtinParts["ledmatrix"] = this.ledMatrixState = new pxsim.LedMatrixState(pxsim.runtime); this.builtinParts["buttonpair"] = this.buttonPairState = new pxsim.ButtonPairState({ ID_BUTTON_A: 1 /* DAL.MICROBIT_ID_BUTTON_A */, ID_BUTTON_B: 2 /* DAL.MICROBIT_ID_BUTTON_B */, ID_BUTTON_AB: 3 /* DAL.MICROBIT_ID_BUTTON_AB */, BUTTON_EVT_UP: 2 /* DAL.MICROBIT_BUTTON_EVT_UP */, BUTTON_EVT_CLICK: 3 /* DAL.MICROBIT_BUTTON_EVT_CLICK */ }); this.builtinParts["edgeconnector"] = this.edgeConnectorState = new pxsim.EdgeConnectorState({ pins: [ 100 /* DAL.MICROBIT_ID_IO_P0 */, 101 /* DAL.MICROBIT_ID_IO_P1 */, 102 /* DAL.MICROBIT_ID_IO_P2 */, 103 /* DAL.MICROBIT_ID_IO_P3 */, 104 /* DAL.MICROBIT_ID_IO_P4 */, 105 /* DAL.MICROBIT_ID_IO_P5 */, 106 /* DAL.MICROBIT_ID_IO_P6 */, 107 /* DAL.MICROBIT_ID_IO_P7 */, 108 /* DAL.MICROBIT_ID_IO_P8 */, 109 /* DAL.MICROBIT_ID_IO_P9 */, 110 /* DAL.MICROBIT_ID_IO_P10 */, 111 /* DAL.MICROBIT_ID_IO_P11 */, 112 /* DAL.MICROBIT_ID_IO_P12 */, 113 /* DAL.MICROBIT_ID_IO_P13 */, 114 /* DAL.MICROBIT_ID_IO_P14 */, 115 /* DAL.MICROBIT_ID_IO_P15 */, 116 /* DAL.MICROBIT_ID_IO_P16 */, 0, 0, 119 /* DAL.MICROBIT_ID_IO_P19 */, 120 /* DAL.MICROBIT_ID_IO_P20 */ ], servos: { "P0": 100 /* DAL.MICROBIT_ID_IO_P0 */, "P1": 101 /* DAL.MICROBIT_ID_IO_P1 */, "P2": 102 /* DAL.MICROBIT_ID_IO_P2 */, "P3": 103 /* DAL.MICROBIT_ID_IO_P3 */, "P4": 104 /* DAL.MICROBIT_ID_IO_P4 */, "P5": 105 /* DAL.MICROBIT_ID_IO_P5 */, "P6": 106 /* DAL.MICROBIT_ID_IO_P6 */, "P7": 107 /* DAL.MICROBIT_ID_IO_P7 */, "P8": 108 /* DAL.MICROBIT_ID_IO_P8 */, "P9": 109 /* DAL.MICROBIT_ID_IO_P9 */, "P10": 110 /* DAL.MICROBIT_ID_IO_P10 */, "P11": 111 /* DAL.MICROBIT_ID_IO_P11 */, "P12": 112 /* DAL.MICROBIT_ID_IO_P12 */, "P13": 113 /* DAL.MICROBIT_ID_IO_P13 */, "P14": 114 /* DAL.MICROBIT_ID_IO_P14 */, "P15": 115 /* DAL.MICROBIT_ID_IO_P15 */, "P16": 116 /* DAL.MICROBIT_ID_IO_P16 */, "P19": 119 /* DAL.MICROBIT_ID_IO_P19 */ } }); this.builtinParts["radio"] = this.radioState = new pxsim.RadioState(pxsim.runtime, this, { ID_RADIO: 9 /* DAL.MICROBIT_ID_RADIO */, RADIO_EVT_DATAGRAM: 1 /* DAL.MICROBIT_RADIO_EVT_DATAGRAM */ }); this.builtinParts["microphone"] = this.microphoneState = new pxsim.MicrophoneState(3001 /* DAL.DEVICE_ID_MICROPHONE */, 0, 255, 86, 165); this.builtinParts["recording"] = this.recordingState = new pxsim.RecordingState(); this.builtinParts["accelerometer"] = this.accelerometerState = new pxsim.AccelerometerState(pxsim.runtime); this.builtinParts["serial"] = this.serialState = new pxsim.SerialState(pxsim.runtime, this); this.builtinParts["thermometer"] = this.thermometerState = new pxsim.ThermometerState(); this.builtinParts["lightsensor"] = this.lightSensorState = new pxsim.LightSensorState(); this.builtinParts["compass"] = this.compassState = new pxsim.CompassState(); this.builtinParts["microservo"] = this.edgeConnectorState; this.builtinParts["logotouch"] = this.logoTouch = new pxsim.Button(121 /* DAL.MICROBIT_ID_LOGO */); this.builtinVisuals["buttonpair"] = () => new pxsim.visuals.ButtonPairView(); this.builtinVisuals["ledmatrix"] = () => new pxsim.visuals.LedMatrixView(); this.builtinVisuals["microservo"] = () => new pxsim.visuals.MicroServoView(); this.builtinParts["neopixel"] = (pin) => { return this.neopixelState(pin.id); }; this.builtinVisuals["neopixel"] = () => new pxsim.visuals.NeoPixelView(pxsim.parsePinString); this.builtinPartVisuals["neopixel"] = (xy) => pxsim.visuals.mkNeoPixelPart(xy); this.builtinPartVisuals["buttonpair"] = (xy) => pxsim.visuals.mkBtnSvg(xy); this.builtinPartVisuals["ledmatrix"] = (xy) => pxsim.visuals.mkLedMatrixSvg(xy, 8, 8); this.builtinPartVisuals["microservo"] = (xy) => pxsim.visuals.mkMicroServoPart(xy); this.samplesState = new pxsim.samples.SamplesState(); } ensureHardwareVersion(version) { if (version > this.hardwareVersion) { this.hardwareVersion = version; this.updateView(); } } initAsync(msg) { super.initAsync(msg); const boardDef = msg.boardDefinition; const cmpsList = msg.parts; const cmpDefs = msg.partDefinitions || {}; const fnArgs = msg.fnArgs; const v2Parts = { "microphone": true, "logotouch": true, "builtinspeaker": true, "flashlog": 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 = { state: this, boardDef: boardDef, partsList: cmpsList, partDefs: cmpDefs, fnArgs: fnArgs, maxWidth: "100%", maxHeight: "100%", highContrast: msg.highContrast }; this.viewHost = new pxsim.visuals.BoardHost(pxsim.visuals.mkBoardView({ visual: boardDef.visual, boardDef: boardDef, highContrast: msg.highContrast }), opts); document.body.innerHTML = ""; // clear children if (pxsim.shouldShowMute()) { document.body.appendChild(pxsim.createMuteButton()); pxsim.AudioContextManager.mute(true); pxsim.setParentMuteState("disabled"); } document.body.appendChild(this.view = this.viewHost.getView()); if (msg.theme === "mbcodal") { this.ensureHardwareVersion(2); } return Promise.resolve(); } tryGetNeopixelState(pinId) { return this.lightState[pinId]; } neopixelState(pinId) { if (pinId === undefined) { pinId = 100 /* DAL.MICROBIT_ID_IO_P0 */; } let state = this.lightState[pinId]; if (!state) state = this.lightState[pinId] = new pxsim.CommonNeoPixelState(); return state; } screenshotAsync(width) { return this.viewHost.screenshotAsync(width); } kill() { super.kill(); this.viewHost.removeEventListeners(); } } pxsim.DalBoard = DalBoard; function initRuntimeWithDalBoard() { pxsim.U.assert(!pxsim.runtime.board); let b = new DalBoard(); pxsim.runtime.board = b; pxsim.runtime.postError = (e) => { pxsim.led.setBrightness(255); let img = board().ledMatrixState.image; img.clear(); img.set(0, 4, 255); img.set(1, 3, 255); img.set(2, 3, 255); img.set(3, 3, 255); img.set(4, 4, 255); img.set(0, 0, 255); img.set(1, 0, 255); img.set(0, 1, 255); img.set(1, 1, 255); img.set(3, 0, 255); img.set(4, 0, 255); img.set(3, 1, 255); img.set(4, 1, 255); pxsim.runtime.updateDisplay(); }; } pxsim.initRuntimeWithDalBoard = initRuntimeWithDalBoard; if (!pxsim.initCurrentRuntime) { pxsim.initCurrentRuntime = initRuntimeWithDalBoard; } function board() { return pxsim.runtime.board; } pxsim.board = board; function parsePinString(gpioPin) { if (gpioPin == "*") return board().edgeConnectorState.getPin(100 /* 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]; } pxsim.parsePinString = parsePinString; })(pxsim || (pxsim = {})); var pxsim; (function (pxsim) { var input; (function (input) { function accForGesture(gesture) { let b = pxsim.board().accelerometerState; b.accelerometer.activate(); if (gesture == 11 && !b.useShake) { // SHAKE b.useShake = true; pxsim.runtime.queueDisplayUpdate(); } return b; } function onGesture(gesture, handler) { const b = accForGesture(gesture); pxsim.pxtcore.registerWithDal(13 /* DAL.MICROBIT_ID_GESTURE */, gesture, handler); } input.onGesture = onGesture; function isGesture(gesture) { const b = accForGesture(gesture); b.accelerometer.activate(); return b.accelerometer.getGesture() == gesture; } input.isGesture = isGesture; function acceleration(dimension) { let b = pxsim.board().accelerometerState; let acc = b.accelerometer; switch (dimension) { case 0: acc.activate(pxsim.AccelerometerFlag.X); return acc.getX(); case 1: acc.activate(pxsim.AccelerometerFlag.Y); return acc.getY(); case 2: acc.activate(pxsim.AccelerometerFlag.Z); return acc.getZ(); default: acc.activate(pxsim.AccelerometerFlag.Strength); return acc.getStrength(); } } input.acceleration = acceleration; function rotation(kind) { const b = pxsim.board().accelerometerState; const acc = b.accelerometer; switch (kind) { case 0: { acc.activate(pxsim.AccelerometerFlag.Pitch); return acc.getPitch(); } case 1: { acc.activate(pxsim.AccelerometerFlag.Roll); return acc.getRoll(); } default: return 0; } } input.rotation = rotation; function setAccelerometerRange(range) { let b = pxsim.board().accelerometerState; b.accelerometer.setSampleRange(range); } input.setAccelerometerRange = setAccelerometerRange; })(input = pxsim.input || (pxsim.input = {})); })(pxsim || (pxsim = {})); (function (pxsim) { /** * Co-ordinate systems that can be used. * RAW: Unaltered data. Data will be returned directly from the accelerometer. * * SIMPLE_CARTESIAN: Data will be returned based on an easy to understand alignment, consistent with the cartesian system taught in schools. * When held upright, facing the user: * * / * +--------------------+ z * | | * | ..... | * | * ..... * | * ^ | ..... | * | | | * y +--------------------+ x--> * * * NORTH_EAST_DOWN: Data will be returned based on the industry convention of the North East Down (NED) system. * When held upright, facing the user: * * z * +--------------------+ / * | | * | ..... | * | * ..... * | * ^ | ..... | * | | | * x +--------------------+ y--> * */ let MicroBitCoordinateSystem; (function (MicroBitCoordinateSystem) { MicroBitCoordinateSystem[MicroBitCoordinateSystem["RAW"] = 0] = "RAW"; MicroBitCoordinateSystem[MicroBitCoordinateSystem["SIMPLE_CARTESIAN"] = 1] = "SIMPLE_CARTESIAN"; MicroBitCoordinateSystem[MicroBitCoordinateSystem["NORTH_EAST_DOWN"] = 2] = "NORTH_EAST_DOWN"; })(MicroBitCoordinateSystem = pxsim.MicroBitCoordinateSystem || (pxsim.MicroBitCoordinateSystem = {})); let AccelerometerFlag; (function (AccelerometerFlag) { AccelerometerFlag[AccelerometerFlag["X"] = 1] = "X"; AccelerometerFlag[AccelerometerFlag["Y"] = 2] = "Y"; AccelerometerFlag[AccelerometerFlag["Z"] = 4] = "Z"; AccelerometerFlag[AccelerometerFlag["Strength"] = 8] = "Strength"; AccelerometerFlag[AccelerometerFlag["Pitch"] = 16] = "Pitch"; AccelerometerFlag[AccelerometerFlag["Roll"] = 32] = "Roll"; })(AccelerometerFlag = pxsim.AccelerometerFlag || (pxsim.AccelerometerFlag = {})); class Accelerometer { constructor(runtime) { this.runtime = runtime; this.sigma = 0; // the number of ticks that the instantaneous gesture has been stable. this.lastGesture = 0; // the last, stable gesture recorded. this.currentGesture = 0; // the instantaneous, unfiltered gesture detected. this.sample = { x: 0, y: 0, z: -1023 }; this.shake = { x: false, y: false, z: false, count: 0, shaken: 0, timer: 0 }; // State information needed to detect shake events. this.isActive = false; this.sampleRange = 2; this.flags = 0; this.id = 5 /* DAL.MICROBIT_ID_ACCELEROMETER */; } setSampleRange(range) { this.activate(); this.sampleRange = Math.max(1, Math.min(8, range)); } activate(flags) { if (!this.isActive) { this.isActive = true; this.runtime.queueDisplayUpdate(); } if (!!flags) this.flags |= flags; } /** * Reads the acceleration data from the accelerometer, and stores it in our buffer. * This is called by the tick() member function, if the interrupt is set! */ update(x, y, z) { // read MSB values... this.sample.x = Math.floor(x); this.sample.y = Math.floor(y); this.sample.z = Math.floor(z); // Update gesture tracking this.updateGesture(); // Indicate that a new sample is available pxsim.board().bus.queue(this.id, 1 /* DAL.MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE */); } 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(); } } 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; } /** * Service function. Determines the best guess posture of the device based on instantaneous data. * This makes no use of historic data (except for shake), and forms this input to the filter implemented in updateGesture(). * * @return A best guess of the current posture of the device, based on instantaneous data. */ instantaneousPosture() { let force = this.instantaneousAccelerationSquared(); let shakeDetected = false; // Test for shake events. // We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by // a string acceleration to the right, then we can infer a shake. Similarly, we can do this for each acxis (left/right, up/down, in/out). // // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device // has been shaken. if ((this.getX() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.x) || (this.getX() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.x)) { shakeDetected = true; this.shake.x = !this.shake.x; } if ((this.getY() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.y) || (this.getY() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.y)) { shakeDetected = true; this.shake.y = !this.shake.y; } if ((this.getZ() < -400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && this.shake.z) || (this.getZ() > 400 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE */ && !this.shake.z)) { shakeDetected = true; this.shake.z = !this.shake.z; } if (shakeDetected && this.shake.count < 4 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD */ && ++this.shake.count == 4 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD */) this.shake.shaken = 1; if (++this.shake.timer >= 10 /* DAL.MICROBIT_ACCELEROMETER_SHAKE_DAMPING */) { this.shake.timer = 0; if (this.shake.count > 0) { if (--this.shake.count == 0) this.shake.shaken = 0; } } if (this.shake.shaken) return 11 /* DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE */; let sq = (n) => n * n; if (force < sq(400 /* DAL.MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE */)) return 7 /* DAL.MICROBIT_ACCELEROMETER_EVT_FREEFALL */; if (force > sq(3072 /* DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE */)) return 8 /* DAL.MICROBIT_ACCELEROMETER_EVT_3G */; if (force > sq(6144 /* DAL.MICROBIT_ACCELEROMETER_6G_TOLERANCE */)) return 9 /* DAL.MICROBIT_ACCELEROMETER_EVT_6G */; if (force > sq(8192 /* DAL.MICROBIT_ACCELEROMETER_8G_TOLERANCE */)) return 10 /* DAL.MICROBIT_ACCELEROMETER_EVT_8G */; // Determine our posture. if (this.getX() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 3 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_LEFT */; if (this.getX() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 4 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT */; if (this.getY() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 2 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_DOWN */; if (this.getY() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 1 /* DAL.MICROBIT_ACCELEROMETER_EVT_TILT_UP */; if (this.getZ() < (-1000 + 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 5 /* DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP */; if (this.getZ() > (1000 - 200 /* DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE */)) return 6 /* DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN */; return 0; } updateGesture() { // Determine what it looks like we're doing based on the latest sample... let g = this.instantaneousPosture(); // Perform some low pass filtering to reduce jitter from any detected effects if (g != this.currentGesture) { this.currentGesture = g; this.sigma = 0; } else if (this.sigma < 5 /* DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING */) { ++this.sigma; } if (this.currentGesture !== this.lastGesture && this.sigma >= 5 /* DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING */) { this.enqueueCurrentGesture(); } } forceGesture(gesture) { this.currentGesture = gesture; this.enqueueCurrentGesture(); } enqueueCurrentGesture() { this.lastGesture = this.currentGesture; pxsim.board().bus.queue(13 /* DAL.MICROBIT_ID_GESTURE */, this.lastGesture); } /** * Reads the X axis value of the latest update from the accelerometer. * @param system The coordinate system to use. By default, a simple cartesian system is provided. * @return The force measured in the X axis, in milli-g. * * Example: * @code * uBit.accelerometer.getX(); * uBit.accelerometer.getX(RAW); * @endcode */ getX(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) { switch (system) { case MicroBitCoordinateSystem.SIMPLE_CARTESIAN: return -this.sample.x; case MicroBitCoordinateSystem.NORTH_EAST_DOWN: return this.sample.y; //case MicroBitCoordinateSystem.SIMPLE_CARTESIAN.RAW: default: return this.sample.x; } } /** * Reads the Y axis value of the latest update from the accelerometer. * @param system The coordinate system to use. By default, a simple cartesian system is provided. * @return The force measured in the Y axis, in milli-g. * * Example: * @code * uBit.accelerometer.getY(); * uBit.accelerometer.getY(RAW); * @endcode */ getY(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) { switch (system) { case MicroBitCoordinateSystem.SIMPLE_CARTESIAN: return -this.sample.y; case MicroBitCoordinateSystem.NORTH_EAST_DOWN: return -this.sample.x; //case RAW: default: return this.sample.y; } } /** * Reads the Z axis value of the latest update from the accelerometer. * @param system The coordinate system to use. By default, a simple cartesian system is provided. * @return The force measured in the Z axis, in milli-g. * * Example: * @code * uBit.accelerometer.getZ(); * uBit.accelerometer.getZ(RAW); * @endcode */ getZ(system = MicroBitCoordinateSystem.SIMPLE_CARTESIAN) { switch (system) { case MicroBitCoordinateSystem.NORTH_EAST_DOWN: return -this.sample.z; //case MicroBitCoordinateSystem.SIMPLE_CARTESIAN: //case MicroBitCoordinateSystem.RAW: default: return this.sample.z; } } /** * Provides a rotation compensated pitch of the device, based on the latest update from the accelerometer. * @return The pitch of the device, in degrees. * * Example: * @code * uBit.accelerometer.getPitch(); * @endcode */ getPitch() { return Math.floor((360 * this.getPitchRadians()) / (2 * Math.PI)); } getPitchRadians() { this.recalculatePitchRoll(); return this.pitch; } /** * Provides a rotation compensated roll of the device, based on the latest update from the accelerometer. * @return The roll of the device, in degrees. * * Example: * @code * uBit.accelerometer.getRoll(); * @endcode */ getRoll() { return Math.floor((360 * this.getRollRadians()) / (2 * Math.PI)); } getRollRadians() { this.recalculatePitchRoll(); return this.roll; } getGesture() { return this.lastGesture; } /** * Recalculate roll and pitch values for the current sample. * We only do this at most once per sample, as the necessary trigonemteric functions are rather * heavyweight for a CPU without a floating point unit... */ recalculatePitchRoll() { let x = this.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN); let y = this.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN); let z = this.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN); this.roll = Math.atan2(y, z); this.pitch = Math.atan(-x / (y * Math.sin(this.roll) + z * Math.cos(this.roll))); } } pxsim.Accelerometer = Accelerometer; class AccelerometerState { constructor(runtime) { this.useShake = false; this.accelerometer = new Accelerometer(runtime); } shake() { this.accelerometer.forceGesture(11 /* DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE */); // SHAKE == 11 } } pxsim.AccelerometerState = AccelerometerState; })(pxsim || (pxsim = {})); var pxsim; (function (pxsim) { var pxtcore; (function (pxtcore) { function updateScreen(img) { } pxtcore.updateScreen = updateScreen; function updateStats(s) { } pxtcore.updateStats = updateStats; function setPalette(b) { } pxtcore.setPalette = setPalette; function setScreenBrightness(b) { } pxtcore.setScreenBrightness = setScreenBrightness; function displayHeight() { return 120; } pxtcore.displayHeight = displayHeight; function displayWidth() { return 160; } pxtcore.displayWidth = displayWidth; function displayPresent() { return true; } pxtcore.displayPresent = displayPresent; })(pxtcore = pxsim.pxtcore || (pxsim.pxtcore = {})); })(pxsim || (pxsim = {})); var pxsim; (function (pxsim) { var samples; (function (samples) { class SampleChannel { constructor(id) { this.id = id; this.sampleRate = 11000; } playSampleAsync(sample) { if (this.playing) { this.playing.cancel(); } this.playing = pxsim.AudioContextManager.startSamplePlayback(sample, pxsim.BufferMethods.NumberFormat.UInt8LE, 255, this.sampleRate, pxsim.music.volume() / 0xff); this.playing.promise.then(() => { this.playing = undefined; }); } } class SamplesState { constructor() { this.channels = []; this.enabled = false; this.channels = [ new SampleChannel(0), new SampleChannel(1), new SampleChannel(2), new SampleChannel(3), ]; } setEnabled(enabled) { this.enabled = enabled; } setSampleRate(channelId, sampleRate) { channelId |= 0; if (channelId < 0 || channelId >= this.channels.length) { return; } this.channels[channelId].sampleRate = sampleRate; } playSampleAsync(channelId, sample) { if (!this.enabled) { return; } channelId |= 0; if (channelId < 0 || channelId >= this.channels.length) { return; } this.channels[channelId].playSampleAsync(sample); } } samples.SamplesState = SamplesState; function enable() { pxsim.board().samplesState.setEnabled(true); } samples.enable = enable; function disable() { pxsim.board().samplesState.setEnabled(false); } samples.disable = disable; function setSampleRate(src, sampleRate) { pxsim.board().samplesState.setSampleRate(src, sampleRate); } samples.setSampleRate = setSampleRate; function playAsync(src, buf) { pxsim.board().samplesState.playSampleAsync(src, buf); } samples.playAsync = playAsync; })(samples = pxsim.samples || (pxsim.samples = {})); })(pxsim || (pxsim = {})); var pxsim; (function (pxsim) { class RefImage extends pxsim.RefObject { constructor(w, h, bpp) { super(); this.isStatic = true; this.revision = 0; this.data = new Uint8Array(w * h); this._width = w; this._height = h; this._bpp = bpp; } scan(mark) { } gcKey() { return "Image"; } gcSize() { return 4 + (this.data.length + 3 >> 3); } gcIsStatic() { return this.isStatic; } pix(x, y) { return (x | 0) + (y | 0) * this._width; } inRange(x, y) { return 0 <= (x | 0) && (x | 0) < this._width && 0 <= (y | 0) && (y | 0) < this._height; } color(c) { return c & 0xff; } clamp(x, y) { x |= 0; y |= 0; if (x < 0) x = 0; else if (x >= this._width) x = this._width - 1; if (y < 0) y = 0; else if (y >= this._height) y = this._height - 1; return [x, y]; } makeWritable() { this.revision++; this.isStatic = false; } toDebugString() { return this._width + "x" + this._height; } } pxsim.RefImage = RefImage; })(pxsim || (pxsim = {})); (function (pxsim) { var BitmapMethods; (function (BitmapMethods) { function XX(x) { return (x << 16) >> 16; } BitmapMethods.XX = XX; function YY(x) { return x >> 16; } BitmapMethods.YY = YY; function __buffer(img) { return new pxsim.RefBuffer(img.data); // no clone for now } BitmapMethods.__buffer = __buffer; function width(img) { return img._width; } BitmapMethods.width = width; function height(img) { return img._height; } BitmapMethods.height = height; function isMono(img) { return img._bpp == 1; } BitmapMethods.isMono = isMono; function isStatic(img) { return img.gcIsStatic(); } BitmapMethods.isStatic = isStatic; function revision(img) { return img.revision; } BitmapMethods.revision = revision; function setPixel(img, x, y, c) { img.makeWritable(); if (img.inRange(x, y)) img.data[img.pix(x, y)] = img.color(c); } BitmapMethods.setPixel = setPixel; function getPixel(img, x, y) { if (img.inRange(x, y)) return img.data[img.pix(x, y)]; return 0; } BitmapMethods.getPixel = getPixel; function fill(img, c) { img.makeWritable(); img.data.fill(img.color(c)); } BitmapMethods.fill = fill; function fillRect(img, x, y, w, h, c) { if (w == 0 || h == 0 || x >= img._width || y >= img._height || x + w - 1 < 0 || y + h - 1 < 0) return; img.makeWritable(); let [x2, y2] = img.clamp(x + w - 1, y + h - 1); [x, y] = img.clamp(x, y); let p = img.pix(x, y); w = x2 - x + 1; h = y2 - y + 1; let d = img._width - w; c = img.color(c); while (h-- > 0) { for (let i = 0; i < w; ++i) img.data[p++] = c; p += d; } } BitmapMethods.fillRect = fillRect; function _fillRect(img, xy, wh, c) { fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); } BitmapMethods._fillRect = _fillRect; function mapRect(img, x, y, w, h, c) { if (c.data.length < 16) return; img.makeWritable(); let [x2, y2] = img.clamp(x + w - 1, y + h - 1); [x, y] = img.clamp(x, y); let p = img.pix(x, y); w = x2 - x + 1; h = y2 - y + 1; let d = img._width - w; while (h-- > 0) { for (let i = 0; i < w; ++i) { img.data[p] = c.data[img.data[p]]; p++; } p += d; } } BitmapMethods.mapRect = mapRect; function _mapRect(img, xy, wh, c) { mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); } BitmapMethods._mapRect = _mapRect; function equals(img, other) { if (!other || img._bpp != other._bpp || img._width != other._width || img._height != other._height) { return false; } let imgData = img.data; let otherData = other.data; let len = imgData.length; for (let i = 0; i < len; i++) { if (imgData[i] != otherData[i]) { return false; } } return true; } BitmapMethods.equals = equals; function getRows(img, x, dst) { x |= 0; if (!img.inRange(x, 0)) return; let dp = 0; let len = Math.min(dst.data.length, (img._width - x) * img._height); let sp = x; let hh = 0; while (len--) { if (hh++ >= img._height) { hh = 1; sp = ++x; } dst.data[dp++] = img.data[sp]; sp += img._width; } } BitmapMethods.getRows = getRows; function setRows(img, x, src) { x |= 0; if (!img.inRange(x, 0)) return; let sp = 0; let len = Math.min(src.data.length, (img._width - x) * img._height); let dp = x; let hh = 0; while (len--) { if (hh++ >= img._height) { hh = 1; dp = ++x; } img.data[dp] = src.data[sp++]; dp += img._width; } } BitmapMethods.setRows = setRows; function clone(img) { let r = new pxsim.RefImage(img._width, img._height, img._bpp); r.data.set(img.data); return r; } BitmapMethods.clone = clone; function flipX(img) { img.makeWritable(); const w = img._width; const h = img._height; for (let i = 0; i < h; ++i) { img.data.subarray(i * w, (i + 1) * w).reverse(); } } BitmapMethods.flipX = flipX; function flipY(img) { img.makeWritable(); const w = img._width; const h = img._height; const d = img.data; for (let i = 0; i < w; ++i) { let top = i; let bot = i + (h - 1) * w; while (top < bot) { let c = d[top]; d[top] = d[bot]; d[bot] = c; top += w; bot -= w; } } } BitmapMethods.flipY = flipY; function transposed(img) { const w = img._width; const h = img._height; const d = img.data; const r = new pxsim.RefImage(h, w, img._bpp); const n = r.data; let src = 0; for (let i = 0; i < h; ++i) { let dst = i; for (let j = 0; j < w; ++j) { n[dst] = d[src++]; dst += w; } } return r; } BitmapMethods.transposed = transposed; function copyFrom(img, from) { if (img._width != from._width || img._height != from._height || img._bpp != from._bpp) return; img.data.set(from.data); } BitmapMethods.copyFrom = copyFrom; function scroll(img, dx, dy) { img.makeWritable(); dx |= 0; dy |= 0; if (dx != 0) { const img2 = clone(img); img.data.fill(0); drawTransparentBitmap(img, img2, dx, dy); } else if (dy < 0) { dy = -dy; if (dy < img._height) img.data.copyWithin(0, dy * img._width); else dy = img._height; img.data.fill(0, (img._height - dy) * img._width); } else if (dy > 0) { if (dy < img._height) img.data.copyWithin(dy * img._width, 0); else dy = img._height; img.data.fill(0, 0, dy * img._width); } // TODO implement dx } BitmapMethods.scroll = scroll; function replace(img, from, to) { to &= 0xf; const d = img.data; for (let i = 0; i < d.length; ++i) if (d[i] == from) d[i] = to; } BitmapMethods.replace = replace; function doubledX(img) { const w = img._width; const h = img._height; const d = img.data; const r = new pxsim.RefImage(w * 2, h, img._bpp); const n = r.data; let dst = 0; for (let src = 0; src < d.length; ++src) { let c = d[src]; n[dst++] = c; n[dst++] = c; } return r; } BitmapMethods.doubledX = doubledX; function doubledY(img) { const w = img._width; const h = img._height; const d = img.data; const r = new pxsim.RefImage(w, h * 2, img._bpp); const n = r.data; let src = 0; let dst0 = 0; let dst1 = w; for (let i = 0; i < h; ++i) { for (let j = 0; j < w; ++j) { let c = d[src++]; n[dst0++] = c; n[dst1++] = c; } dst0 += w; dst1 += w; } return r; } BitmapMethods.doubledY = doubledY; function doubled(img) { return doubledX(doubledY(img)); } BitmapMethods.doubled = doubled; function drawImageCore(img, from, x, y, clear, check) { x |= 0; y |= 0; const w = from._width; let h = from._height; const sh = img._height; const sw = img._width; if (x + w <= 0) return false; if (x >= sw) return false; if (y + h <= 0) return false; if (y >= sh) return false; if (clear) fillRect(img, x, y, from._width, from._height, 0); else if (!check) img.makeWritable(); const len = x < 0 ? Math.min(sw, w + x) : Math.min(sw - x, w); const fdata = from.data; const tdata = img.data; for (let p = 0; h--; y++, p += w) { if (0 <= y && y < sh) { let dst = y * sw; let src = p; if (x < 0) src += -x; else dst += x; for (let i = 0; i < len; ++i) { const v = fdata[src++]; if (v) { if (check) { if (tdata[dst]) return true; } else { tdata[dst] = v; } } dst++; } } } return false; } function drawBitmap(img, from, x, y) { drawImageCore(img, from, x, y, true, false); } BitmapMethods.drawBitmap = drawBitmap; function drawTransparentBitmap(img, from, x, y) { drawImageCore(img, from, x, y, false, false); } BitmapMethods.drawTransparentBitmap = drawTransparentBitmap; function overlapsWith(img, other, x, y) { return drawImageCore(img, other, x, y, false, true); } BitmapMethods.overlapsWith = overlapsWith; function drawLineLow(img, x0, y0, x1, y1, c) { let dx = x1 - x0; let dy = y1 - y0; let yi = img._width; if (dy < 0) { yi = -yi; dy = -dy; } let D = 2 * dy - dx; dx <<= 1; dy <<= 1; c = img.color(c); let ptr = img.pix(x0, y0); for (let x = x0; x <= x1; ++x) { img.data[ptr] = c; if (D > 0) { ptr += yi; D -= dx; } D += dy; ptr++; } } function drawLineHigh(img, x0, y0, x1, y1, c) { let dx = x1 - x0; let dy = y1 - y0; let xi = 1; if (dx < 0) { xi = -1; dx = -dx; } let D = 2 * dx - dy; dx <<= 1; dy <<= 1; c = img.color(c); let ptr = img.pix(x0, y0); for (let y = y0; y <= y1; ++y) { img.data[ptr] = c; if (D > 0) { ptr += xi; D -= dy; } D += dx; ptr += img._width; } } function _drawLine(img, xy, wh, c) { drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c); } BitmapMethods._drawLine = _drawLine; function drawLine(img, x0, y0, x1, y1, c) { x0 |= 0; y0 |= 0; x1 |= 0; y1 |= 0; if (x1 < x0) { drawLine(img, x1, y1, x0, y0, c); return; } let w = x1 - x0; let h = y1 - y0; if (h == 0) { if (w == 0) setPixel(img, x0, y0, c); else fillRect(img, x0, y0, w + 1, 1, c); return; } if (w == 0) { if (h > 0) fillRect(img, x0, y0, 1, h + 1, c); else fillRect(img, x0, y1, 1, -h + 1, c); return; } if (x1 < 0 || x0 >= img._width) return; if (x0 < 0) { y0 -= (h * x0 / w) | 0; x0 = 0; } if (x1 >= img._width) { let d = (img._width - 1) - x1; y1 += (h * d / w) | 0; x1 = img._width - 1; } if (y0 < y1) { if (y0 >= img._height || y1 < 0) return; if (y0 < 0) { x0 -= (w * y0 / h) | 0; y0 = 0; } if (y1 >= img._height) { let d = (img._height - 1) - y1; x1 += (w * d / h) | 0; y1 = img._height; } } else { if (y1 >= img._height || y0 < 0) return; if (y1 < 0) { x1 -= (w * y1 / h) | 0; y1 = 0; } if (y0 >= img._height) { let d = (img._height - 1) - y0; x0 += (w * d / h) | 0; y0 = img._height; } } img.makeWritable(); if (h < 0) { h = -h; if (h < w) drawLineLow(img, x0, y0, x1, y1, c); else drawLineHigh(img, x1, y1, x0, y0, c); } else { if (h < w) drawLineLow(img, x0, y0, x1, y1, c); else drawLineHigh(img, x0, y0, x1, y1, c); } } BitmapMethods.drawLine = drawLine; function drawIcon(img, icon, x, y, color) { const src = icon.data; if (!pxsim.bitmaps.isValidImage(icon)) return; if (src[1] != 1) return; // only mono let width = pxsim.bitmaps.bufW(src); let height = pxsim.bitmaps.bufH(src); let byteH = pxsim.bitmaps.byteHeight(height, 1); x |= 0; y |= 0; const destHeight = img._height; const destWidth = img._width; if (x + width <= 0) return; if (x >= destWidth) return; if (y + height <= 0) return; if (y >= destHeight) return; img.makeWritable(); let srcPointer = 8; color = img.color(color); const screen = img.data; for (let i = 0; i < width; ++i) { let destX = x + i; if (0 <= destX && destX < destWidth) { let destIndex = destX + y * destWidth; let srcIndex = srcPointer; let destY = y; let destEnd = Math.min(destHeight, height + y); if (y < 0) { srcIndex += ((-y) >> 3); destY += ((-y) >> 3) * 8; destIndex += (destY - y) * destWidth; } let mask = 0x01; let srcByte = src[srcIndex++]; while (destY < destEnd) { if (destY >= 0 && (srcByte & mask)) { screen[destIndex] = color; } mask <<= 1; if (mask == 0x100) { mask = 0x01; srcByte = src[srcIndex++]; } destIndex += destWidth; destY++; } } srcPointer += byteH; } } BitmapMethods.drawIcon = drawIcon; function _drawIcon(img, icon, xy, color) { drawIcon(img, icon, XX(xy), YY(xy), color); } BitmapMethods._drawIcon = _drawIco