Merge branch 'breadboarding' of https://github.com/Microsoft/pxt-microbit into breadboarding
This commit is contained in:
commit
85e3148f23
@ -81,7 +81,13 @@ namespace pxsim {
|
|||||||
let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json
|
let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json
|
||||||
let fnArgs = msg.fnArgs;
|
let fnArgs = msg.fnArgs;
|
||||||
|
|
||||||
let viewHost = new visuals.BoardHost(this, boardDef, cmpsList, cmpDefs, fnArgs);
|
let viewHost = new visuals.BoardHost({
|
||||||
|
state: this,
|
||||||
|
boardDef: boardDef,
|
||||||
|
cmpsList: cmpsList,
|
||||||
|
cmpDefs: cmpDefs,
|
||||||
|
fnArgs: fnArgs
|
||||||
|
});
|
||||||
|
|
||||||
document.body.innerHTML = ""; // clear children
|
document.body.innerHTML = ""; // clear children
|
||||||
document.body.appendChild(viewHost.getView());
|
document.body.appendChild(viewHost.getView());
|
||||||
|
@ -45,7 +45,6 @@ namespace pxsim.instructions {
|
|||||||
const NUM_MARGIN = 5;
|
const NUM_MARGIN = 5;
|
||||||
const FRONT_PAGE_BOARD_WIDTH = 200;
|
const FRONT_PAGE_BOARD_WIDTH = 200;
|
||||||
const STYLE = `
|
const STYLE = `
|
||||||
${visuals.BOARD_SYTLE}
|
|
||||||
.instr-panel {
|
.instr-panel {
|
||||||
margin: ${PANEL_MARGIN}px;
|
margin: ${PANEL_MARGIN}px;
|
||||||
padding: ${PANEL_PADDING}px;
|
padding: ${PANEL_PADDING}px;
|
||||||
@ -126,22 +125,12 @@ namespace pxsim.instructions {
|
|||||||
cmpScale?: number
|
cmpScale?: number
|
||||||
};
|
};
|
||||||
function mkBoardImgSvg(def: BoardImageDefinition): visuals.SVGElAndSize {
|
function mkBoardImgSvg(def: BoardImageDefinition): visuals.SVGElAndSize {
|
||||||
let img = svg.elt( "image");
|
return new visuals.MicrobitBoardSvg({
|
||||||
let [l, t] = [0, 0];
|
theme: visuals.randomTheme()
|
||||||
let w = def.width;
|
}).getView();
|
||||||
let h = def.height;
|
|
||||||
svg.hydrate(img, {
|
|
||||||
class: "sim-board",
|
|
||||||
x: l,
|
|
||||||
y: t,
|
|
||||||
width: def.width,
|
|
||||||
height: def.height,
|
|
||||||
"href": `${def.image}`});
|
|
||||||
|
|
||||||
return {el: img, w: w, h: h, x: l, y: t};
|
|
||||||
}
|
}
|
||||||
function mkBBSvg(): visuals.SVGElAndSize {
|
function mkBBSvg(): visuals.SVGElAndSize {
|
||||||
let bb = new visuals.Breadboard();
|
let bb = new visuals.Breadboard({});
|
||||||
return bb.getSVGAndSize();
|
return bb.getSVGAndSize();
|
||||||
}
|
}
|
||||||
function wrapSvg(el: visuals.SVGElAndSize, opts: mkCmpDivOpts): HTMLElement {
|
function wrapSvg(el: visuals.SVGElAndSize, opts: mkCmpDivOpts): HTMLElement {
|
||||||
@ -280,7 +269,8 @@ namespace pxsim.instructions {
|
|||||||
function mkCmpDiv(type: "wire" | string, opts: mkCmpDivOpts): HTMLElement {
|
function mkCmpDiv(type: "wire" | string, opts: mkCmpDivOpts): HTMLElement {
|
||||||
let el: visuals.SVGElAndSize;
|
let el: visuals.SVGElAndSize;
|
||||||
if (type == "wire") {
|
if (type == "wire") {
|
||||||
el = visuals.mkWirePart([0, 0], opts.wireClr || "red");
|
//TODO: support non-croc wire parts
|
||||||
|
el = visuals.mkWirePart([0, 0], opts.wireClr || "red", true);
|
||||||
} else {
|
} else {
|
||||||
let cnstr = builtinComponentPartVisual[type];
|
let cnstr = builtinComponentPartVisual[type];
|
||||||
el = cnstr([0, 0]);
|
el = cnstr([0, 0]);
|
||||||
@ -290,6 +280,7 @@ namespace pxsim.instructions {
|
|||||||
type BoardProps = {
|
type BoardProps = {
|
||||||
boardDef: BoardDefinition,
|
boardDef: BoardDefinition,
|
||||||
cmpDefs: Map<PartDefinition>,
|
cmpDefs: Map<PartDefinition>,
|
||||||
|
fnArgs: any,
|
||||||
allAlloc: AllocatorResult,
|
allAlloc: AllocatorResult,
|
||||||
stepToWires: WireInst[][],
|
stepToWires: WireInst[][],
|
||||||
stepToCmps: CmpInst[][]
|
stepToCmps: CmpInst[][]
|
||||||
@ -338,6 +329,7 @@ namespace pxsim.instructions {
|
|||||||
return {
|
return {
|
||||||
boardDef: allocOpts.boardDef,
|
boardDef: allocOpts.boardDef,
|
||||||
cmpDefs: allocOpts.cmpDefs,
|
cmpDefs: allocOpts.cmpDefs,
|
||||||
|
fnArgs: allocOpts.fnArgs,
|
||||||
allAlloc: allocRes,
|
allAlloc: allocRes,
|
||||||
stepToWires: stepToWires,
|
stepToWires: stepToWires,
|
||||||
stepToCmps: stepToCmps,
|
stepToCmps: stepToCmps,
|
||||||
@ -348,28 +340,19 @@ namespace pxsim.instructions {
|
|||||||
allWireColors: allWireColors,
|
allWireColors: allWireColors,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function mkBoard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, width: number, buildMode: boolean = false): visuals.GenericBoardSvg {
|
function mkBlankBoardAndBreadboard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, fnArgs: any, width: number, buildMode: boolean = false): visuals.BoardHost {
|
||||||
let board = new visuals.GenericBoardSvg({
|
let state = runtime.board as pxsim.DalBoard;
|
||||||
runtime: pxsim.runtime,
|
let boardHost = new visuals.BoardHost({
|
||||||
boardDef: boardDef,
|
state: state,
|
||||||
activeComponents: [],
|
boardDef: boardDef,
|
||||||
componentDefinitions: cmpDefs,
|
forceBreadboard: true,
|
||||||
})
|
cmpDefs: cmpDefs,
|
||||||
svg.hydrate(board.hostElement, {
|
maxWidth: `${width}px`,
|
||||||
"width": width,
|
fnArgs: fnArgs,
|
||||||
|
wireframe: buildMode,
|
||||||
});
|
});
|
||||||
svg.addClass(board.hostElement, "board-svg");
|
let view = boardHost.getView();
|
||||||
if (buildMode) {
|
svg.addClass(view, "board-svg");
|
||||||
svg.hydrate(board.background, {
|
|
||||||
"href": `${(<BoardImageDefinition>boardDef.visual).outlineImage}`
|
|
||||||
})
|
|
||||||
svg.addClass(board.hostElement, "sim-board-outline")
|
|
||||||
let bb = board.breadboard.bb;
|
|
||||||
svg.addClass(bb, "sim-bb-outline")
|
|
||||||
let style = <SVGStyleElement>svg.child(bb, "style", {});
|
|
||||||
}
|
|
||||||
|
|
||||||
board.updateState();
|
|
||||||
|
|
||||||
//set smiley
|
//set smiley
|
||||||
//HACK
|
//HACK
|
||||||
@ -383,11 +366,12 @@ namespace pxsim.instructions {
|
|||||||
// img.set(4, 2, 255);
|
// img.set(4, 2, 255);
|
||||||
// board.updateState();
|
// board.updateState();
|
||||||
|
|
||||||
return board;
|
return boardHost;
|
||||||
}
|
}
|
||||||
function drawSteps(board: visuals.GenericBoardSvg, step: number, props: BoardProps) {
|
function drawSteps(board: visuals.BoardHost, step: number, props: BoardProps) {
|
||||||
|
let view = board.getView();
|
||||||
if (step > 0) {
|
if (step > 0) {
|
||||||
svg.addClass(board.hostElement, "grayed");
|
svg.addClass(view, "grayed");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i <= step; i++) {
|
for (let i = 0; i <= step; i++) {
|
||||||
@ -399,16 +383,15 @@ namespace pxsim.instructions {
|
|||||||
if (i === step) {
|
if (i === step) {
|
||||||
//location highlights
|
//location highlights
|
||||||
if (w.start.type == "breadboard") {
|
if (w.start.type == "breadboard") {
|
||||||
let [row, col] = (<BBLoc>w.start).rowCol;
|
let lbls = board.highlightBreadboardPin((<BBLoc>w.start).rowCol);
|
||||||
let lbls = board.breadboard.highlightLoc(row, col);
|
|
||||||
} else {
|
} else {
|
||||||
board.highlightLoc((<BoardLoc>w.start).pin);
|
board.highlightBoardPin((<BoardLoc>w.start).pin);
|
||||||
}
|
}
|
||||||
if (w.end.type == "breadboard") {
|
if (w.end.type == "breadboard") {
|
||||||
let [row, col] = (<BBLoc>w.end).rowCol;
|
let [row, col] = (<BBLoc>w.end).rowCol;
|
||||||
let lbls = board.breadboard.highlightLoc(row, col);
|
let lbls = board.highlightBreadboardPin((<BBLoc>w.end).rowCol);
|
||||||
} else {
|
} else {
|
||||||
board.highlightLoc((<BoardLoc>w.end).pin);
|
board.highlightBoardPin((<BoardLoc>w.end).pin);
|
||||||
}
|
}
|
||||||
//highlight wire
|
//highlight wire
|
||||||
board.highlightWire(wire);
|
board.highlightWire(wire);
|
||||||
@ -419,14 +402,14 @@ namespace pxsim.instructions {
|
|||||||
if (cmps) {
|
if (cmps) {
|
||||||
cmps.forEach(cmpInst => {
|
cmps.forEach(cmpInst => {
|
||||||
let cmp = board.addComponent(cmpInst)
|
let cmp = board.addComponent(cmpInst)
|
||||||
let [row, col]: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn}`];
|
let rowCol: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn}`];
|
||||||
//last step
|
//last step
|
||||||
if (i === step) {
|
if (i === step) {
|
||||||
board.breadboard.highlightLoc(row, col);
|
board.highlightBreadboardPin(rowCol);
|
||||||
if (cmpInst.visual === "buttonpair") {
|
if (cmpInst.visual === "buttonpair") {
|
||||||
//TODO: don't specialize this
|
//TODO: don't specialize this
|
||||||
let [row2, col2]: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn + 3}`];
|
let rowCol2: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn + 3}`];
|
||||||
board.breadboard.highlightLoc(row2, col2);
|
board.highlightBreadboardPin(rowCol2);
|
||||||
}
|
}
|
||||||
svg.addClass(cmp.element, "notgrayed");
|
svg.addClass(cmp.element, "notgrayed");
|
||||||
}
|
}
|
||||||
@ -498,9 +481,9 @@ namespace pxsim.instructions {
|
|||||||
let panel = mkPanel();
|
let panel = mkPanel();
|
||||||
|
|
||||||
//board
|
//board
|
||||||
let board = mkBoard(props.boardDef, props.cmpDefs, BOARD_WIDTH, true)
|
let board = mkBlankBoardAndBreadboard(props.boardDef, props.cmpDefs, props.fnArgs, BOARD_WIDTH, true)
|
||||||
drawSteps(board, step, props);
|
drawSteps(board, step, props);
|
||||||
panel.appendChild(board.hostElement);
|
panel.appendChild(board.getView());
|
||||||
|
|
||||||
//number
|
//number
|
||||||
let numDiv = document.createElement("div");
|
let numDiv = document.createElement("div");
|
||||||
@ -568,9 +551,9 @@ namespace pxsim.instructions {
|
|||||||
function updateFrontPanel(props: BoardProps): [HTMLElement, BoardProps] {
|
function updateFrontPanel(props: BoardProps): [HTMLElement, BoardProps] {
|
||||||
let panel = document.getElementById("front-panel");
|
let panel = document.getElementById("front-panel");
|
||||||
|
|
||||||
let board = mkBoard(props.boardDef, props.cmpDefs, FRONT_PAGE_BOARD_WIDTH, false);
|
let board = mkBlankBoardAndBreadboard(props.boardDef, props.cmpDefs, props.fnArgs, FRONT_PAGE_BOARD_WIDTH, false);
|
||||||
board.addAll(props.allAlloc);
|
board.addAll(props.allAlloc);
|
||||||
panel.appendChild(board.hostElement);
|
panel.appendChild(board.getView());
|
||||||
|
|
||||||
return [panel, props];
|
return [panel, props];
|
||||||
}
|
}
|
||||||
@ -579,9 +562,9 @@ namespace pxsim.instructions {
|
|||||||
|
|
||||||
let panel = mkPanel();
|
let panel = mkPanel();
|
||||||
addClass(panel, "back-panel");
|
addClass(panel, "back-panel");
|
||||||
let board = mkBoard(props.boardDef, props.cmpDefs, BACK_PAGE_BOARD_WIDTH, false)
|
let board = mkBlankBoardAndBreadboard(props.boardDef, props.cmpDefs, props.fnArgs, BACK_PAGE_BOARD_WIDTH, false)
|
||||||
board.addAll(props.allAlloc);
|
board.addAll(props.allAlloc);
|
||||||
panel.appendChild(board.hostElement);
|
panel.appendChild(board.getView());
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
@ -646,11 +629,14 @@ namespace pxsim.instructions {
|
|||||||
const cmpDefs = PART_DEFINITIONS;
|
const cmpDefs = PART_DEFINITIONS;
|
||||||
|
|
||||||
//props
|
//props
|
||||||
let dummyBreadboard = new visuals.Breadboard();
|
let dummyBreadboard = new visuals.Breadboard({});
|
||||||
|
let onboardCmps = boardDef.onboardComponents || [];
|
||||||
|
let activeComponents = (parts || []).filter(c => onboardCmps.indexOf(c) < 0);
|
||||||
|
activeComponents.sort();
|
||||||
let props = mkBoardProps({
|
let props = mkBoardProps({
|
||||||
boardDef: boardDef,
|
boardDef: boardDef,
|
||||||
cmpDefs: cmpDefs,
|
cmpDefs: cmpDefs,
|
||||||
cmpList: parts,
|
cmpList: activeComponents,
|
||||||
fnArgs: fnArgs,
|
fnArgs: fnArgs,
|
||||||
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
|
getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
|
||||||
});
|
});
|
||||||
@ -669,7 +655,7 @@ namespace pxsim.instructions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//final
|
//final
|
||||||
let finalPanel = mkFinalPanel(props);
|
// let finalPanel = mkFinalPanel(props);
|
||||||
document.body.appendChild(finalPanel);
|
// document.body.appendChild(finalPanel);
|
||||||
}
|
}
|
||||||
}
|
}
|
254
sim/microbit.ts
254
sim/microbit.ts
@ -1,4 +1,121 @@
|
|||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
const MB_STYLE = `
|
||||||
|
svg.sim {
|
||||||
|
margin-bottom:1em;
|
||||||
|
}
|
||||||
|
svg.sim.grayscale {
|
||||||
|
-moz-filter: grayscale(1);
|
||||||
|
-webkit-filter: grayscale(1);
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
.sim-button {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-button-outer:hover {
|
||||||
|
stroke:grey;
|
||||||
|
stroke-width: 3px;
|
||||||
|
}
|
||||||
|
.sim-button-nut {
|
||||||
|
fill:#704A4A;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
.sim-button-nut:hover {
|
||||||
|
stroke:1px solid #704A4A;
|
||||||
|
}
|
||||||
|
.sim-pin:hover {
|
||||||
|
stroke:#D4AF37;
|
||||||
|
stroke-width:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-pin-touch.touched:hover {
|
||||||
|
stroke:darkorange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-led-back:hover {
|
||||||
|
stroke:#a0a0a0;
|
||||||
|
stroke-width:3px;
|
||||||
|
}
|
||||||
|
.sim-led:hover {
|
||||||
|
stroke:#ff7f7f;
|
||||||
|
stroke-width:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-systemled {
|
||||||
|
fill:#333;
|
||||||
|
stroke:#555;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-light-level-button {
|
||||||
|
stroke:#fff;
|
||||||
|
stroke-width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-antenna {
|
||||||
|
stroke:#555;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-text {
|
||||||
|
font-family:"Lucida Console", Monaco, monospace;
|
||||||
|
font-size:25px;
|
||||||
|
fill:#fff;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-text-pin {
|
||||||
|
font-family:"Lucida Console", Monaco, monospace;
|
||||||
|
font-size:20px;
|
||||||
|
fill:#fff;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-thermometer {
|
||||||
|
stroke:#aaa;
|
||||||
|
stroke-width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* animations */
|
||||||
|
.sim-theme-glow {
|
||||||
|
animation-name: sim-theme-glow-animation;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-direction: alternate;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-duration: 1.25s;
|
||||||
|
}
|
||||||
|
@keyframes sim-theme-glow-animation {
|
||||||
|
from { opacity: 1; }
|
||||||
|
to { opacity: 0.75; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-flash {
|
||||||
|
animation-name: sim-flash-animation;
|
||||||
|
animation-duration: 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sim-flash-animation {
|
||||||
|
from { fill: yellow; }
|
||||||
|
to { fill: default; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sim-flash-stroke {
|
||||||
|
animation-name: sim-flash-stroke-animation;
|
||||||
|
animation-duration: 0.4s;
|
||||||
|
animation-timing-function: ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sim-flash-stroke-animation {
|
||||||
|
from { stroke: yellow; }
|
||||||
|
to { stroke: default; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wireframe */
|
||||||
|
.sim-wireframe * {
|
||||||
|
fill: none;
|
||||||
|
stroke: black;
|
||||||
|
}
|
||||||
|
`;
|
||||||
const pins4onXs = [66.7, 79.1, 91.4, 103.7, 164.3, 176.6, 188.9, 201.3, 213.6, 275.2, 287.5, 299.8, 312.1, 324.5, 385.1, 397.4, 409.7, 422];
|
const pins4onXs = [66.7, 79.1, 91.4, 103.7, 164.3, 176.6, 188.9, 201.3, 213.6, 275.2, 287.5, 299.8, 312.1, 324.5, 385.1, 397.4, 409.7, 422];
|
||||||
const pins4onMids = pins4onXs.map(x => x + 5);
|
const pins4onMids = pins4onXs.map(x => x + 5);
|
||||||
const littlePinDist = pins4onMids[1] - pins4onMids[0];
|
const littlePinDist = pins4onMids[1] - pins4onMids[0];
|
||||||
@ -83,9 +200,10 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IBoardProps {
|
export interface IBoardProps {
|
||||||
runtime: pxsim.Runtime;
|
runtime?: pxsim.Runtime;
|
||||||
theme?: IBoardTheme;
|
theme?: IBoardTheme;
|
||||||
disableTilt?: boolean;
|
disableTilt?: boolean;
|
||||||
|
wireframe?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointerEvents = !!(window as any).PointerEvent ? {
|
const pointerEvents = !!(window as any).PointerEvent ? {
|
||||||
@ -132,15 +250,20 @@ namespace pxsim.visuals {
|
|||||||
private pinNmToCoord: Map<Coord> = {};
|
private pinNmToCoord: Map<Coord> = {};
|
||||||
|
|
||||||
constructor(public props: IBoardProps) {
|
constructor(public props: IBoardProps) {
|
||||||
this.board = this.props.runtime.board as pxsim.DalBoard;
|
|
||||||
this.board.updateSubscribers.push(() => this.updateState());
|
|
||||||
|
|
||||||
this.recordPinCoords();
|
this.recordPinCoords();
|
||||||
this.buildDom();
|
this.buildDom();
|
||||||
|
if (props && props.wireframe)
|
||||||
|
svg.addClass(this.element, "sim-wireframe");
|
||||||
|
|
||||||
this.updateTheme();
|
if (props && props.theme)
|
||||||
this.updateState();
|
this.updateTheme();
|
||||||
this.attachEvents();
|
|
||||||
|
if (props && props.runtime) {
|
||||||
|
this.board = this.props.runtime.board as pxsim.DalBoard;
|
||||||
|
this.board.updateSubscribers.push(() => this.updateState());
|
||||||
|
this.updateState();
|
||||||
|
this.attachEvents();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getView(): SVGAndSize<SVGSVGElement> {
|
public getView(): SVGAndSize<SVGSVGElement> {
|
||||||
@ -157,6 +280,10 @@ namespace pxsim.visuals {
|
|||||||
return this.pinNmToCoord[pinNm];
|
return this.pinNmToCoord[pinNm];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public highlightPin(pinNm: string): void {
|
||||||
|
//TODO: for instructions
|
||||||
|
}
|
||||||
|
|
||||||
public getPinDist(): number {
|
public getPinDist(): number {
|
||||||
return littlePinDist * 1.7;
|
return littlePinDist * 1.7;
|
||||||
}
|
}
|
||||||
@ -438,118 +565,7 @@ namespace pxsim.visuals {
|
|||||||
"height": MB_HEIGHT + "px",
|
"height": MB_HEIGHT + "px",
|
||||||
});
|
});
|
||||||
this.style = <SVGStyleElement>svg.child(this.element, "style", {});
|
this.style = <SVGStyleElement>svg.child(this.element, "style", {});
|
||||||
this.style.textContent = `
|
this.style.textContent = MB_STYLE;
|
||||||
svg.sim {
|
|
||||||
margin-bottom:1em;
|
|
||||||
}
|
|
||||||
svg.sim.grayscale {
|
|
||||||
-moz-filter: grayscale(1);
|
|
||||||
-webkit-filter: grayscale(1);
|
|
||||||
filter: grayscale(1);
|
|
||||||
}
|
|
||||||
.sim-button {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-button-outer:hover {
|
|
||||||
stroke:grey;
|
|
||||||
stroke-width: 3px;
|
|
||||||
}
|
|
||||||
.sim-button-nut {
|
|
||||||
fill:#704A4A;
|
|
||||||
pointer-events:none;
|
|
||||||
}
|
|
||||||
.sim-button-nut:hover {
|
|
||||||
stroke:1px solid #704A4A;
|
|
||||||
}
|
|
||||||
.sim-pin:hover {
|
|
||||||
stroke:#D4AF37;
|
|
||||||
stroke-width:2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-pin-touch.touched:hover {
|
|
||||||
stroke:darkorange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-led-back:hover {
|
|
||||||
stroke:#a0a0a0;
|
|
||||||
stroke-width:3px;
|
|
||||||
}
|
|
||||||
.sim-led:hover {
|
|
||||||
stroke:#ff7f7f;
|
|
||||||
stroke-width:3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-systemled {
|
|
||||||
fill:#333;
|
|
||||||
stroke:#555;
|
|
||||||
stroke-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-light-level-button {
|
|
||||||
stroke:#fff;
|
|
||||||
stroke-width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-antenna {
|
|
||||||
stroke:#555;
|
|
||||||
stroke-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-text {
|
|
||||||
font-family:"Lucida Console", Monaco, monospace;
|
|
||||||
font-size:25px;
|
|
||||||
fill:#fff;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-text-pin {
|
|
||||||
font-family:"Lucida Console", Monaco, monospace;
|
|
||||||
font-size:20px;
|
|
||||||
fill:#fff;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-thermometer {
|
|
||||||
stroke:#aaa;
|
|
||||||
stroke-width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* animations */
|
|
||||||
.sim-theme-glow {
|
|
||||||
animation-name: sim-theme-glow-animation;
|
|
||||||
animation-timing-function: ease-in-out;
|
|
||||||
animation-direction: alternate;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-duration: 1.25s;
|
|
||||||
}
|
|
||||||
@keyframes sim-theme-glow-animation {
|
|
||||||
from { opacity: 1; }
|
|
||||||
to { opacity: 0.75; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-flash {
|
|
||||||
animation-name: sim-flash-animation;
|
|
||||||
animation-duration: 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sim-flash-animation {
|
|
||||||
from { fill: yellow; }
|
|
||||||
to { fill: default; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.sim-flash-stroke {
|
|
||||||
animation-name: sim-flash-stroke-animation;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
animation-timing-function: ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sim-flash-stroke-animation {
|
|
||||||
from { stroke: yellow; }
|
|
||||||
to { stroke: default; }
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
|
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
|
||||||
this.g = <SVGGElement>svg.elt("g");
|
this.g = <SVGGElement>svg.elt("g");
|
||||||
|
@ -209,5 +209,6 @@ namespace pxsim.visuals {
|
|||||||
getView(): SVGAndSize<SVGSVGElement>;
|
getView(): SVGAndSize<SVGSVGElement>;
|
||||||
getCoord(pinNm: string): Coord;
|
getCoord(pinNm: string): Coord;
|
||||||
getPinDist(): number;
|
getPinDist(): number;
|
||||||
|
highlightPin(pinNm: string): void;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,15 @@
|
|||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
export interface BoardHostOpts {
|
||||||
|
state: DalBoard,
|
||||||
|
boardDef: BoardDefinition,
|
||||||
|
cmpsList?: string[],
|
||||||
|
cmpDefs: Map<PartDefinition>,
|
||||||
|
fnArgs: any,
|
||||||
|
forceBreadboard?: boolean,
|
||||||
|
maxWidth?: string,
|
||||||
|
maxHeight?: string
|
||||||
|
wireframe?: boolean
|
||||||
|
}
|
||||||
export class BoardHost {
|
export class BoardHost {
|
||||||
private components: IBoardComponent<any>[] = [];
|
private components: IBoardComponent<any>[] = [];
|
||||||
private wireFactory: WireFactory;
|
private wireFactory: WireFactory;
|
||||||
@ -11,33 +22,27 @@ namespace pxsim.visuals {
|
|||||||
private defs: SVGDefsElement;
|
private defs: SVGDefsElement;
|
||||||
private state: DalBoard;
|
private state: DalBoard;
|
||||||
|
|
||||||
constructor(state: DalBoard, boardDef: BoardDefinition, cmpsList: string[], cmpDefs: Map<PartDefinition>, fnArgs: any) {
|
constructor(opts: BoardHostOpts) {
|
||||||
this.state = state;
|
this.state = opts.state;
|
||||||
let onboardCmps = boardDef.onboardComponents || [];
|
let onboardCmps = opts.boardDef.onboardComponents || [];
|
||||||
let activeComponents = (cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
|
let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
|
||||||
activeComponents.sort();
|
activeComponents.sort();
|
||||||
|
|
||||||
// boardDef.visual === "microbit"
|
|
||||||
this.boardView = new visuals.MicrobitBoardSvg({
|
this.boardView = new visuals.MicrobitBoardSvg({
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
theme: visuals.randomTheme(),
|
theme: visuals.randomTheme(),
|
||||||
disableTilt: false
|
disableTilt: false,
|
||||||
|
wireframe: opts.wireframe,
|
||||||
});
|
});
|
||||||
//TODO: port Arduino/generic board
|
|
||||||
// this.boardView = new visuals.GenericBoardSvg({
|
|
||||||
// boardDef: boardDef,
|
|
||||||
// activeComponents: activeComponents,
|
|
||||||
// componentDefinitions: cmpDefs,
|
|
||||||
// runtime: runtime,
|
|
||||||
// fnArgs: fnArgs
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
const VIEW_WIDTH = "100%";
|
let maxWidth = opts.maxWidth || "100%";
|
||||||
const VIEW_HEIGHT = "100%";
|
let maxHeight = opts.maxHeight || "100%";
|
||||||
|
|
||||||
if (0 < activeComponents.length) {
|
let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard;
|
||||||
this.breadboard = new Breadboard();
|
if (useBreadboard) {
|
||||||
|
this.breadboard = new Breadboard({
|
||||||
|
wireframe: opts.wireframe,
|
||||||
|
});
|
||||||
let composition = composeSVG({
|
let composition = composeSVG({
|
||||||
el1: this.boardView.getView(),
|
el1: this.boardView.getView(),
|
||||||
scaleUnit1: this.boardView.getPinDist(),
|
scaleUnit1: this.boardView.getPinDist(),
|
||||||
@ -45,8 +50,8 @@ namespace pxsim.visuals {
|
|||||||
scaleUnit2: this.breadboard.getPinDist(),
|
scaleUnit2: this.breadboard.getPinDist(),
|
||||||
margin: [0, 0, 10, 0],
|
margin: [0, 0, 10, 0],
|
||||||
middleMargin: 80,
|
middleMargin: 80,
|
||||||
maxWidth: VIEW_WIDTH,
|
maxWidth: maxWidth,
|
||||||
maxHeight: VIEW_HEIGHT,
|
maxHeight: maxHeight,
|
||||||
});
|
});
|
||||||
let under = composition.under;
|
let under = composition.under;
|
||||||
let over = composition.over;
|
let over = composition.over;
|
||||||
@ -62,9 +67,9 @@ namespace pxsim.visuals {
|
|||||||
this.wireFactory = new WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
|
this.wireFactory = new WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
|
||||||
|
|
||||||
let allocRes = allocateDefinitions({
|
let allocRes = allocateDefinitions({
|
||||||
boardDef: boardDef,
|
boardDef: opts.boardDef,
|
||||||
cmpDefs: cmpDefs,
|
cmpDefs: opts.cmpDefs,
|
||||||
fnArgs: fnArgs,
|
fnArgs: opts.fnArgs,
|
||||||
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
|
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
|
||||||
cmpList: activeComponents,
|
cmpList: activeComponents,
|
||||||
});
|
});
|
||||||
@ -74,14 +79,37 @@ namespace pxsim.visuals {
|
|||||||
let el = this.boardView.getView().el;
|
let el = this.boardView.getView().el;
|
||||||
this.view = el;
|
this.view = el;
|
||||||
svg.hydrate(this.view, {
|
svg.hydrate(this.view, {
|
||||||
width: VIEW_WIDTH,
|
width: maxWidth,
|
||||||
height: VIEW_HEIGHT,
|
height: maxHeight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.updateSubscribers.push(() => this.updateState());
|
this.state.updateSubscribers.push(() => this.updateState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public highlightBoardPin(pinNm: string) {
|
||||||
|
this.boardView.highlightPin(pinNm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public highlightBreadboardPin(rowCol: BBRowCol) {
|
||||||
|
this.breadboard.highlightLoc(rowCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
public highlightWire(wire: Wire) {
|
||||||
|
//underboard wires
|
||||||
|
wire.wires.forEach(e => {
|
||||||
|
(<any>e).style["visibility"] = "visible";
|
||||||
|
});
|
||||||
|
|
||||||
|
//un greyed out
|
||||||
|
[wire.end1, wire.end2].forEach(e => {
|
||||||
|
svg.addClass(e, "highlight");
|
||||||
|
});
|
||||||
|
wire.wires.forEach(e => {
|
||||||
|
svg.addClass(e, "highlight");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public getView(): SVGElement {
|
public getView(): SVGElement {
|
||||||
return this.view;
|
return this.view;
|
||||||
}
|
}
|
||||||
@ -120,7 +148,7 @@ namespace pxsim.visuals {
|
|||||||
let builtinVisual = cmpDesc.visual as string;
|
let builtinVisual = cmpDesc.visual as string;
|
||||||
let cnstr = builtinComponentSimVisual[builtinVisual];
|
let cnstr = builtinComponentSimVisual[builtinVisual];
|
||||||
let stateFn = builtinComponentSimState[builtinVisual];
|
let stateFn = builtinComponentSimState[builtinVisual];
|
||||||
let cmp = cnstr();
|
cmp = cnstr();
|
||||||
cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
|
cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
|
||||||
this.components.push(cmp);
|
this.components.push(cmp);
|
||||||
this.view.appendChild(cmp.element);
|
this.view.appendChild(cmp.element);
|
||||||
|
@ -280,6 +280,10 @@ namespace pxsim.visuals {
|
|||||||
el: SVGRectElement,
|
el: SVGRectElement,
|
||||||
group?: string
|
group?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface BreadboardOpts {
|
||||||
|
wireframe?: boolean,
|
||||||
|
}
|
||||||
export class Breadboard {
|
export class Breadboard {
|
||||||
public bb: SVGSVGElement;
|
public bb: SVGSVGElement;
|
||||||
private styleEl: SVGStyleElement;
|
private styleEl: SVGStyleElement;
|
||||||
@ -293,8 +297,11 @@ namespace pxsim.visuals {
|
|||||||
private rowColToPin: Map<Map<GridPin>> = {};
|
private rowColToPin: Map<Map<GridPin>> = {};
|
||||||
private rowColToLbls: Map<Map<GridLabel[]>> = {};
|
private rowColToLbls: Map<Map<GridLabel[]>> = {};
|
||||||
|
|
||||||
constructor() {
|
constructor(opts: BreadboardOpts) {
|
||||||
this.buildDom();
|
this.buildDom();
|
||||||
|
|
||||||
|
if (opts.wireframe)
|
||||||
|
svg.addClass(this.bb, "sim-bb-outline");
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateLocation(x: number, y: number) {
|
public updateLocation(x: number, y: number) {
|
||||||
@ -627,7 +634,8 @@ namespace pxsim.visuals {
|
|||||||
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
|
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
|
||||||
}
|
}
|
||||||
|
|
||||||
public highlightLoc(row: string, col: string) {
|
public highlightLoc(rowCol: BBRowCol) {
|
||||||
|
let [row, col] = rowCol;
|
||||||
let pin = this.rowColToPin[row][col];
|
let pin = this.rowColToPin[row][col];
|
||||||
let {cx, cy} = pin;
|
let {cx, cy} = pin;
|
||||||
let lbls = this.rowColToLbls[row][col];
|
let lbls = this.rowColToLbls[row][col];
|
||||||
|
@ -232,7 +232,7 @@ namespace pxsim.visuals {
|
|||||||
this.componentDefs = props.componentDefinitions;
|
this.componentDefs = props.componentDefinitions;
|
||||||
|
|
||||||
// breadboard
|
// breadboard
|
||||||
this.breadboard = new Breadboard()
|
this.breadboard = new Breadboard({})
|
||||||
this.g.appendChild(this.breadboard.bb);
|
this.g.appendChild(this.breadboard.bb);
|
||||||
let bbSize = this.breadboard.getSVGAndSize();
|
let bbSize = this.breadboard.getSVGAndSize();
|
||||||
let [bbWidth, bbHeight] = [bbSize.w, bbSize.h];
|
let [bbWidth, bbHeight] = [bbSize.w, bbSize.h];
|
||||||
|
@ -67,14 +67,18 @@ namespace pxsim.visuals {
|
|||||||
colorClass?: string,
|
colorClass?: string,
|
||||||
bendFactor?: number,
|
bendFactor?: number,
|
||||||
}
|
}
|
||||||
export function mkWirePart(cp: [number, number], clr: string): visuals.SVGAndSize<SVGGElement> {
|
export function mkWirePart(cp: [number, number], clr: string, croc: boolean = false): visuals.SVGAndSize<SVGGElement> {
|
||||||
let g = <SVGGElement>svg.elt("g");
|
let g = <SVGGElement>svg.elt("g");
|
||||||
let [cx, cy] = cp;
|
let [cx, cy] = cp;
|
||||||
let offset = WIRE_PART_CURVE_OFF;
|
let offset = WIRE_PART_CURVE_OFF;
|
||||||
let p1: visuals.Coord = [cx - offset, cy - WIRE_PART_LENGTH / 2];
|
let p1: visuals.Coord = [cx - offset, cy - WIRE_PART_LENGTH / 2];
|
||||||
let p2: visuals.Coord = [cx + offset, cy + WIRE_PART_LENGTH / 2];
|
let p2: visuals.Coord = [cx + offset, cy + WIRE_PART_LENGTH / 2];
|
||||||
clr = visuals.mapWireColor(clr);
|
clr = visuals.mapWireColor(clr);
|
||||||
let e1 = mkOpenJumperEnd(p1, true, clr);
|
let e1: SVGElAndSize;
|
||||||
|
if (croc)
|
||||||
|
e1 = mkCrocEnd(p1, true, clr);
|
||||||
|
else
|
||||||
|
e1 = mkOpenJumperEnd(p1, true, clr);
|
||||||
let s = mkWirePartSeg(p1, p2, clr);
|
let s = mkWirePartSeg(p1, p2, clr);
|
||||||
let e2 = mkOpenJumperEnd(p2, false, clr);
|
let e2 = mkOpenJumperEnd(p2, false, clr);
|
||||||
g.appendChild(s.el);
|
g.appendChild(s.el);
|
||||||
|
Loading…
Reference in New Issue
Block a user