2.1.28, initiation update to PXT v5.28.24 (#54)
@@ -19,6 +19,7 @@ namespace pxsim {
|
||||
fileSystem: FileSystemState;
|
||||
|
||||
// visual
|
||||
viewHost: visuals.BoardHost;
|
||||
view: SVGElement;
|
||||
|
||||
constructor() {
|
||||
@@ -92,15 +93,15 @@ namespace pxsim {
|
||||
|
||||
switch (msg.type || "") {
|
||||
case "eventbus":
|
||||
let ev = <SimulatorEventBusMessage>msg;
|
||||
const ev = <SimulatorEventBusMessage>msg;
|
||||
this.bus.queue(ev.id, ev.eventid, ev.value);
|
||||
break;
|
||||
case "serial":
|
||||
let data = (<SimulatorSerialMessage>msg).data || "";
|
||||
const data = (<SimulatorSerialMessage>msg).data || "";
|
||||
this.serialState.receiveData(data);
|
||||
break;
|
||||
case "radiopacket":
|
||||
let packet = <SimulatorRadioPacketMessage>msg;
|
||||
const packet = <SimulatorRadioPacketMessage>msg;
|
||||
this.radioState.receivePacket(packet);
|
||||
break;
|
||||
}
|
||||
@@ -126,19 +127,20 @@ namespace pxsim {
|
||||
maxHeight: "100%",
|
||||
highContrast: msg.highContrast
|
||||
};
|
||||
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
|
||||
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
|
||||
visual: boardDef.visual,
|
||||
boardDef: boardDef,
|
||||
highContrast: msg.highContrast
|
||||
}), opts);
|
||||
|
||||
document.body.innerHTML = ""; // clear children
|
||||
document.body.appendChild(this.view = viewHost.getView());
|
||||
document.body.appendChild(this.view = this.viewHost.getView());
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
screenshot(): string {
|
||||
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
||||
screenshotAsync(width?: number): Promise<ImageData> {
|
||||
return this.viewHost.screenshotAsync(width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,4 +176,4 @@ namespace pxsim {
|
||||
export function board() {
|
||||
return runtime.board as DalBoard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/typings/globals/bluebird/index.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtrunner.d.ts"/>
|
||||
|
||||
//HACK: allows instructions.html to access pxtblocks without requiring simulator.html to import blocks as well
|
||||
if (!(<any>window).pxt) (<any>window).pxt = {};
|
||||
import pxtrunner = pxt.runner;
|
||||
import pxtdocs = pxt.docs;
|
||||
|
||||
namespace pxsim.instructions {
|
||||
export function drawInstructions() {
|
||||
pxsim.visuals.mkBoardView = (opts: pxsim.visuals.BoardViewOptions): pxsim.visuals.BoardView => {
|
||||
return new visuals.MicrobitBoardSvg({
|
||||
runtime: runtime,
|
||||
theme: visuals.randomTheme(),
|
||||
disableTilt: false,
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
}
|
||||
|
||||
let getQsVal = parseQueryString();
|
||||
|
||||
//project name
|
||||
let name = getQsVal("name") || "Untitled";
|
||||
|
||||
// board def
|
||||
const boardDef = JSON.parse(getQsVal("board")) as pxsim.BoardDefinition;
|
||||
|
||||
//parts list
|
||||
let parts = (getQsVal("parts") || "").split(" ");
|
||||
parts.sort();
|
||||
|
||||
// parts definitions
|
||||
let partDefinitions = JSON.parse(getQsVal("partdefs") || "{}") as pxsim.Map<PartDefinition>
|
||||
|
||||
//fn args
|
||||
let fnArgs = JSON.parse((getQsVal("fnArgs") || "{}"));
|
||||
|
||||
//project code
|
||||
let tsCode = getQsVal("code");
|
||||
let tsPackage = getQsVal("package") || "";
|
||||
let codeSpinnerDiv = document.getElementById("proj-code-spinner");
|
||||
let codeContainerDiv = document.getElementById("proj-code-container");
|
||||
if (tsCode) {
|
||||
//we use the docs renderer to decompile the code to blocks and render it
|
||||
//TODO: render the blocks code directly
|
||||
let md =
|
||||
`\`\`\`blocks
|
||||
${tsCode}
|
||||
\`\`\`
|
||||
\`\`\`package
|
||||
${tsPackage}
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
pxtdocs.requireMarked = function () { return (<any>window).marked; }
|
||||
pxtrunner.renderMarkdownAsync(codeContainerDiv, md)
|
||||
.done(function () {
|
||||
let codeSvg = $("#proj-code-container svg");
|
||||
if (codeSvg.length > 0) {
|
||||
//code rendered successfully as blocks
|
||||
codeSvg.css("width", "inherit");
|
||||
codeSvg.css("height", "inherit");
|
||||
//takes the svg out of the wrapper markdown
|
||||
codeContainerDiv.innerHTML = "";
|
||||
codeContainerDiv.appendChild(codeSvg[0]);
|
||||
} else {
|
||||
//code failed to convert to blocks, display as typescript instead
|
||||
codeContainerDiv.innerText = tsCode;
|
||||
}
|
||||
$(codeContainerDiv).show();
|
||||
$(codeSpinnerDiv).hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (name)
|
||||
$("#proj-title").text(name);
|
||||
|
||||
//init runtime
|
||||
if (!pxsim.initCurrentRuntime)
|
||||
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
|
||||
|
||||
renderParts({
|
||||
name,
|
||||
boardDef,
|
||||
parts,
|
||||
partDefinitions,
|
||||
fnArgs
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
sim/public/blockly/media/click.mp3
Normal file
BIN
sim/public/blockly/media/click.ogg
Normal file
BIN
sim/public/blockly/media/click.wav
Normal file
BIN
sim/public/blockly/media/delete.mp3
Normal file
BIN
sim/public/blockly/media/delete.ogg
Normal file
BIN
sim/public/blockly/media/delete.wav
Normal file
BIN
sim/public/blockly/media/disconnect.mp3
Normal file
BIN
sim/public/blockly/media/disconnect.ogg
Normal file
BIN
sim/public/blockly/media/disconnect.wav
Normal file
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,203 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-framework="typescript">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Assembly Instructions</title>
|
||||
<style>
|
||||
|
||||
svg {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.blocklyText, .ace_editor {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace !important;
|
||||
}
|
||||
|
||||
.blocklyText, .ace_editor {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
.blocklyTreeLabel {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
.blocklyCheckbox {
|
||||
fill: #ff3030 !important;
|
||||
text-shadow: 0px 0px 6px #f00;
|
||||
font-size: 17pt !important;
|
||||
}
|
||||
|
||||
.ui.card .blocklyPreview {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: calc(100% - 1em);
|
||||
max-height: calc(100% - 1em);
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
code.lang-config, code.lang-package { display:none; }
|
||||
|
||||
code.lang-blocks::before,
|
||||
code.lang-sig::before,
|
||||
code.lang-block::before,
|
||||
code.lang-shuffle::before,
|
||||
code.lang-sim::before,
|
||||
code.lang-cards::before,
|
||||
code.lang-namespaces::before,
|
||||
code.lang-codecard::before {
|
||||
content: "...";
|
||||
position: absolute;
|
||||
top: calc(50% - 0.5em);
|
||||
left: calc(50% - 5em);
|
||||
}
|
||||
|
||||
code.lang-blocks,
|
||||
code.lang-sig,
|
||||
code.lang-block,
|
||||
code.lang-shuffle,
|
||||
code.lang-sim,
|
||||
code.lang-cards,
|
||||
code.lang-namespaces,
|
||||
code.lang-codecard {
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@import "/cdn/semantic.css";
|
||||
@import "/cdn/icons.css";
|
||||
</style>
|
||||
<style>
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
}
|
||||
|
||||
div {
|
||||
/*undo semantic UI*/
|
||||
box-sizing: content-box;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
/*TODO: Share CSS with main webpage*/
|
||||
|
||||
.organization {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
font-family: 'Segoe UI', 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#front-panel .board-svg {
|
||||
position: absolute;
|
||||
left: 2rem;
|
||||
width: 300px;
|
||||
top: 16rem;
|
||||
}
|
||||
|
||||
#proj-title {
|
||||
width: 100%;
|
||||
font-size: 70px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#proj-code {
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
top: 16rem;
|
||||
}
|
||||
#proj-code-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 4px;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
}
|
||||
#proj-code-spinner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.back-panel svg {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
left: inherit;
|
||||
bottom: -7px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='loading' class="ui active inverted dimmer">
|
||||
<div class="ui large loader"></div>
|
||||
</div>
|
||||
<!-- start Mixpanel --><script type="text/javascript">(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config reset people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
||||
mixpanel.init("762fef19c053a0ea4cec43d2fecae76e");</script><!-- end Mixpanel -->
|
||||
<script>
|
||||
// This line gets patched up by the cloud
|
||||
var pxtConfig = null;
|
||||
</script>
|
||||
<script type="text/javascript" src="/cdn/lzma/lzma_worker-min.js"></script>
|
||||
<script type="text/javascript" src="/cdn/marked/marked.min.js"></script>
|
||||
<script type="text/javascript" src="/cdn/jquery.js"></script>
|
||||
<script type="text/javascript" src="/cdn/typescript.js"></script>
|
||||
<script type="text/javascript" src="/cdn/blockly/blockly_compressed.js"></script>
|
||||
<script type="text/javascript" src="/cdn/blockly/blocks_compressed.js"></script>
|
||||
<script type="text/javascript" src="/cdn/blockly/msg/js/en.js"></script>
|
||||
<script type="text/javascript" src="/cdn/pxtlib.js"></script>
|
||||
<script type="text/javascript" src="/cdn/pxtblocks.js"></script>
|
||||
<script type="text/javascript" src="/cdn/pxtsim.js"></script>
|
||||
<script type="text/javascript" src="/cdn/pxtrunner.js"></script>
|
||||
<script type="text/javascript" src="/cdn/semantic.js"></script>
|
||||
<script type="text/javascript" src="/embed.js"></script>
|
||||
<script type="text/javascript" src="/sim/sim.js"></script>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
ksRunnerReady(function() {
|
||||
var orgLogo = pxt.appTarget.appTheme.organizationLogo;
|
||||
if (orgLogo)
|
||||
$('#front-panel').append(
|
||||
$('<img/>').attr('class', 'organization').attr('src', orgLogo)
|
||||
);
|
||||
var loading = document.getElementById('loading');
|
||||
pxsim.instructions.drawInstructions();
|
||||
$(loading).hide();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<div id="front-panel" class="instr-panel">
|
||||
<h1 id="proj-title"></h1>
|
||||
|
||||
<!--TODO: extract real code snapshot from PXT -->
|
||||
<div id="proj-code">
|
||||
<i id="proj-code-spinner" class="spinner loading icon"></i>
|
||||
<div id="proj-code-container">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,37 +1,53 @@
|
||||
namespace pxsim.input {
|
||||
export function onGesture(gesture: number, handler: RefAction) {
|
||||
function accForGesture(gesture: number) {
|
||||
let b = board().accelerometerState;
|
||||
b.accelerometer.activate();
|
||||
|
||||
if (gesture == 11 && !b.useShake) { // SAKE
|
||||
if (gesture == 11 && !b.useShake) { // SHAKE
|
||||
b.useShake = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
export function onGesture(gesture: number, handler: RefAction) {
|
||||
const b = accForGesture(gesture);
|
||||
pxtcore.registerWithDal(DAL.MICROBIT_ID_GESTURE, gesture, handler);
|
||||
}
|
||||
|
||||
export function isGesture(gesture: number): boolean {
|
||||
const b = accForGesture(gesture);
|
||||
return b.accelerometer.getGesture() == gesture;
|
||||
}
|
||||
|
||||
export function acceleration(dimension: number): number {
|
||||
let b = board().accelerometerState;
|
||||
let acc = b.accelerometer;
|
||||
acc.activate();
|
||||
switch (dimension) {
|
||||
case 0: return acc.getX();
|
||||
case 1: return acc.getY();
|
||||
case 2: return acc.getZ();
|
||||
default: return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
|
||||
case 0:
|
||||
acc.activate(AccelerometerFlag.X);
|
||||
return acc.getX();
|
||||
case 1:
|
||||
acc.activate(AccelerometerFlag.Y);
|
||||
return acc.getY();
|
||||
case 2:
|
||||
acc.activate(AccelerometerFlag.Z);
|
||||
return acc.getZ();
|
||||
default:
|
||||
acc.activate();
|
||||
return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared()));
|
||||
}
|
||||
}
|
||||
|
||||
export function rotation(kind: number): number {
|
||||
let b = board().accelerometerState;
|
||||
let acc = b.accelerometer;
|
||||
const b = board().accelerometerState;
|
||||
const acc = b.accelerometer;
|
||||
acc.activate();
|
||||
let x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
let y = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
let z = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
const x = acc.getX(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
const y = acc.getY(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
const z = acc.getZ(MicroBitCoordinateSystem.NORTH_EAST_DOWN);
|
||||
|
||||
let roll = Math.atan2(y, z);
|
||||
let pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
|
||||
const roll = Math.atan2(y, z);
|
||||
const pitch = Math.atan(-x / (y * Math.sin(roll) + z * Math.cos(roll)));
|
||||
|
||||
let r = 0;
|
||||
switch (kind) {
|
||||
@@ -99,6 +115,12 @@ namespace pxsim {
|
||||
NORTH_EAST_DOWN
|
||||
}
|
||||
|
||||
export enum AccelerometerFlag {
|
||||
X = 1,
|
||||
Y = 2,
|
||||
Z = 4
|
||||
}
|
||||
|
||||
export class Accelerometer {
|
||||
private sigma: number = 0; // the number of ticks that the instantaneous gesture has been stable.
|
||||
private lastGesture: number = 0; // the last, stable gesture recorded.
|
||||
@@ -110,6 +132,7 @@ namespace pxsim {
|
||||
private id: number;
|
||||
public isActive = false;
|
||||
public sampleRange = 2;
|
||||
public flags: AccelerometerFlag = 0;
|
||||
|
||||
constructor(public runtime: Runtime) {
|
||||
this.id = DAL.MICROBIT_ID_ACCELEROMETER;
|
||||
@@ -120,11 +143,13 @@ namespace pxsim {
|
||||
this.sampleRange = Math.max(1, Math.min(8, range));
|
||||
}
|
||||
|
||||
public activate() {
|
||||
public activate(flags?: AccelerometerFlag) {
|
||||
if (!this.isActive) {
|
||||
this.isActive = true;
|
||||
this.runtime.queueDisplayUpdate();
|
||||
}
|
||||
if (flags)
|
||||
this.flags |= flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,9 +224,8 @@ namespace pxsim {
|
||||
if (force < sq(DAL.MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE))
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_FREEFALL;
|
||||
|
||||
// TODO: fix this
|
||||
//if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
|
||||
// return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
|
||||
if (force > sq(DAL.MICROBIT_ACCELEROMETER_3G_TOLERANCE))
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_3G;
|
||||
|
||||
if (force > sq(DAL.MICROBIT_ACCELEROMETER_6G_TOLERANCE))
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_6G;
|
||||
@@ -223,10 +247,10 @@ namespace pxsim {
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_TILT_UP;
|
||||
|
||||
if (this.getZ() < (-1000 + DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
|
||||
|
||||
if (this.getZ() > (1000 - DAL.MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_UP;
|
||||
return DAL.MICROBIT_ACCELEROMETER_EVT_FACE_DOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -236,22 +260,28 @@ namespace pxsim {
|
||||
let g = this.instantaneousPosture();
|
||||
|
||||
// Perform some low pass filtering to reduce jitter from any detected effects
|
||||
if (g == this.currentGesture) {
|
||||
if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
|
||||
this.sigma++;
|
||||
}
|
||||
else {
|
||||
if (g != this.currentGesture) {
|
||||
this.currentGesture = g;
|
||||
this.sigma = 0;
|
||||
} else if (this.sigma < DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
|
||||
++this.sigma;
|
||||
}
|
||||
|
||||
// If we've reached threshold, update our record and raise the relevant event...
|
||||
if (this.currentGesture != this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
|
||||
this.lastGesture = this.currentGesture;
|
||||
board().bus.queue(DAL.MICROBIT_ID_GESTURE, this.lastGesture);
|
||||
if (this.currentGesture !== this.lastGesture && this.sigma >= DAL.MICROBIT_ACCELEROMETER_GESTURE_DAMPING) {
|
||||
this.enqueueCurrentGesture();
|
||||
}
|
||||
}
|
||||
|
||||
forceGesture(gesture: number) {
|
||||
this.currentGesture = gesture;
|
||||
this.enqueueCurrentGesture();
|
||||
}
|
||||
|
||||
private enqueueCurrentGesture() {
|
||||
this.lastGesture = this.currentGesture;
|
||||
board().bus.queue(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.
|
||||
@@ -363,6 +393,10 @@ namespace pxsim {
|
||||
return this.roll;
|
||||
}
|
||||
|
||||
getGesture(): number {
|
||||
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
|
||||
@@ -384,7 +418,11 @@ namespace pxsim {
|
||||
useShake = false;
|
||||
|
||||
constructor(runtime: Runtime) {
|
||||
this.accelerometer = new Accelerometer(runtime);
|
||||
this.accelerometer = new Accelerometer(runtime);
|
||||
}
|
||||
|
||||
shake() {
|
||||
this.accelerometer.forceGesture(DAL.MICROBIT_ACCELEROMETER_EVT_SHAKE); // SHAKE == 11
|
||||
}
|
||||
}
|
||||
}
|
||||
201
sim/state/buttonpairsim.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
namespace pxsim.visuals {
|
||||
export function mkBtnSvg(xy: Coord): SVGAndSize<SVGGElement> {
|
||||
let [innerCls, outerCls] = ["sim-button", "sim-button-outer"];
|
||||
const tabSize = PIN_DIST / 2.5;
|
||||
const pegR = PIN_DIST / 5;
|
||||
const btnR = PIN_DIST * .8;
|
||||
const pegMargin = PIN_DIST / 8;
|
||||
const plateR = PIN_DIST / 12;
|
||||
|
||||
const pegOffset = pegMargin + pegR;
|
||||
let [x, y] = xy;
|
||||
const left = x - tabSize / 2;
|
||||
const top = y - tabSize / 2;
|
||||
const plateH = 3 * PIN_DIST - tabSize;
|
||||
const plateW = 2 * PIN_DIST + tabSize;
|
||||
const plateL = left;
|
||||
const plateT = top + tabSize;
|
||||
const btnCX = plateL + plateW / 2;
|
||||
const btnCY = plateT + plateH / 2;
|
||||
|
||||
let btng = <SVGGElement>svg.elt("g");
|
||||
//tabs
|
||||
const mkTab = (x: number, y: number) => {
|
||||
svg.child(btng, "rect", { class: "sim-button-tab", x: x, y: y, width: tabSize, height: tabSize})
|
||||
}
|
||||
mkTab(left, top);
|
||||
mkTab(left + 2 * PIN_DIST, top);
|
||||
mkTab(left, top + 3 * PIN_DIST);
|
||||
mkTab(left + 2 * PIN_DIST, top + 3 * PIN_DIST);
|
||||
|
||||
//plate
|
||||
svg.child(btng, "rect", { class: outerCls, x: plateL, y: plateT, rx: plateR, ry: plateR, width: plateW, height: plateH });
|
||||
|
||||
//pegs
|
||||
const mkPeg = (x: number, y: number) => {
|
||||
svg.child(btng, "circle", { class: "sim-button-nut", cx: x, cy: y, r: pegR });
|
||||
}
|
||||
mkPeg(plateL + pegOffset, plateT + pegOffset)
|
||||
mkPeg(plateL + plateW - pegOffset, plateT + pegOffset)
|
||||
mkPeg(plateL + pegOffset, plateT + plateH - pegOffset)
|
||||
mkPeg(plateL + plateW - pegOffset, plateT + plateH - pegOffset)
|
||||
|
||||
//inner btn
|
||||
let innerBtn = svg.child(btng, "circle", { class: innerCls, cx: btnCX, cy: btnCY, r: btnR });
|
||||
|
||||
//return
|
||||
return { el: btng, y: top, x: left, w: plateW, h: plateH + 2 * tabSize };
|
||||
}
|
||||
export const BUTTON_PAIR_STYLE = `
|
||||
.sim-button {
|
||||
pointer-events: none;
|
||||
fill: #000;
|
||||
}
|
||||
.sim-button-outer:active ~ .sim-button,
|
||||
.sim-button-virtual:active {
|
||||
fill: #FFA500;
|
||||
}
|
||||
.sim-button-outer {
|
||||
cursor: pointer;
|
||||
fill: #979797;
|
||||
}
|
||||
.sim-button-outer:hover {
|
||||
stroke:gray;
|
||||
stroke-width: ${PIN_DIST / 5}px;
|
||||
}
|
||||
.sim-button-nut {
|
||||
fill:#000;
|
||||
pointer-events:none;
|
||||
}
|
||||
.sim-button-nut:hover {
|
||||
stroke:${PIN_DIST / 15}px solid #704A4A;
|
||||
}
|
||||
.sim-button-tab {
|
||||
fill:#FFF;
|
||||
pointer-events:none;
|
||||
}
|
||||
.sim-button-virtual {
|
||||
cursor: pointer;
|
||||
fill: rgba(255, 255, 255, 0.6);
|
||||
stroke: rgba(255, 255, 255, 1);
|
||||
stroke-width: ${PIN_DIST / 5}px;
|
||||
}
|
||||
.sim-button-virtual:hover {
|
||||
stroke: rgba(128, 128, 128, 1);
|
||||
}
|
||||
.sim-text-virtual {
|
||||
fill: #000;
|
||||
pointer-events:none;
|
||||
}
|
||||
`;
|
||||
export class ButtonPairView implements IBoardPart<ButtonPairState> {
|
||||
public element: SVGElement;
|
||||
public defs: SVGElement[];
|
||||
public style = BUTTON_PAIR_STYLE;
|
||||
private state: ButtonPairState;
|
||||
private bus: EventBus;
|
||||
private aBtn: SVGGElement;
|
||||
private bBtn: SVGGElement;
|
||||
private abBtn: SVGGElement;
|
||||
|
||||
public init(bus: EventBus, state: ButtonPairState) {
|
||||
this.state = state;
|
||||
this.bus = bus;
|
||||
this.defs = [];
|
||||
this.element = this.mkBtns();
|
||||
this.updateState();
|
||||
this.attachEvents();
|
||||
}
|
||||
|
||||
public moveToCoord(xy: Coord) {
|
||||
let btnWidth = PIN_DIST * 3;
|
||||
let [x, y] = xy;
|
||||
translateEl(this.aBtn, [x, y])
|
||||
translateEl(this.bBtn, [x + btnWidth, y])
|
||||
translateEl(this.abBtn, [x + PIN_DIST * 1.5, y + PIN_DIST * 4])
|
||||
}
|
||||
|
||||
public updateState() {
|
||||
let stateBtns = [this.state.aBtn, this.state.bBtn, this.state.abBtn];
|
||||
let svgBtns = [this.aBtn, this.bBtn, this.abBtn];
|
||||
|
||||
if (this.state.usesButtonAB && this.abBtn.style.visibility != "visible") {
|
||||
this.abBtn.style.visibility = "visible";
|
||||
}
|
||||
}
|
||||
|
||||
public updateTheme() {}
|
||||
|
||||
private mkBtns() {
|
||||
this.aBtn = mkBtnSvg([0, 0]).el;
|
||||
this.bBtn = mkBtnSvg([0, 0]).el;
|
||||
|
||||
const mkVirtualBtn = () => {
|
||||
const numPins = 2;
|
||||
const w = PIN_DIST * 2.8;
|
||||
const offset = (w - (numPins * PIN_DIST)) / 2;
|
||||
const corner = PIN_DIST / 2;
|
||||
const cx = 0 - offset + w / 2;
|
||||
const cy = cx;
|
||||
const txtSize = PIN_DIST * 1.3;
|
||||
const x = -offset;
|
||||
const y = -offset;
|
||||
const txtXOff = PIN_DIST / 7;
|
||||
const txtYOff = PIN_DIST / 10;
|
||||
|
||||
let btng = <SVGGElement>svg.elt("g");
|
||||
let btn = svg.child(btng, "rect", { class: "sim-button-virtual", x: x, y: y, rx: corner, ry: corner, width: w, height: w});
|
||||
let btnTxt = mkTxt(cx + txtXOff, cy + txtYOff, txtSize, 0, "A+B");
|
||||
|
||||
U.addClass(btnTxt, "sim-text")
|
||||
U.addClass(btnTxt, "sim-text-virtual");
|
||||
btng.appendChild(btnTxt);
|
||||
|
||||
return btng;
|
||||
}
|
||||
|
||||
this.abBtn = mkVirtualBtn();
|
||||
this.abBtn.style.visibility = "hidden";
|
||||
|
||||
let el = svg.elt("g");
|
||||
U.addClass(el, "sim-buttonpair")
|
||||
el.appendChild(this.aBtn);
|
||||
el.appendChild(this.bBtn);
|
||||
el.appendChild(this.abBtn);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
private attachEvents() {
|
||||
let btnStates = [this.state.aBtn, this.state.bBtn];
|
||||
let btnSvgs = [this.aBtn, this.bBtn];
|
||||
btnSvgs.forEach((btn, index) => {
|
||||
pxsim.pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
btnStates[index].pressed = true;
|
||||
}))
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
btnStates[index].pressed = false;
|
||||
})
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
btnStates[index].pressed = false;
|
||||
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_UP);
|
||||
this.bus.queue(btnStates[index].id, this.state.props.BUTTON_EVT_CLICK);
|
||||
})
|
||||
})
|
||||
let updateBtns = (s: boolean) => {
|
||||
btnStates.forEach(b => b.pressed = s)
|
||||
};
|
||||
pxsim.pointerEvents.down.forEach(evid => this.abBtn.addEventListener(evid, ev => {
|
||||
updateBtns(true);
|
||||
}));
|
||||
this.abBtn.addEventListener(pointerEvents.leave, ev => {
|
||||
updateBtns(false);
|
||||
})
|
||||
this.abBtn.addEventListener(pointerEvents.up, ev => {
|
||||
updateBtns(false);
|
||||
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_UP);
|
||||
this.bus.queue(this.state.abBtn.id, this.state.props.BUTTON_EVT_CLICK);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ namespace pxsim.input {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_CLICK, handler);
|
||||
}
|
||||
|
||||
@@ -10,6 +11,7 @@ namespace pxsim.input {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_UP, handler);
|
||||
}
|
||||
|
||||
@@ -29,7 +31,7 @@ namespace pxsim {
|
||||
namespace pxsim.pins {
|
||||
export function digitalReadPin(pinId: number): number {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
if (!pin) return -1;
|
||||
pin.mode = PinFlags.Digital | PinFlags.Input;
|
||||
return pin.value > 100 ? 1 : 0;
|
||||
}
|
||||
@@ -50,7 +52,7 @@ namespace pxsim.pins {
|
||||
|
||||
export function analogReadPin(pinId: number): number {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
if (!pin) return -1;
|
||||
pin.mode = PinFlags.Analog | PinFlags.Input;
|
||||
return pin.value || 0;
|
||||
}
|
||||
@@ -76,7 +78,7 @@ namespace pxsim.pins {
|
||||
if (!pin) return;
|
||||
|
||||
analogSetPeriod(pinId, 20000);
|
||||
pin.servoAngle = Math.max(0, Math.min(180, value));
|
||||
pin.servoAngle = value;
|
||||
}
|
||||
|
||||
export function servoSetPulse(pinId: number, micros: number) {
|
||||
|
||||
89
sim/state/edgeconnectorsim.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
namespace pxsim {
|
||||
export enum PinFlags {
|
||||
Unused = 0,
|
||||
Digital = 0x0001,
|
||||
Analog = 0x0002,
|
||||
Input = 0x0004,
|
||||
Output = 0x0008,
|
||||
Touch = 0x0010
|
||||
}
|
||||
|
||||
export class Pin {
|
||||
constructor(public id: number) { }
|
||||
touched = false;
|
||||
value = 0;
|
||||
period = 0;
|
||||
servoAngle = 0;
|
||||
mode = PinFlags.Unused;
|
||||
pitch = false;
|
||||
pull = 0; // PullDown
|
||||
|
||||
digitalReadPin(): number {
|
||||
this.mode = PinFlags.Digital | PinFlags.Input;
|
||||
return this.value > 100 ? 1 : 0;
|
||||
}
|
||||
|
||||
digitalWritePin(value: number) {
|
||||
this.mode = PinFlags.Digital | PinFlags.Output;
|
||||
this.value = value > 0 ? 200 : 0;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
|
||||
setPull(pull: number) {
|
||||
this.pull = pull;
|
||||
}
|
||||
|
||||
analogReadPin(): number {
|
||||
this.mode = PinFlags.Analog | PinFlags.Input;
|
||||
return this.value || 0;
|
||||
}
|
||||
|
||||
analogWritePin(value: number) {
|
||||
value = value >> 0;
|
||||
this.mode = PinFlags.Analog | PinFlags.Output;
|
||||
this.value = Math.max(0, Math.min(1023, value));
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
|
||||
analogSetPeriod(micros: number) {
|
||||
micros = micros >> 0;
|
||||
this.mode = PinFlags.Analog | PinFlags.Output;
|
||||
this.period = micros;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
|
||||
servoWritePin(value: number) {
|
||||
value = value >> 0;
|
||||
this.analogSetPeriod(20000);
|
||||
this.servoAngle = Math.max(0, Math.min(180, value));
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
|
||||
servoSetPulse(pinId: number, micros: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
isTouched(): boolean {
|
||||
this.mode = PinFlags.Touch | PinFlags.Analog | PinFlags.Input;
|
||||
return this.touched;
|
||||
}
|
||||
}
|
||||
|
||||
export interface EdgeConnectorProps {
|
||||
pins: number[];
|
||||
servos?: { [name: string]: number; }
|
||||
}
|
||||
|
||||
export class EdgeConnectorState {
|
||||
pins: Pin[];
|
||||
|
||||
constructor(public props: EdgeConnectorProps) {
|
||||
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
|
||||
}
|
||||
|
||||
public getPin(id: number) {
|
||||
return this.pins.filter(p => p && p.id == id)[0] || null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,4 +15,10 @@ namespace pxsim.files {
|
||||
const b = board();
|
||||
b.fileSystem.remove(filename);
|
||||
}
|
||||
export function readToSerial(filename: string) {
|
||||
const b = board();
|
||||
let f = b.fileSystem.files[filename];
|
||||
if (f)
|
||||
b.serialState.writeSerial(f);
|
||||
}
|
||||
}
|
||||
@@ -28,17 +28,24 @@ namespace pxsim {
|
||||
this.data = data;
|
||||
}
|
||||
public print() {
|
||||
// console.debug(`Image id:${this.id} refs:${this.refcnt} size:${this.width}x${Image.height}`)
|
||||
console.debug(`Image id:${this.id} size:${this.width}x${Image.height}`)
|
||||
}
|
||||
public get(x: number, y: number): number {
|
||||
x = x >> 0;
|
||||
y = y >> 0;
|
||||
if (x < 0 || x >= this.width || y < 0 || y >= 5) return 0;
|
||||
return this.data[y * this.width + x];
|
||||
}
|
||||
public set(x: number, y: number, v: number) {
|
||||
x = x >> 0;
|
||||
y = y >> 0;
|
||||
if (x < 0 || x >= this.width || y < 0 || y >= 5) return;
|
||||
this.data[y * this.width + x] = Math.max(0, Math.min(255, v));
|
||||
}
|
||||
public copyTo(xSrcIndex: number, length: number, target: Image, xTargetIndex: number): void {
|
||||
xSrcIndex = xSrcIndex >> 0;
|
||||
length = length >> 0;
|
||||
xTargetIndex = xTargetIndex >> 0;
|
||||
for (let x = 0; x < length; x++) {
|
||||
for (let y = 0; y < 5; y++) {
|
||||
let value = this.get(xSrcIndex + x, y);
|
||||
@@ -47,12 +54,14 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
public shiftLeft(cols: number) {
|
||||
cols = cols >> 0;
|
||||
for (let x = 0; x < this.width; ++x)
|
||||
for (let y = 0; y < 5; ++y)
|
||||
this.set(x, y, x < this.width - cols ? this.get(x + cols, y) : 0);
|
||||
}
|
||||
|
||||
public shiftRight(cols: number) {
|
||||
cols = cols >> 0;
|
||||
for (let x = this.width - 1; x >= 0; --x)
|
||||
for (let y = 0; y < 5; ++y)
|
||||
this.set(x, y, x >= cols ? this.get(x - cols, y) : 0);
|
||||
@@ -65,12 +74,13 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
export function createInternalImage(width: number): Image {
|
||||
width = width >> 0;
|
||||
let img = createImage(width)
|
||||
pxsim.noLeakTracking(img)
|
||||
return img
|
||||
}
|
||||
|
||||
export function createImage(width: number): Image {
|
||||
width = width >> 0;
|
||||
return new Image(width, new Array(width * 5));
|
||||
}
|
||||
|
||||
@@ -131,9 +141,12 @@ namespace pxsim.images {
|
||||
namespace pxsim.ImageMethods {
|
||||
export function showImage(leds: Image, offset: number, interval: number) {
|
||||
pxtrt.nullCheck(leds)
|
||||
offset = offset >> 0;
|
||||
interval = interval >> 0;
|
||||
let cb = getResume();
|
||||
let first = true;
|
||||
|
||||
leds = clampPixelBrightness(leds);
|
||||
board().ledMatrixState.animationQ.enqueue({
|
||||
interval,
|
||||
frame: () => {
|
||||
@@ -150,7 +163,9 @@ namespace pxsim.ImageMethods {
|
||||
|
||||
export function plotImage(leds: Image, offset: number): void {
|
||||
pxtrt.nullCheck(leds)
|
||||
offset = offset >> 0;
|
||||
|
||||
leds = clampPixelBrightness(leds);
|
||||
board().ledMatrixState.animationQ.enqueue({
|
||||
interval: 0,
|
||||
frame: () => {
|
||||
@@ -205,12 +220,15 @@ namespace pxsim.ImageMethods {
|
||||
|
||||
export function scrollImage(leds: Image, stride: number, interval: number): void {
|
||||
pxtrt.nullCheck(leds)
|
||||
stride = stride >> 0;
|
||||
interval = interval >> 0;
|
||||
if (stride == 0) stride = 1;
|
||||
|
||||
let cb = getResume();
|
||||
let off = stride > 0 ? 0 : leds.width - 1;
|
||||
let display = board().ledMatrixState.image;
|
||||
|
||||
leds = clampPixelBrightness(leds);
|
||||
board().ledMatrixState.animationQ.enqueue({
|
||||
interval: interval,
|
||||
frame: () => {
|
||||
@@ -230,10 +248,27 @@ namespace pxsim.ImageMethods {
|
||||
whenDone: cb
|
||||
})
|
||||
}
|
||||
|
||||
function clampPixelBrightness(img: Image): Image {
|
||||
let res = img;
|
||||
if (led.displayMode() === DisplayMode.greyscale && led.brightness() < 0xff) {
|
||||
res = new Image(img.width, img.data);
|
||||
const b = led.brightness();
|
||||
for (let x = 0; x < res.width; ++x) {
|
||||
for (let y = 0; y < 5; ++y) {
|
||||
if (pixelBrightness(res, x, y) > b) {
|
||||
setPixelBrightness(res, x, y, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.basic {
|
||||
export function showNumber(x: number, interval: number) {
|
||||
interval = interval >> 0;
|
||||
if (interval <= 0)
|
||||
interval = 1;
|
||||
let leds = createImageFromString(x.toString());
|
||||
@@ -242,19 +277,21 @@ namespace pxsim.basic {
|
||||
}
|
||||
|
||||
export function showString(s: string, interval: number) {
|
||||
interval = interval >> 0;
|
||||
if (interval <= 0)
|
||||
interval = 1;
|
||||
if (s.length == 0) {
|
||||
clearScreen();
|
||||
pause(interval * 5);
|
||||
} else if (s.length > 1) {
|
||||
ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
|
||||
} else {
|
||||
if (s.length == 1) showLeds(createImageFromString(s), 0);
|
||||
else ImageMethods.scrollImage(createImageFromString(s + " "), 1, interval);
|
||||
showLeds(createImageFromString(s), interval * 5);
|
||||
}
|
||||
}
|
||||
|
||||
export function showLeds(leds: Image, delay: number): void {
|
||||
showAnimation(leds, delay);
|
||||
export function showLeds(leds: Image, interval: number): void {
|
||||
showAnimation(leds, interval);
|
||||
}
|
||||
|
||||
export function clearScreen() {
|
||||
@@ -279,9 +316,10 @@ namespace pxsim.led {
|
||||
|
||||
export function plotBrightness(x: number, y: number, brightness: number) {
|
||||
const state = board().ledMatrixState;
|
||||
brightness = Math.max(0, Math.min(0xff, brightness));
|
||||
brightness = brightness >> 0;
|
||||
brightness = Math.max(0, Math.min(led.brightness(), brightness));
|
||||
if (brightness != 0 && brightness != 0xff && state.displayMode != DisplayMode.greyscale)
|
||||
state.displayMode = DisplayMode.greyscale;
|
||||
state.displayMode = DisplayMode.greyscale;
|
||||
state.image.set(x, y, brightness);
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
@@ -300,6 +338,7 @@ namespace pxsim.led {
|
||||
}
|
||||
|
||||
export function setBrightness(value: number): void {
|
||||
value = value >> 0;
|
||||
board().ledMatrixState.brigthness = Math.max(0, Math.min(255, value));
|
||||
runtime.queueDisplayUpdate()
|
||||
}
|
||||
|
||||
83
sim/state/microservo.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
namespace pxsim.visuals {
|
||||
function createMicroServoElement() {
|
||||
return svg.parseString(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg2" width="112.188" height="299.674">
|
||||
<g id="layer1" stroke-linecap="round" stroke-linejoin="round" transform="scale(0.8)">
|
||||
<path id="path8212" fill="#0061ff" stroke-width="6.6" d="M.378 44.61v255.064h112.188V44.61H.378z"/>
|
||||
<path id="crankbase" fill="#00f" stroke-width="6.6" d="M56.57 88.047C25.328 88.047 0 113.373 0 144.615c.02 22.352 11.807 42.596 32.238 51.66.03 3.318.095 5.24.088 7.938 0 13.947 11.307 25.254 25.254 25.254 13.947 0 25.254-11.307 25.254-25.254-.006-2.986-.415-5.442-.32-8.746 19.487-9.45 30.606-29.195 30.625-50.852 0-31.24-25.33-56.568-56.57-56.568z"/>
|
||||
<path id="lowertip" fill="#00a2ff" stroke-width="2" d="M.476 260.78v38.894h53.82v-10.486a6.82 6.566 0 0 1-4.545-6.182 6.82 6.566 0 0 1 6.82-6.566 6.82 6.566 0 0 1 6.82 6.566 6.82 6.566 0 0 1-4.545 6.182v10.486h53.82V260.78H.475z"/>
|
||||
<path id="uppertip" fill="#00a2ff" stroke-width="2" d="M112.566 83.503V44.61h-53.82v10.487a6.82 6.566 0 0 1 4.544 6.18 6.82 6.566 0 0 1-6.818 6.568 6.82 6.566 0 0 1-6.82-6.567 6.82 6.566 0 0 1 4.546-6.18V44.61H.378v38.893h112.188z"/>
|
||||
<path id="VCC" fill="red" stroke-width="2" d="M53.72 21.93h5.504v22.627H53.72z"/>
|
||||
<path id="LOGIC" fill="#fc0" stroke-width="2" d="M47.3 21.93h5.503v22.627H47.3z"/>
|
||||
<path id="GND" fill="#a02c2c" stroke-width="2" d="M60.14 21.93h5.505v22.627H60.14z"/>
|
||||
<path id="connector" fill="#111" stroke-width="2" d="M45.064 0a1.488 1.488 0 0 0-1.488 1.488v24.5a1.488 1.488 0 0 0 1.488 1.487h22.71a1.488 1.488 0 0 0 1.49-1.488v-24.5A1.488 1.488 0 0 0 67.774 0h-22.71z"/>
|
||||
<g id="crank" transform="translate(0 -752.688)">
|
||||
<path id="arm" fill="#ececec" stroke="#000" stroke-width="1.372" d="M47.767 880.88c-4.447 1.162-8.412 8.278-8.412 18.492s3.77 18.312 8.412 18.494c8.024.314 78.496 5.06 78.51-16.952.012-22.013-74.377-21.117-78.51-20.035z"/>
|
||||
<circle id="path8216" cx="56.661" cy="899.475" r="8.972" fill="gray" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
`).firstElementChild as SVGGElement;
|
||||
}
|
||||
|
||||
export function mkMicroServoPart(xy: Coord = [0, 0]): SVGElAndSize {
|
||||
return { el: createMicroServoElement(), x: xy[0], y: xy[1], w: 112.188, h: 299.674 };
|
||||
}
|
||||
|
||||
export class MicroServoView implements IBoardPart<EdgeConnectorState> {
|
||||
public style: string = "";
|
||||
public overElement: SVGElement = undefined;
|
||||
public element: SVGElement;
|
||||
public defs: SVGElement[] = [];
|
||||
public state: EdgeConnectorState;
|
||||
public bus: EventBus;
|
||||
private currentAngle = 0;
|
||||
private targetAngle = 0;
|
||||
private lastAngleTime = 0;
|
||||
private pin: number;
|
||||
|
||||
private crankEl: SVGGElement;
|
||||
private crankTransform: string;
|
||||
|
||||
public init(bus: EventBus, state: EdgeConnectorState, svgEl: SVGSVGElement, otherParams: Map<string>) {
|
||||
this.state = state;
|
||||
this.pin = this.state.props.servos[
|
||||
pxsim.readPin(otherParams["name"] || otherParams["pin"])
|
||||
];
|
||||
this.bus = bus;
|
||||
this.defs = [];
|
||||
this.initDom();
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
initDom() {
|
||||
this.element = createMicroServoElement();
|
||||
this.crankEl = this.element.querySelector("#crank") as SVGGElement;
|
||||
this.crankTransform = this.crankEl.getAttribute("transform");
|
||||
}
|
||||
|
||||
moveToCoord(xy: visuals.Coord): void {
|
||||
let [x, y] = xy;
|
||||
translateEl(this.element, [x, y])
|
||||
}
|
||||
updateState(): void {
|
||||
this.targetAngle = 180.0 - this.state.getPin(this.pin).servoAngle;
|
||||
if (this.targetAngle != this.currentAngle) {
|
||||
const now = U.now();
|
||||
const cx = 56.661;
|
||||
const cy = 899.475;
|
||||
const speed = 300; // 0.1s/60 degree
|
||||
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
|
||||
const delta = this.targetAngle - this.currentAngle;
|
||||
this.currentAngle += Math.min(Math.abs(delta), speed * dt) * (delta > 0 ? 1 : -1);
|
||||
this.crankEl.setAttribute("transform", this.crankTransform
|
||||
+ ` rotate(${this.currentAngle}, ${cx}, ${cy})`)
|
||||
this.lastAngleTime = now;
|
||||
setTimeout(() => runtime.updateDisplay(), 20);
|
||||
}
|
||||
}
|
||||
updateTheme(): void {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
6
sim/state/midi.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace pxsim.control {
|
||||
export function __midiSend(data: RefBuffer) {
|
||||
const b = board();
|
||||
pxsim.AudioContextManager.sendMidiMessage(data);
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,13 @@ namespace pxsim.basic {
|
||||
namespace pxsim.control {
|
||||
export var inBackground = thread.runInBackground;
|
||||
|
||||
export function createBuffer(sz: number) {
|
||||
return pxsim.BufferMethods.createBuffer(sz)
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
U.userError("reset not implemented in simulator yet")
|
||||
const cb = getResume();
|
||||
pxsim.runtime.restart();
|
||||
}
|
||||
|
||||
export function waitMicros(micros: number) {
|
||||
@@ -58,6 +63,13 @@ namespace pxsim.control {
|
||||
}
|
||||
|
||||
export function onEvent(id: number, evid: number, handler: RefAction) {
|
||||
if (id == DAL.MICROBIT_ID_BUTTON_AB) {
|
||||
const b = board().buttonPairState;
|
||||
if (!b.usesButtonAB) {
|
||||
b.usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
pxtcore.registerWithDal(id, evid, handler)
|
||||
}
|
||||
|
||||
@@ -65,6 +77,14 @@ namespace pxsim.control {
|
||||
// TODO mode?
|
||||
board().bus.queue(id, evid)
|
||||
}
|
||||
|
||||
export function eventTimestamp() {
|
||||
return board().bus.getLastEventTime()
|
||||
}
|
||||
|
||||
export function eventValue() {
|
||||
return board().bus.getLastEventValue()
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pxtcore {
|
||||
@@ -78,7 +98,12 @@ namespace pxsim.input {
|
||||
return runtime.runningTime();
|
||||
}
|
||||
|
||||
export function calibrate() {
|
||||
export function runningTimeMicros(): number {
|
||||
return runtime.runningTimeUs();
|
||||
}
|
||||
|
||||
export function calibrateCompass() {
|
||||
// device calibrates...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +131,18 @@ namespace pxsim.pins {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function spiFrequency(f: number): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function spiFormat(bits: number, mode: number): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function spiPins(mosi: number, miso: number, sck: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function i2cReadBuffer(address: number, size: number, repeat?: boolean): RefBuffer {
|
||||
// fake reading zeros
|
||||
return createBuffer(size)
|
||||
@@ -137,7 +174,7 @@ namespace pxsim.devices {
|
||||
export function onSignalStrengthChanged(action: number) {
|
||||
// TODO
|
||||
}
|
||||
export function signalStrength() : number {
|
||||
export function signalStrength(): number {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@@ -168,25 +205,34 @@ namespace pxsim.bluetooth {
|
||||
export function startUartService(): void {
|
||||
// TODO
|
||||
}
|
||||
export function uartWrite(s : string): void {
|
||||
export function uartWriteString(s: string): void {
|
||||
serial.writeString(s)
|
||||
}
|
||||
|
||||
export function uartWriteBuffer(b: RefBuffer): void {
|
||||
serial.writeBuffer(b);
|
||||
}
|
||||
|
||||
export function uartReadBuffer(): RefBuffer {
|
||||
return pins.createBuffer(0);
|
||||
}
|
||||
|
||||
export function uartReadUntil(del: string): string {
|
||||
return serial.readUntil(del);
|
||||
}
|
||||
export function onDataReceived(delimiters: string, handler: RefAction) {
|
||||
export function onUartDataReceived(delimiters: string, handler: RefAction) {
|
||||
let b = board();
|
||||
b.bus.listen(DAL.MICROBIT_ID_BLE_UART, DAL.MICROBIT_UART_S_EVT_DELIM_MATCH, handler);
|
||||
}
|
||||
export function onBluetoothConnected(a : RefAction) {
|
||||
export function onBluetoothConnected(a: RefAction) {
|
||||
// TODO
|
||||
}
|
||||
export function onBluetoothDisconnected(a : RefAction) {
|
||||
export function onBluetoothDisconnected(a: RefAction) {
|
||||
// TODO
|
||||
}
|
||||
export function advertiseUrl(url: string, power: number, connectable: boolean) { }
|
||||
export function advertiseUidBuffer(nsAndInstance: Buffer, power: number, connectable: boolean) { }
|
||||
export function advertiseUidBuffer(nsAndInstance: RefBuffer, power: number, connectable: boolean) { }
|
||||
export function stopAdvertising() { }
|
||||
export function setTransmitPower(power: number) {}
|
||||
export function setTransmitPower(power: number) { }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
namespace pxsim {
|
||||
export function sendBufferAsm(buffer: Buffer, pin: DigitalPin) {
|
||||
export function sendBufferAsm(buffer: RefBuffer, pin: DigitalPin) {
|
||||
let b = board();
|
||||
if (b) {
|
||||
let np = b.neopixelState;
|
||||
if (np) {
|
||||
let buf = <Uint8Array>(<any>buffer).data;
|
||||
np.updateBuffer(buf, pin);
|
||||
let buf = buffer.data;
|
||||
np.updateBuffer(buf as any, pin); // TODO this is wrong
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace pxsim {
|
||||
time: number;
|
||||
}
|
||||
|
||||
// Extends interface in pxt-core
|
||||
export interface SimulatorRadioPacketPayload {
|
||||
bufferData?: Uint8Array;
|
||||
}
|
||||
|
||||
export class RadioDatagram {
|
||||
datagram: PacketBuffer[] = [];
|
||||
lastReceived: PacketBuffer = RadioDatagram.defaultPacket();
|
||||
@@ -24,8 +29,9 @@ namespace pxsim {
|
||||
const b = board();
|
||||
Runtime.postMessage(<SimulatorRadioPacketMessage>{
|
||||
type: "radiopacket",
|
||||
rssi: 70, // Not yet supported
|
||||
serial: b.radioState.bus.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
|
||||
broadcast: true,
|
||||
rssi: -42, // -42 is the strongest signal
|
||||
serial: b.radioState.transmitSerialNumber ? pxsim.control.deviceSerialNumber() : 0,
|
||||
time: new Date().getTime(),
|
||||
payload
|
||||
})
|
||||
@@ -42,22 +48,31 @@ namespace pxsim {
|
||||
rssi: -1,
|
||||
serial: 0,
|
||||
time: 0,
|
||||
payload: { type: -1, groupId: 0 }
|
||||
payload: { type: -1, groupId: 0, bufferData: new Uint8Array(0) }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class RadioBus {
|
||||
// uint8_t radioDefaultGroup = MICROBIT_RADIO_DEFAULT_GROUP;
|
||||
export class RadioState {
|
||||
power = 0;
|
||||
transmitSerialNumber = false;
|
||||
datagram: RadioDatagram;
|
||||
groupId: number;
|
||||
band: number;
|
||||
|
||||
constructor(private runtime: Runtime) {
|
||||
constructor(runtime: Runtime) {
|
||||
this.datagram = new RadioDatagram(runtime);
|
||||
this.power = 6; // default value
|
||||
this.groupId = 0;
|
||||
this.band = 7; // https://github.com/lancaster-university/microbit-dal/blob/master/inc/core/MicroBitConfig.h#L320
|
||||
}
|
||||
|
||||
public setGroup(id: number) {
|
||||
this.groupId = id & 0xff; // byte only
|
||||
}
|
||||
|
||||
setTransmitPower(power: number) {
|
||||
power = power | 0;
|
||||
this.power = Math.max(0, Math.min(7, power));
|
||||
}
|
||||
|
||||
@@ -65,54 +80,33 @@ namespace pxsim {
|
||||
this.transmitSerialNumber = !!sn;
|
||||
}
|
||||
|
||||
broadcast(msg: number, groupId: number) {
|
||||
setFrequencyBand(band: number) {
|
||||
band = band | 0;
|
||||
if (band < 0 || band > 83) return;
|
||||
this.band = band;
|
||||
}
|
||||
|
||||
raiseEvent(id: number, eventid: number) {
|
||||
Runtime.postMessage(<SimulatorEventBusMessage>{
|
||||
type: "eventbus",
|
||||
id: DAL.MES_BROADCAST_GENERAL_ID,
|
||||
eventid: msg,
|
||||
broadcast: true,
|
||||
id,
|
||||
eventid,
|
||||
power: this.power,
|
||||
group: groupId
|
||||
group: this.groupId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class RadioState {
|
||||
bus: RadioBus;
|
||||
groupId: number;
|
||||
|
||||
constructor(runtime: Runtime) {
|
||||
this.bus = new RadioBus(runtime);
|
||||
this.groupId = 0;
|
||||
}
|
||||
|
||||
public setGroup(id: number) {
|
||||
this.groupId = id & 0xff; // byte only
|
||||
}
|
||||
|
||||
public broadcast(msg: number) {
|
||||
this.bus.broadcast(msg, this.groupId)
|
||||
}
|
||||
|
||||
public receivePacket(packet: SimulatorRadioPacketMessage) {
|
||||
receivePacket(packet: SimulatorRadioPacketMessage) {
|
||||
if (this.groupId == packet.payload.groupId)
|
||||
this.bus.datagram.queue(packet)
|
||||
this.datagram.queue(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.radio {
|
||||
enum PacketPayloadType {
|
||||
NUMBER = 0,
|
||||
VALUE = 1,
|
||||
STRING = 2
|
||||
}
|
||||
|
||||
export function broadcastMessage(msg: number): void {
|
||||
board().radioState.broadcast(msg);
|
||||
}
|
||||
|
||||
export function onBroadcastMessageReceived(msg: number, handler: RefAction): void {
|
||||
pxtcore.registerWithDal(DAL.MES_BROADCAST_GENERAL_ID, msg, handler);
|
||||
export function raiseEvent(id: number, eventid: number): void {
|
||||
board().radioState.raiseEvent(id, eventid);
|
||||
}
|
||||
|
||||
export function setGroup(id: number): void {
|
||||
@@ -120,99 +114,34 @@ namespace pxsim.radio {
|
||||
}
|
||||
|
||||
export function setTransmitPower(power: number): void {
|
||||
board().radioState.bus.setTransmitPower(power);
|
||||
board().radioState.setTransmitPower(power);
|
||||
}
|
||||
|
||||
export function setTransmitSerialNumber(transmit: boolean): void {
|
||||
board().radioState.bus.setTransmitSerialNumber(transmit);
|
||||
export function setFrequencyBand(band: number) {
|
||||
board().radioState.setFrequencyBand(band);
|
||||
}
|
||||
|
||||
export function sendNumber(value: number): void {
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.NUMBER,
|
||||
export function sendRawPacket(buf: RefBuffer) {
|
||||
let cb = getResume();
|
||||
board().radioState.datagram.send({
|
||||
type: 0,
|
||||
groupId: board().radioState.groupId,
|
||||
numberData: value,
|
||||
bufferData: buf.data
|
||||
});
|
||||
setTimeout(cb, 1);
|
||||
}
|
||||
|
||||
export function sendString(msg: string): void {
|
||||
msg = msg.substr(0, 19);
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.STRING,
|
||||
groupId: board().radioState.groupId,
|
||||
stringData: msg,
|
||||
});
|
||||
}
|
||||
|
||||
export function writeValueToSerial(): void {
|
||||
const b = board();
|
||||
writePacketToSerial(b, b.radioState.bus.datagram.recv())
|
||||
}
|
||||
|
||||
export function writeReceivedPacketToSerial(): void {
|
||||
const b = board();
|
||||
writePacketToSerial(b, b.radioState.bus.datagram.lastReceived);
|
||||
}
|
||||
|
||||
export function sendValue(name: string, value: number) {
|
||||
name = name.substr(0, 12);
|
||||
const msg: number[] = [];
|
||||
msg.push()
|
||||
board().radioState.bus.datagram.send({
|
||||
type: PacketPayloadType.VALUE,
|
||||
groupId: board().radioState.groupId,
|
||||
stringData: name,
|
||||
numberData: value
|
||||
});
|
||||
}
|
||||
|
||||
export function receiveNumber(): number {
|
||||
const packet = board().radioState.bus.datagram.recv();
|
||||
return receivedNumber();
|
||||
}
|
||||
|
||||
export function receiveString(): string {
|
||||
const packet = board().radioState.bus.datagram.recv();
|
||||
return receivedString();
|
||||
export function readRawPacket() {
|
||||
const packet = board().radioState.datagram.recv();
|
||||
return new RefBuffer(packet.payload.bufferData)
|
||||
}
|
||||
|
||||
export function receivedSignalStrength(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.rssi;
|
||||
return board().radioState.datagram.lastReceived.rssi;
|
||||
}
|
||||
|
||||
export function onDataReceived(handler: RefAction): void {
|
||||
pxtcore.registerWithDal(DAL.MICROBIT_ID_RADIO, DAL.MICROBIT_RADIO_EVT_DATAGRAM, handler);
|
||||
radio.receiveNumber();
|
||||
}
|
||||
|
||||
export function receivedNumber(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.payload.numberData || 0;
|
||||
}
|
||||
|
||||
export function receivedSerial(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.serial;
|
||||
}
|
||||
|
||||
export function receivedString(): string {
|
||||
return initString(board().radioState.bus.datagram.lastReceived.payload.stringData || "");
|
||||
}
|
||||
|
||||
export function receivedTime(): number {
|
||||
return board().radioState.bus.datagram.lastReceived.time;
|
||||
}
|
||||
|
||||
function writePacketToSerial(b: DalBoard, p: PacketBuffer) {
|
||||
switch(p.payload.type) {
|
||||
case PacketPayloadType.NUMBER:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"v":${p.payload.numberData}}\r\n`)
|
||||
break;
|
||||
case PacketPayloadType.VALUE:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}","v":${p.payload.numberData}}\r\n`)
|
||||
break;
|
||||
case PacketPayloadType.STRING:
|
||||
b.writeSerial(`{"t":${p.time},"s":${p.serial},"n":"${p.payload.stringData}"}\r\n`)
|
||||
break;
|
||||
default:
|
||||
}
|
||||
readRawPacket();
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,21 @@ 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);
|
||||
}
|
||||
|
||||
export function writeBuffer(buf: RefBuffer) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function readUntil(del: string): string {
|
||||
return readString();
|
||||
}
|
||||
@@ -53,4 +63,18 @@ namespace pxsim.serial {
|
||||
export function redirectToUSB() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function setRxBufferSize(size: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function setTxBufferSize(size: number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export function readBuffer(length: number) {
|
||||
if (length <= 0)
|
||||
length = 64;
|
||||
return pins.createBuffer(length);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
"out": "../built/sim.js",
|
||||
"rootDir": ".",
|
||||
"newLine": "LF",
|
||||
"sourceMap": false
|
||||
"sourceMap": false,
|
||||
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
|
||||
"types": ["jquery", "bluebird"],
|
||||
"typeRoots": ["../node_modules/@types"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ namespace pxsim.visuals {
|
||||
wireframe: opts.wireframe,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/// <reference path="../../node_modules/pxt-core/typings/globals/bluebird/index.d.ts"/>
|
||||
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
|
||||
namespace pxsim.visuals {
|
||||
@@ -40,7 +39,7 @@ namespace pxsim.visuals {
|
||||
svg.fills(result.ledsOuter, defaultLedMatrixTheme.ledOff);
|
||||
|
||||
//turn off LEDs
|
||||
result.leds.forEach(l => (<SVGStylable><any>l).style.opacity = 0 + "");
|
||||
result.leds.forEach(l => (<SVGStyleElement><any>l).style.opacity = 0 + "");
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -103,7 +102,7 @@ namespace pxsim.visuals {
|
||||
public updateState() {
|
||||
if (this.state.disabled) {
|
||||
this.leds.forEach((led, i) => {
|
||||
let sel = (<SVGStylable><any>led)
|
||||
let sel = (<SVGStyleElement><any>led)
|
||||
sel.style.opacity = 0 + "";
|
||||
});
|
||||
return;
|
||||
@@ -112,7 +111,7 @@ namespace pxsim.visuals {
|
||||
const bw = this.state.displayMode == pxsim.DisplayMode.bw
|
||||
const img = this.state.image;
|
||||
this.leds.forEach((led, i) => {
|
||||
let sel = (<SVGStylable><any>led)
|
||||
let sel = (<SVGStyleElement><any>led)
|
||||
let dx = i % this.DRAW_SIZE;
|
||||
let dy = (i - dx) / this.DRAW_SIZE;
|
||||
if (dx < this.ACTIVE_SIZE && dy < this.ACTIVE_SIZE) {
|
||||
|
||||
@@ -1059,7 +1059,7 @@ namespace pxsim.visuals {
|
||||
constructor(public props: IBoardProps) {
|
||||
this.buildDom();
|
||||
if (props && props.wireframe)
|
||||
svg.addClass(this.element, "sim-wireframe");
|
||||
U.addClass(this.element, "sim-wireframe");
|
||||
|
||||
if (props && props.theme)
|
||||
this.updateTheme();
|
||||
@@ -1133,15 +1133,25 @@ namespace pxsim.visuals {
|
||||
|
||||
if (state.ledMatrixState.disabled) {
|
||||
this.leds.forEach((led, i) => {
|
||||
const sel = (<SVGStylable><any>led)
|
||||
const sel = (<SVGStyleElement><any>led)
|
||||
sel.style.opacity = "0";
|
||||
})
|
||||
} else {
|
||||
const bw = state.ledMatrixState.displayMode == pxsim.DisplayMode.bw
|
||||
const img = state.ledMatrixState.image;
|
||||
const br = state.ledMatrixState.brigthness != undefined ? state.ledMatrixState.brigthness : 255;
|
||||
this.leds.forEach((led, i) => {
|
||||
const sel = (<SVGStylable><any>led)
|
||||
sel.style.opacity = ((bw ? img.data[i] > 0 ? 255 : 0 : img.data[i]) / 255.0) + "";
|
||||
const sel = (<SVGStyleElement><any>led)
|
||||
let imgbr = bw ? (img.data[i] > 0 ? br : 0) : img.data[i];
|
||||
// correct brightness
|
||||
const opacity = imgbr > 0 ? imgbr / 255 * 155 + 100 : 0;
|
||||
const transfrom = imgbr > 0 ? imgbr / 255 * 0.4 + 0.6 : 0;
|
||||
sel.style.opacity = (opacity / 255) + "";
|
||||
if (transfrom > 0) {
|
||||
(sel.style as any).transformBox = 'fill-box';
|
||||
sel.style.transformOrigin = '50% 50%';
|
||||
sel.style.transform = `scale(${transfrom})`;
|
||||
}
|
||||
})
|
||||
}
|
||||
this.updatePins();
|
||||
@@ -1154,8 +1164,8 @@ namespace pxsim.visuals {
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
|
||||
if (!runtime || runtime.dead) svg.addClass(this.element, "grayscale");
|
||||
else svg.removeClass(this.element, "grayscale");
|
||||
if (!runtime || runtime.dead) U.addClass(this.element, "grayscale");
|
||||
else U.removeClass(this.element, "grayscale");
|
||||
}
|
||||
|
||||
private updateRgbLed() {
|
||||
@@ -1445,7 +1455,7 @@ namespace pxsim.visuals {
|
||||
this.pins = pinNames.map(n => {
|
||||
let p = this.element.getElementById(n) as SVGElement;
|
||||
if(!p) console.log("missing "+n);
|
||||
svg.addClass(p, "sim-pin");
|
||||
U.addClass(p, "sim-pin");
|
||||
return p;
|
||||
});
|
||||
|
||||
@@ -1463,9 +1473,9 @@ namespace pxsim.visuals {
|
||||
// BTN A, B
|
||||
const btnids = ["BTN_A", "BTN_B"];
|
||||
this.buttonsOuter = btnids.map(n => this.element.getElementById(n + "_BOX") as SVGElement);
|
||||
this.buttonsOuter.forEach(b => svg.addClass(b, "sim-button-outer"));
|
||||
this.buttonsOuter.forEach(b => U.addClass(b, "sim-button-outer"));
|
||||
this.buttons = btnids.map(n => this.element.getElementById(n) as SVGElement);
|
||||
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
|
||||
this.buttons.forEach(b => U.addClass(b, "sim-button"));
|
||||
|
||||
// BTN A+B
|
||||
const outerBtn = (left: number, top: number) => {
|
||||
@@ -1581,7 +1591,7 @@ namespace pxsim.visuals {
|
||||
let state = this.board;
|
||||
let pin = state.edgeConnectorState.pins[index];
|
||||
let svgpin = this.pins[index];
|
||||
svg.addClass(svgpin, "touched");
|
||||
U.addClass(svgpin, "touched");
|
||||
if (pin.mode & PinFlags.Input) {
|
||||
let cursor = svg.cursorPoint(pt, this.element, ev);
|
||||
let v = (400 - cursor.y) / 40 * 1023
|
||||
@@ -1594,17 +1604,18 @@ namespace pxsim.visuals {
|
||||
let state = this.board;
|
||||
let pin = state.edgeConnectorState.pins[index];
|
||||
let svgpin = this.pins[index];
|
||||
svg.removeClass(svgpin, "touched");
|
||||
U.removeClass(svgpin, "touched");
|
||||
this.updatePin(pin, index);
|
||||
return false;
|
||||
});
|
||||
})
|
||||
this.pins.slice(0, 3).forEach((btn, index) => {
|
||||
btn.addEventListener(pointerEvents.down, ev => {
|
||||
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;
|
||||
@@ -1622,11 +1633,12 @@ namespace pxsim.visuals {
|
||||
let bpState = this.board.buttonPairState;
|
||||
let stateButtons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
|
||||
this.buttonsOuter.slice(0, 2).forEach((btn, index) => {
|
||||
btn.addEventListener(pointerEvents.down, ev => {
|
||||
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);
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[index].pressed = false;
|
||||
@@ -1640,7 +1652,7 @@ namespace pxsim.visuals {
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
})
|
||||
this.buttonsOuter[2].addEventListener(pointerEvents.down, ev => {
|
||||
pointerEvents.down.forEach(evid => this.buttonsOuter[2].addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = true;
|
||||
stateButtons[1].pressed = true;
|
||||
@@ -1648,7 +1660,8 @@ namespace pxsim.visuals {
|
||||
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.buttonsOuter[2].addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = false;
|
||||
@@ -1672,4 +1685,4 @@ namespace pxsim.visuals {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
|
||||
/// <reference path="../../libs/core/dal.d.ts"/>
|
||||
/// <reference path="../../libs/core/shims.d.ts"/>
|
||||
/// <reference path="../../libs/core/enums.d.ts"/>
|
||||
|
||||
namespace pxsim.visuals {
|
||||
@@ -133,7 +132,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
//show the canvas if it's hidden
|
||||
svg.removeClass(this.background, "hidden");
|
||||
U.removeClass(this.background, "hidden");
|
||||
|
||||
//resize if necessary
|
||||
let [first, last] = [this.pixels[0], this.pixels[this.pixels.length - 1]]
|
||||
@@ -179,6 +178,7 @@ namespace pxsim.visuals {
|
||||
.sim-neopixel-canvas-parent:hover {
|
||||
transform-origin: center;
|
||||
transform: scale(4) translateY(-60px);
|
||||
-moz-transform: scale(4) translateY(-220px);
|
||||
}
|
||||
.sim-neopixel-canvas .hidden {
|
||||
visibility:hidden;
|
||||
@@ -239,4 +239,4 @@ namespace pxsim.visuals {
|
||||
}
|
||||
public updateTheme(): void { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||