adds support for arduino zero

This commit is contained in:
darzu 2016-08-31 18:03:34 -07:00
parent ae17d4380e
commit 2b87b26f00
8 changed files with 173 additions and 335 deletions

View File

@ -1,4 +1,4 @@
# don't check in until OSS request is approved # don't check in until OSS request is approved
neopixel-black-60-vert.svg
sparkfun-* sparkfun-*
raspberrypi-* raspberrypi-*
arduino-*

View File

@ -79,9 +79,9 @@
"partsAspectRatio": 0.69, "partsAspectRatio": 0.69,
"builtinParts": { "builtinParts": {
"accelerometer": true, "accelerometer": true,
"buttonpair": true, "buttonpair": false,
"ledmatrix": true, "ledmatrix": false,
"speaker": true, "speaker": false,
"bluetooth": true, "bluetooth": true,
"thermometer": true, "thermometer": true,
"compass": true "compass": true

View File

@ -71,11 +71,7 @@ namespace pxsim {
initAsync(msg: SimulatorRunMessage): Promise<void> { initAsync(msg: SimulatorRunMessage): Promise<void> {
let options = (msg.options || {}) as RuntimeOptions; let options = (msg.options || {}) as RuntimeOptions;
//TODO: read from pxt.json/pxttarget.json let boardDef = CURRENT_BOARD; //TODO: read from pxt.json/pxttarget.json
let boardDef = MICROBIT_DEF;
// let boardDef = ARDUINO_ZERO;
// let boardDef = SPARKFUN_PHOTON;
// let boardDef = RASPBERRYPI_MODELB;
let cmpsList = msg.parts; let cmpsList = msg.parts;
let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json

View File

@ -7,10 +7,11 @@ namespace pxsim {
export interface PinBlockDefinition { export interface PinBlockDefinition {
x: number, x: number,
y: number, y: number,
labelPosition: "above" | "below";
labels: string[] labels: string[]
} }
export interface BoardImageDefinition { export interface BoardImageDefinition {
image?: string, image: string,
outlineImage?: string, outlineImage?: string,
width: number, width: number,
height: number, height: number,
@ -25,6 +26,8 @@ namespace pxsim {
threeVoltPins: string[], threeVoltPins: string[],
attachPowerOnRight?: boolean, attachPowerOnRight?: boolean,
onboardComponents?: string[] onboardComponents?: string[]
useCrocClips?: boolean,
marginWhenBreadboarding?: [number, number, number, number],
} }
export interface FactoryFunctionPinAlloc { export interface FactoryFunctionPinAlloc {
type: "factoryfunction", type: "factoryfunction",
@ -44,9 +47,8 @@ namespace pxsim {
image: string, image: string,
width: number, width: number,
height: number, height: number,
left: number,
top: number,
pinDist: number, pinDist: number,
firstPin: [number, number],
} }
export interface PartDefinition { export interface PartDefinition {
visual: string | PartVisualDefinition, visual: string | PartVisualDefinition,
@ -100,6 +102,8 @@ namespace pxsim {
threeVoltPins: ["+3v3"], threeVoltPins: ["+3v3"],
attachPowerOnRight: true, attachPowerOnRight: true,
onboardComponents: ["buttonpair", "ledmatrix"], onboardComponents: ["buttonpair", "ledmatrix"],
useCrocClips: true,
marginWhenBreadboarding: [0, 0, 80, 0],
} }
export const RASPBERRYPI_MODELB: BoardDefinition = { export const RASPBERRYPI_MODELB: BoardDefinition = {
visual: { visual: {
@ -109,8 +113,8 @@ namespace pxsim {
height: 230, height: 230,
pinDist: 9, pinDist: 9,
pinBlocks: [ pinBlocks: [
{ x: 5, y: 31, labels: ["3V3", "SDA", "SCL", "#4", "--", "#17", "#21", "#22", "--", "MOSI", "MISO", "SCLK", "--"]}, { x: 5, y: 31, labelPosition: "above", labels: ["3V3", "SDA", "SCL", "#4", "--", "#17", "#21", "#22", "--", "MOSI", "MISO", "SCLK", "--"]},
{ x: 5, y: 39, labels: ["5V", "--", "GND", "TXD", "RXD", "#18", "--", "#23", "#24", "--", "#25", "CS0", "CS1"]} { x: 5, y: 39, labelPosition: "below", labels: ["5V", "--", "GND", "TXD", "RXD", "#18", "--", "#23", "#24", "--", "#25", "CS0", "CS1"]}
], ],
}, },
gpioPinBlocks: [ gpioPinBlocks: [
@ -139,6 +143,7 @@ namespace pxsim {
}, },
groundPins: ["GND"], groundPins: ["GND"],
threeVoltPins: ["3V3"], threeVoltPins: ["3V3"],
marginWhenBreadboarding: [20, 0, 40, 0],
} }
export const SPARKFUN_PHOTON: BoardDefinition = { export const SPARKFUN_PHOTON: BoardDefinition = {
visual: { visual: {
@ -148,10 +153,10 @@ namespace pxsim {
height: 202.4, height: 202.4,
pinDist: 9.5, pinDist: 9.5,
pinBlocks: [ pinBlocks: [
{x: 72, y: 6, labels: ["~SCL/D1", "~SDA/D0", " ", "GND0", "SCK/A3", "~MISO/A4", "~MOSI/A5", "SS/A2", "~WKP", "DAC"]}, {x: 72, y: 6, labelPosition: "below", labels: ["~SCL/D1", "~SDA/D0", " ", "GND0", "SCK/A3", "~MISO/A4", "~MOSI/A5", "SS/A2", "~WKP", "DAC"]},
{x: 174, y: 6, labels: ["D7", "D6", "D5", "D4", "~D3", "~D2", "~TX", "~RX"]}, {x: 174, y: 6, labelPosition: "below", labels: ["D7", "D6", "D5", "D4", "~D3", "~D2", "~TX", "~RX"]},
{x: 107, y: 188, labels: [" ", " ", "RESET", "3.3V", "V-USB", "GND1", "GND2", "VIN"]}, {x: 107, y: 188, labelPosition: "above", labels: [" ", " ", "RESET", "3.3V", "V-USB", "GND1", "GND2", "VIN"]},
{x: 193, y: 188, labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, {x: 193, y: 188, labelPosition: "above", labels: ["A0", "A1", "A2", "A3", "A4", "A5"]},
], ],
}, },
gpioPinBlocks: [ gpioPinBlocks: [
@ -181,6 +186,7 @@ namespace pxsim {
}, },
groundPins: ["GND0", "GND1", "GND2"], groundPins: ["GND0", "GND1", "GND2"],
threeVoltPins: ["3.3V"], threeVoltPins: ["3.3V"],
marginWhenBreadboarding: [20, 0, 40, 0],
} }
export const ARDUINO_ZERO: BoardDefinition = { export const ARDUINO_ZERO: BoardDefinition = {
visual: { visual: {
@ -190,10 +196,10 @@ namespace pxsim {
height: 762, height: 762,
pinDist: 35.5, pinDist: 35.5,
pinBlocks: [ pinBlocks: [
{x: 276.8, y: 17.8, labels: ["SCL", "SDA", "AREF", "GND0", "~13", "~12", "~11", "~10", "~9", "~8"]}, {x: 276.8, y: 17.8, labelPosition: "below", labels: ["SCL", "SDA", "AREF", "GND0", "~13", "~12", "~11", "~10", "~9", "~8"]},
{x: 655.5, y: 17.8, labels: ["7", "~6", "~5", "~4", "~3", "2", "TX->1", "RX<-0"]}, {x: 655.5, y: 17.8, labelPosition: "below", labels: ["7", "~6", "~5", "~4", "~3", "2", "TX->1", "RX<-0"]},
{x: 411.7, y: 704.6, labels: ["ATN", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN"]}, {x: 411.7, y: 704.6, labelPosition: "above", labels: ["ATN", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN"]},
{x: 732.9, y: 704.6, labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, {x: 732.9, y: 704.6, labelPosition: "above", labels: ["A0", "A1", "A2", "A3", "A4", "A5"]},
], ],
}, },
gpioPinBlocks: [ gpioPinBlocks: [
@ -224,8 +230,9 @@ namespace pxsim {
}, },
groundPins: ["GND0", "GND1", "GND2"], groundPins: ["GND0", "GND1", "GND2"],
threeVoltPins: ["3.3V"], threeVoltPins: ["3.3V"],
marginWhenBreadboarding: [20, 0, 40, 0],
} }
export const PART_DEFINITIONS: Map<PartDefinition> = { export const PART_DEFINITIONS: Map<PartDefinition> = {
"ledmatrix": { "ledmatrix": {
visual: "ledmatrix", visual: "ledmatrix",
@ -287,8 +294,7 @@ namespace pxsim {
image: "/static/hardware/speaker.svg", image: "/static/hardware/speaker.svg",
width: 500, width: 500,
height: 500, height: 500,
left: -180, firstPin: [180, 135],
top: -135,
pinDist: 70, pinDist: 70,
}, },
breadboardColumnsNeeded: 5, breadboardColumnsNeeded: 5,
@ -299,8 +305,8 @@ namespace pxsim {
}, },
assemblyStep: 0, assemblyStep: 0,
wires: [ wires: [
{start: ["breadboard", "j", 1], end: ["GPIO", 0], color: "white", assemblyStep: 1}, {start: ["breadboard", "j", 1], end: ["GPIO", 0], color: "#ff80fa", assemblyStep: 1},
{start: ["breadboard", "j", 3], end: "ground", color: "white", assemblyStep: 1}, {start: ["breadboard", "j", 3], end: "ground", color: "blue", assemblyStep: 1},
], ],
}, },
} }
@ -327,4 +333,8 @@ namespace pxsim {
"ledmatrix": (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8), "ledmatrix": (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8),
"neopixel": (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy), "neopixel": (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy),
}; };
//TODO: add multiple board support
//export const CURRENT_BOARD = MICROBIT_DEF;
export const CURRENT_BOARD = ARDUINO_ZERO;
} }

View File

@ -128,10 +128,18 @@ namespace pxsim.instructions {
cmpHeight?: number, cmpHeight?: number,
cmpScale?: number cmpScale?: number
}; };
function mkBoardImgSvg(def: BoardImageDefinition): visuals.SVGElAndSize { function mkBoardImgSvg(def: string | BoardImageDefinition): visuals.SVGElAndSize {
return new visuals.MicrobitBoardSvg({ let boardView: visuals.BoardView;
theme: visuals.randomTheme() if (def === "microbit") {
}).getView(); boardView = new visuals.MicrobitBoardSvg({
theme: visuals.randomTheme()
})
} else {
boardView = new visuals.GenericBoardSvg({
visualDef: <BoardImageDefinition>def
})
}
return boardView.getView();
} }
function mkBBSvg(): visuals.SVGElAndSize { function mkBBSvg(): visuals.SVGElAndSize {
let bb = new visuals.Breadboard({}); let bb = new visuals.Breadboard({});
@ -432,7 +440,7 @@ namespace pxsim.instructions {
let panel = mkPanel(); let panel = mkPanel();
// board and breadboard // board and breadboard
let boardImg = mkBoardImgSvg(<BoardImageDefinition>props.boardDef.visual); let boardImg = mkBoardImgSvg(props.boardDef.visual);
let board = wrapSvg(boardImg, {left: QUANT_LBL(1), leftSize: QUANT_LBL_SIZE, cmpScale: PARTS_BOARD_SCALE}); let board = wrapSvg(boardImg, {left: QUANT_LBL(1), leftSize: QUANT_LBL_SIZE, cmpScale: PARTS_BOARD_SCALE});
panel.appendChild(board); panel.appendChild(board);
let bbRaw = mkBBSvg(); let bbRaw = mkBBSvg();
@ -633,7 +641,7 @@ ${tsPackage}
style.textContent += STYLE; style.textContent += STYLE;
const boardDef = MICROBIT_DEF; const boardDef = CURRENT_BOARD;
const cmpDefs = PART_DEFINITIONS; const cmpDefs = PART_DEFINITIONS;
//props //props

View File

@ -142,6 +142,28 @@ namespace pxsim.visuals {
}; };
} }
export function mkScaleFn(originUnit: number, targetUnit: number): (n: number) => number {
return (n: number) => n * (targetUnit / originUnit);
}
export interface MkImageOpts {
image: string,
width: number,
height: number,
imageUnitDist: number,
targetUnitDist: number
}
export function mkImageSVG(opts: MkImageOpts): SVGAndSize<SVGImageElement> {
let scaleFn = mkScaleFn(opts.imageUnitDist, opts.targetUnitDist);
let w = scaleFn(opts.width);
let h = scaleFn(opts.height);
let img = <SVGImageElement>svg.elt("image", {
width: w,
height: h,
"href": `${opts.image}`
});
return {el: img, w: w, h: h, x: 0, y: 0};
}
export type Coord = [number, number]; export type Coord = [number, number];
export function findDistSqrd(a: Coord, b: Coord): number { export function findDistSqrd(a: Coord, b: Coord): number {
let x = a[0] - b[0]; let x = a[0] - b[0];

View File

@ -21,32 +21,43 @@ namespace pxsim.visuals {
private style: SVGStyleElement; private style: SVGStyleElement;
private defs: SVGDefsElement; private defs: SVGDefsElement;
private state: DalBoard; private state: DalBoard;
private useCrocClips: boolean;
constructor(opts: BoardHostOpts) { constructor(opts: BoardHostOpts) {
this.state = opts.state; this.state = opts.state;
let onboardCmps = opts.boardDef.onboardComponents || []; let onboardCmps = opts.boardDef.onboardComponents || [];
let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0); let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
activeComponents.sort(); activeComponents.sort();
this.useCrocClips = opts.boardDef.useCrocClips;
this.boardView = new visuals.MicrobitBoardSvg({ if (opts.boardDef.visual === "microbit") {
runtime: runtime, this.boardView = new visuals.MicrobitBoardSvg({
theme: visuals.randomTheme(), runtime: runtime,
disableTilt: false, theme: visuals.randomTheme(),
wireframe: opts.wireframe, disableTilt: false,
}); wireframe: opts.wireframe,
});
} else {
let boardVis = opts.boardDef.visual as BoardImageDefinition;
this.boardView = new visuals.GenericBoardSvg({
visualDef: boardVis,
wireframe: opts.wireframe,
});
}
let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard; let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard;
if (useBreadboard) { if (useBreadboard) {
this.breadboard = new Breadboard({ this.breadboard = new Breadboard({
wireframe: opts.wireframe, wireframe: opts.wireframe,
}); });
let bMarg = opts.boardDef.marginWhenBreadboarding || [0, 0, 40, 0];
let composition = composeSVG({ let composition = composeSVG({
el1: this.boardView.getView(), el1: this.boardView.getView(),
scaleUnit1: this.boardView.getPinDist(), scaleUnit1: this.boardView.getPinDist(),
el2: this.breadboard.getSVGAndSize(), el2: this.breadboard.getSVGAndSize(),
scaleUnit2: this.breadboard.getPinDist(), scaleUnit2: this.breadboard.getPinDist(),
margin: [0, 0, 20, 0], margin: [bMarg[0], bMarg[1], 20, bMarg[3]],
middleMargin: 80, middleMargin: bMarg[2],
maxWidth: opts.maxWidth, maxWidth: opts.maxWidth,
maxHeight: opts.maxHeight, maxHeight: opts.maxHeight,
}); });
@ -166,7 +177,7 @@ namespace pxsim.visuals {
return cmp; return cmp;
} }
public addWire(inst: WireInst): Wire { public addWire(inst: WireInst): Wire {
return this.wireFactory.addWire(inst.start, inst.end, inst.color, true); return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
} }
public addAll(basicWiresAndCmpsAndWires: AllocatorResult) { public addAll(basicWiresAndCmpsAndWires: AllocatorResult) {
let {powerWires, components} = basicWiresAndCmpsAndWires; let {powerWires, components} = basicWiresAndCmpsAndWires;

View File

@ -3,46 +3,6 @@
/// <reference path="../../libs/microbit/dal.d.ts"/> /// <reference path="../../libs/microbit/dal.d.ts"/>
namespace pxsim.visuals { namespace pxsim.visuals {
const svg = pxsim.svg;
export interface IBoardSvgProps {
runtime: pxsim.Runtime;
boardDef: BoardDefinition;
disableTilt?: boolean;
activeComponents: string[];
fnArgs?: any;
componentDefinitions: Map<PartDefinition>;
}
export const VIEW_WIDTH = 498;
export const VIEW_HEIGHT = 725;
const TOP_MARGIN = 20;
const MID_MARGIN = 40;
const BOT_MARGIN = 20;
const PIN_LBL_SIZE = PIN_DIST * 0.7;
const PIN_LBL_HOVER_SIZE = PIN_LBL_SIZE * 1.5;
const SQUARE_PIN_WIDTH = PIN_DIST * 0.66666;
const SQUARE_PIN_HOVER_WIDTH = PIN_DIST * 0.66666 + PIN_DIST / 3.0;
export type ComputedBoardDimensions = {
scaleFn: (n: number) => number,
height: number,
width: number,
xOff: number,
yOff: number
};
export function getBoardDimensions(vis: BoardImageDefinition): ComputedBoardDimensions {
let scaleFn = (n: number) => n * (PIN_DIST / vis.pinDist);
let width = scaleFn(vis.width);
return {
scaleFn: scaleFn,
height: scaleFn(vis.height),
width: width,
xOff: (VIEW_WIDTH - width) / 2.0,
yOff: TOP_MARGIN
}
}
export const BOARD_SYTLE = ` export const BOARD_SYTLE = `
.noselect { .noselect {
-webkit-touch-callout: none; /* iOS Safari */ -webkit-touch-callout: none; /* iOS Safari */
@ -53,52 +13,6 @@ namespace pxsim.visuals {
user-select: none; /* Non-prefixed version, currently user-select: none; /* Non-prefixed version, currently
not supported by any browser */ not supported by any browser */
} }
svg.sim.grayscale {
-moz-filter: grayscale(1);
-webkit-filter: grayscale(1);
filter: grayscale(1);
}
.sim-text {
font-family:"Lucida Console", Monaco, monospace;
font-size:25px;
fill:#fff;
pointer-events: none;
}
/* 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; }
}
.sim-board-pin { .sim-board-pin {
fill:#999; fill:#999;
@ -178,202 +92,72 @@ namespace pxsim.visuals {
stroke:#000; stroke:#000;
} }
`; `;
const PIN_LBL_SIZE = PIN_DIST * 0.7;
const PIN_LBL_HOVER_SIZE = PIN_LBL_SIZE * 1.5;
const SQUARE_PIN_WIDTH = PIN_DIST * 0.66666;
const SQUARE_PIN_HOVER_WIDTH = PIN_DIST * 0.66666 + PIN_DIST / 3.0;
export interface GenericBoardProps {
visualDef: BoardImageDefinition;
wireframe?: boolean;
}
let nextBoardId = 0; let nextBoardId = 0;
export class GenericBoardSvg /*TODO: implements BoardView*/ { export class GenericBoardSvg implements BoardView {
public hostElement: SVGSVGElement; private element: SVGSVGElement;
private style: SVGStyleElement; private style: SVGStyleElement;
private defs: SVGDefsElement; private defs: SVGDefsElement;
private g: SVGGElement; private g: SVGGElement;
public board: pxsim.DalBoard; private background: SVGElement;
public background: SVGElement; private width: number;
private components: IBoardComponent<any>[]; private height: number;
public breadboard: Breadboard;
private underboard: SVGGElement;
public boardDef: BoardDefinition;
private boardDim: ComputedBoardDimensions;
public componentDefs: Map<PartDefinition>;
private boardEdges: number[];
private id: number; private id: number;
public bbX: number;
public bbY: number; // pins & labels
private boardTopEdge: number; //(truth)
private boardBotEdge: number;
private wireFactory: WireFactory;
//truth
private allPins: GridPin[] = []; private allPins: GridPin[] = [];
private allLabels: GridLabel[] = []; private allLabels: GridLabel[] = [];
//cache //(cache)
private pinNmToLbl: Map<GridLabel> = {}; private pinNmToLbl: Map<GridLabel> = {};
private pinNmToPin: Map<GridPin> = {}; private pinNmToPin: Map<GridPin> = {};
constructor(public props: IBoardSvgProps) { constructor(public props: GenericBoardProps) {
//TODO: handle wireframe mode
this.id = nextBoardId++; this.id = nextBoardId++;
this.boardDef = props.boardDef; let visDef = props.visualDef;
this.boardDim = getBoardDimensions(<BoardImageDefinition>this.boardDef.visual); let imgHref = props.wireframe ? visDef.outlineImage : visDef.image;
this.board = this.props.runtime.board as pxsim.DalBoard; let boardImgAndSize = mkImageSVG({
this.board.updateView = () => this.updateState(); image: imgHref,
this.hostElement = <SVGSVGElement>svg.elt("svg") width: visDef.width,
svg.hydrate(this.hostElement, { height: visDef.height,
imageUnitDist: visDef.pinDist,
targetUnitDist: PIN_DIST
});
let scaleFn = mkScaleFn(visDef.pinDist, PIN_DIST);
this.width = boardImgAndSize.w;
this.height = boardImgAndSize.h;
let img = boardImgAndSize.el;
this.element = <SVGSVGElement>svg.elt("svg");
svg.hydrate(this.element, {
"version": "1.0", "version": "1.0",
"viewBox": `0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`, "viewBox": `0 0 ${this.width} ${this.height}`,
"enable-background": `new 0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`,
"class": `sim sim-board-id-${this.id}`, "class": `sim sim-board-id-${this.id}`,
"x": "0px", "x": "0px",
"y": "0px" "y": "0px"
}); });
this.style = <SVGStyleElement>svg.child(this.hostElement, "style", {}); if (props.wireframe)
svg.addClass(this.element, "sim-board-outline")
this.style = <SVGStyleElement>svg.child(this.element, "style", {});
this.style.textContent += BOARD_SYTLE; this.style.textContent += BOARD_SYTLE;
this.defs = <SVGDefsElement>svg.child(this.hostElement, "defs", {}); this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
this.g = <SVGGElement>svg.elt("g"); this.g = <SVGGElement>svg.elt("g");
this.hostElement.appendChild(this.g); this.element.appendChild(this.g);
this.underboard = <SVGGElement>svg.child(this.g, "g", {class: "sim-underboard"});
this.components = [];
this.componentDefs = props.componentDefinitions;
// breadboard
this.breadboard = new Breadboard({})
this.g.appendChild(this.breadboard.bb);
let bbSize = this.breadboard.getSVGAndSize();
let [bbWidth, bbHeight] = [bbSize.w, bbSize.h];
const bbX = (VIEW_WIDTH - bbWidth) / 2;
this.bbX = bbX;
const bbY = TOP_MARGIN + this.boardDim.height + MID_MARGIN;
this.bbY = bbY;
this.breadboard.updateLocation(bbX, bbY);
// edges
this.boardTopEdge = TOP_MARGIN;
this.boardBotEdge = TOP_MARGIN + this.boardDim.height;
this.boardEdges = [this.boardTopEdge, this.boardBotEdge, bbY, bbY + bbHeight]
this.wireFactory = new WireFactory(this.underboard, this.g, this.boardEdges, this.style, this.getLocCoord.bind(this));
this.buildDom();
this.updateTheme();
this.updateState();
let cmps = props.activeComponents;
if (cmps.length) {
let allocRes = allocateDefinitions({
boardDef: this.boardDef,
cmpDefs: this.componentDefs,
fnArgs: this.props.fnArgs,
getBBCoord: this.getBBCoord.bind(this),
cmpList: props.activeComponents,
});
this.addAll(allocRes);
}
}
private getBoardPinCoord(pinNm: string): Coord {
let pin = this.pinNmToPin[pinNm];
if (!pin)
return null;
return [pin.cx, pin.cy];
}
private getBBCoord(rowCol: BBRowCol): Coord {
let bbCoord = this.breadboard.getCoord(rowCol);
if (!bbCoord)
return null;
let [x, y] = bbCoord;
return [x + this.bbX, y + this.bbY];
}
public getLocCoord(loc: Loc): Coord {
let coord: Coord;
if (loc.type === "breadboard") {
let rowCol = (<BBLoc>loc).rowCol;
coord = this.getBBCoord(rowCol);
} else {
let pinNm = (<BoardLoc>loc).pin;
coord = this.getBoardPinCoord(pinNm);
}
if (!coord) {
console.error("Unknown location: " + name)
return [0, 0];
}
return coord;
}
private mkGrayCover(x: number, y: number, w: number, h: number) {
let rect = <SVGRectElement>svg.elt("rect");
svg.hydrate(rect, {x: x, y: y, width: w, height: h, class: "gray-cover"});
return rect;
}
private getCmpClass = (type: string) => `sim-${type}-cmp`;
public addWire(inst: WireInst): Wire {
return this.wireFactory.addWire(inst.start, inst.end, inst.color);
}
public addAll(basicWiresAndCmpsAndWires: AllocatorResult) {
let {powerWires, components} = basicWiresAndCmpsAndWires;
powerWires.forEach(w => this.addWire(w));
components.forEach((cAndWs, idx) => {
let {component, wires} = cAndWs;
wires.forEach(w => this.addWire(w));
this.addComponent(component);
});
}
public addComponent(cmpDesc: CmpInst): IBoardComponent<any> {
let cmp: IBoardComponent<any> = null;
if (typeof cmpDesc.visual === "string") {
let builtinVisual = cmpDesc.visual as string;
let cnstr = builtinComponentSimVisual[builtinVisual];
let stateFn = builtinComponentSimState[builtinVisual];
let state = stateFn(this.board);
cmp = cnstr();
cmp.init(this.board.bus, state, this.hostElement, cmpDesc.microbitPins, cmpDesc.otherArgs);
this.components.push(cmp);
this.g.appendChild(cmp.element);
if (cmp.defs)
cmp.defs.forEach(d => this.defs.appendChild(d));
this.style.textContent += cmp.style || "";
let rowCol = <BBRowCol>[`${cmpDesc.breadboardStartRow}`, `${cmpDesc.breadboardStartColumn}`];
let coord = this.getBBCoord(rowCol);
cmp.moveToCoord(coord);
let cls = this.getCmpClass(name);
svg.addClass(cmp.element, cls);
svg.addClass(cmp.element, "sim-cmp");
cmp.updateTheme();
cmp.updateState();
} else {
//TODO: adding generic components
}
return cmp;
}
private updateTheme() {
this.components.forEach(c => c.updateTheme());
}
public updateState() {
let state = this.board;
if (!state) return;
this.components.forEach(c => c.updateState());
if (!runtime || runtime.dead) svg.addClass(this.hostElement, "grayscale");
else svg.removeClass(this.hostElement, "grayscale");
}
private buildDom() {
// filters
let glow = svg.child(this.defs, "filter", { id: "filterglow", x: "-5%", y: "-5%", width: "120%", height: "120%" });
svg.child(glow, "feGaussianBlur", { stdDeviation: "5", result: "glow" });
let merge = svg.child(glow, "feMerge", {});
for (let i = 0; i < 3; ++i)
svg.child(merge, "feMergeNode", { in: "glow" })
// main board // main board
this.background = svg.child(this.g, "image", this.g.appendChild(img);
{ class: "sim-board", x: this.boardDim.xOff, y: this.boardDim.yOff, width: this.boardDim.width, height: this.boardDim.height, this.background = img;
"href": `${(<BoardImageDefinition>this.boardDef.visual).image}`}); svg.hydrate(img, { class: "sim-board" });
let backgroundCover = this.mkGrayCover(this.boardDim.xOff, this.boardDim.yOff, this.boardDim.width, this.boardDim.height); let backgroundCover = this.mkGrayCover(0, 0, this.width, this.height);
this.g.appendChild(backgroundCover); this.g.appendChild(backgroundCover);
// ----- pins // ----- pins
@ -398,8 +182,8 @@ namespace pxsim.visuals {
return {el: el, w: width, h: width, x: 0, y: 0}; return {el: el, w: width, h: width, x: 0, y: 0};
} }
const mkPinBlockGrid = (pinBlock: PinBlockDefinition, blockIdx: number) => { const mkPinBlockGrid = (pinBlock: PinBlockDefinition, blockIdx: number) => {
let xOffset = this.boardDim.xOff + this.boardDim.scaleFn(pinBlock.x) + PIN_DIST / 2.0; let xOffset = scaleFn(pinBlock.x) + PIN_DIST / 2.0;
let yOffset = this.boardDim.yOff + this.boardDim.scaleFn(pinBlock.y) + PIN_DIST / 2.0; let yOffset = scaleFn(pinBlock.y) + PIN_DIST / 2.0;
let rowCount = 1; let rowCount = 1;
let colCount = pinBlock.labels.length; let colCount = pinBlock.labels.length;
let getColName = (colIdx: number) => pinBlock.labels[colIdx]; let getColName = (colIdx: number) => pinBlock.labels[colIdx];
@ -422,9 +206,11 @@ namespace pxsim.visuals {
svg.addClass(gridRes.g, "sim-board-pin-group"); svg.addClass(gridRes.g, "sim-board-pin-group");
return gridRes; return gridRes;
}; };
let pinBlocks = (<BoardImageDefinition>this.boardDef.visual).pinBlocks.map(mkPinBlockGrid); let pinBlocks = visDef.pinBlocks.map(mkPinBlockGrid);
pinBlocks.forEach(blk => blk.allPins.forEach(p => { let pinToBlockDef: PinBlockDefinition[] = [];
pinBlocks.forEach((blk, blkIdx) => blk.allPins.forEach((p, pIdx) => {
this.allPins.push(p); this.allPins.push(p);
pinToBlockDef.push(visDef.pinBlocks[blkIdx]);
})); }));
//tooltip //tooltip
this.allPins.forEach(p => { this.allPins.forEach(p => {
@ -443,15 +229,12 @@ namespace pxsim.visuals {
}); });
// ----- labels // ----- labels
const mkLabelTxtEl = (pinX: number, pinY: number, size: number, txt: string): SVGTextElement => { const mkLabelTxtEl = (pinX: number, pinY: number, size: number, txt: string, pos: "above" | "below"): SVGTextElement => {
//TODO: extract constants //TODO: extract constants
let lblY: number; let lblY: number;
let lblX: number; let lblX: number;
let edges = [this.boardTopEdge, this.boardBotEdge];
let distFromTopBot = edges.map(e => Math.abs(e - pinY)); if (pos === "below") {
let closestEdgeIdx = distFromTopBot.reduce((pi, n, ni) => n < distFromTopBot[pi] ? ni : pi, 0);
let topEdge = closestEdgeIdx == 0;
if (topEdge) {
let lblLen = size * 0.25 * txt.length; let lblLen = size * 0.25 * txt.length;
lblX = pinX; lblX = pinX;
lblY = pinY + 12 + lblLen; lblY = pinY + 12 + lblLen;
@ -463,16 +246,17 @@ namespace pxsim.visuals {
let el = mkTxt(lblX, lblY, size, -90, txt); let el = mkTxt(lblX, lblY, size, -90, txt);
return el; return el;
}; };
const mkLabel = (pinX: number, pinY: number, txt: string): GridLabel => { const mkLabel = (pinX: number, pinY: number, txt: string, pos: "above" | "below"): GridLabel => {
let el = mkLabelTxtEl(pinX, pinY, PIN_LBL_SIZE, txt); let el = mkLabelTxtEl(pinX, pinY, PIN_LBL_SIZE, txt, pos);
svg.addClass(el, "sim-board-pin-lbl"); svg.addClass(el, "sim-board-pin-lbl");
let hoverEl = mkLabelTxtEl(pinX, pinY, PIN_LBL_HOVER_SIZE, txt); let hoverEl = mkLabelTxtEl(pinX, pinY, PIN_LBL_HOVER_SIZE, txt, pos);
svg.addClass(hoverEl, "sim-board-pin-lbl-hover"); svg.addClass(hoverEl, "sim-board-pin-lbl-hover");
let label: GridLabel = {el: el, hoverEl: hoverEl, txt: txt}; let label: GridLabel = {el: el, hoverEl: hoverEl, txt: txt};
return label; return label;
} }
this.allLabels = this.allPins.map(p => { this.allLabels = this.allPins.map((p, pIdx) => {
return mkLabel(p.cx, p.cy, p.col); let blk = pinToBlockDef[pIdx];
return mkLabel(p.cx, p.cy, p.col, blk.labelPosition);
}); });
//attach labels //attach labels
this.allLabels.forEach(l => { this.allLabels.forEach(l => {
@ -486,7 +270,29 @@ namespace pxsim.visuals {
}); });
} }
public highlightLoc(pinNm: string) { public getCoord(pinNm: string): Coord {
let pin = this.pinNmToPin[pinNm];
if (!pin)
return null;
return [pin.cx, pin.cy];
}
private mkGrayCover(x: number, y: number, w: number, h: number) {
let rect = <SVGRectElement>svg.elt("rect");
svg.hydrate(rect, {x: x, y: y, width: w, height: h, class: "gray-cover"});
return rect;
}
public getView(): SVGAndSize<SVGSVGElement> {
return {el: this.element, w: this.width, h: this.height, x: 0, y: 0};
}
public getPinDist() {
return PIN_DIST;
}
public highlightPin(pinNm: string) {
let lbl = this.pinNmToLbl[pinNm]; let lbl = this.pinNmToLbl[pinNm];
let pin = this.pinNmToPin[pinNm]; let pin = this.pinNmToPin[pinNm];
if (lbl && pin) { if (lbl && pin) {
@ -496,20 +302,5 @@ namespace pxsim.visuals {
svg.addClass(pin.hoverEl, "highlight"); svg.addClass(pin.hoverEl, "highlight");
} }
} }
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");
});
}
} }
} }