Merge branch 'breadboarding' of https://github.com/Microsoft/pxt-microbit into breadboarding
This commit is contained in:
		@@ -81,7 +81,13 @@ namespace pxsim {
 | 
			
		||||
            let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json
 | 
			
		||||
            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.appendChild(viewHost.getView());
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,6 @@ namespace pxsim.instructions {
 | 
			
		||||
    const NUM_MARGIN = 5;
 | 
			
		||||
    const FRONT_PAGE_BOARD_WIDTH = 200;
 | 
			
		||||
    const STYLE = `
 | 
			
		||||
            ${visuals.BOARD_SYTLE}
 | 
			
		||||
            .instr-panel {
 | 
			
		||||
                margin: ${PANEL_MARGIN}px;
 | 
			
		||||
                padding: ${PANEL_PADDING}px;
 | 
			
		||||
@@ -126,22 +125,12 @@ namespace pxsim.instructions {
 | 
			
		||||
        cmpScale?: number
 | 
			
		||||
    };
 | 
			
		||||
    function mkBoardImgSvg(def: BoardImageDefinition): visuals.SVGElAndSize {
 | 
			
		||||
        let img = svg.elt( "image");
 | 
			
		||||
        let [l, t] = [0, 0];
 | 
			
		||||
        let w = def.width;
 | 
			
		||||
        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};
 | 
			
		||||
        return new visuals.MicrobitBoardSvg({
 | 
			
		||||
            theme: visuals.randomTheme()
 | 
			
		||||
        }).getView();
 | 
			
		||||
    }
 | 
			
		||||
    function mkBBSvg(): visuals.SVGElAndSize {
 | 
			
		||||
        let bb = new visuals.Breadboard();
 | 
			
		||||
        let bb = new visuals.Breadboard({});
 | 
			
		||||
        return bb.getSVGAndSize();
 | 
			
		||||
    }
 | 
			
		||||
    function wrapSvg(el: visuals.SVGElAndSize, opts: mkCmpDivOpts): HTMLElement {
 | 
			
		||||
@@ -280,7 +269,8 @@ namespace pxsim.instructions {
 | 
			
		||||
    function mkCmpDiv(type: "wire" | string, opts: mkCmpDivOpts): HTMLElement {
 | 
			
		||||
        let el: visuals.SVGElAndSize;
 | 
			
		||||
        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 {
 | 
			
		||||
            let cnstr = builtinComponentPartVisual[type];
 | 
			
		||||
            el = cnstr([0, 0]);
 | 
			
		||||
@@ -290,6 +280,7 @@ namespace pxsim.instructions {
 | 
			
		||||
    type BoardProps = {
 | 
			
		||||
        boardDef: BoardDefinition,
 | 
			
		||||
        cmpDefs: Map<PartDefinition>,
 | 
			
		||||
        fnArgs: any,
 | 
			
		||||
        allAlloc: AllocatorResult,
 | 
			
		||||
        stepToWires: WireInst[][],
 | 
			
		||||
        stepToCmps: CmpInst[][]
 | 
			
		||||
@@ -338,6 +329,7 @@ namespace pxsim.instructions {
 | 
			
		||||
        return {
 | 
			
		||||
            boardDef: allocOpts.boardDef,
 | 
			
		||||
            cmpDefs: allocOpts.cmpDefs,
 | 
			
		||||
            fnArgs: allocOpts.fnArgs,
 | 
			
		||||
            allAlloc: allocRes,
 | 
			
		||||
            stepToWires: stepToWires,
 | 
			
		||||
            stepToCmps: stepToCmps,
 | 
			
		||||
@@ -348,28 +340,19 @@ namespace pxsim.instructions {
 | 
			
		||||
            allWireColors: allWireColors,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    function mkBoard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, width: number, buildMode: boolean = false): visuals.GenericBoardSvg {
 | 
			
		||||
        let board = new visuals.GenericBoardSvg({
 | 
			
		||||
            runtime: pxsim.runtime,
 | 
			
		||||
            boardDef: boardDef,
 | 
			
		||||
            activeComponents: [],
 | 
			
		||||
            componentDefinitions: cmpDefs,
 | 
			
		||||
        })
 | 
			
		||||
        svg.hydrate(board.hostElement, {
 | 
			
		||||
            "width": width,
 | 
			
		||||
    function mkBlankBoardAndBreadboard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, fnArgs: any, width: number, buildMode: boolean = false): visuals.BoardHost {
 | 
			
		||||
        let state = runtime.board as pxsim.DalBoard;
 | 
			
		||||
        let boardHost = new visuals.BoardHost({
 | 
			
		||||
            state: state, 
 | 
			
		||||
            boardDef: boardDef, 
 | 
			
		||||
            forceBreadboard: true,
 | 
			
		||||
            cmpDefs: cmpDefs, 
 | 
			
		||||
            maxWidth: `${width}px`,
 | 
			
		||||
            fnArgs: fnArgs,
 | 
			
		||||
            wireframe: buildMode,
 | 
			
		||||
        });
 | 
			
		||||
        svg.addClass(board.hostElement, "board-svg");
 | 
			
		||||
        if (buildMode) {
 | 
			
		||||
            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();
 | 
			
		||||
        let view = boardHost.getView();
 | 
			
		||||
        svg.addClass(view, "board-svg");
 | 
			
		||||
 | 
			
		||||
        //set smiley
 | 
			
		||||
        //HACK
 | 
			
		||||
@@ -383,11 +366,12 @@ namespace pxsim.instructions {
 | 
			
		||||
        // img.set(4, 2, 255);
 | 
			
		||||
        // 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) {
 | 
			
		||||
            svg.addClass(board.hostElement, "grayed");
 | 
			
		||||
            svg.addClass(view, "grayed");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i <= step; i++) {
 | 
			
		||||
@@ -399,16 +383,15 @@ namespace pxsim.instructions {
 | 
			
		||||
                    if (i === step) {
 | 
			
		||||
                        //location highlights
 | 
			
		||||
                        if (w.start.type == "breadboard") {
 | 
			
		||||
                            let [row, col] = (<BBLoc>w.start).rowCol;
 | 
			
		||||
                            let lbls = board.breadboard.highlightLoc(row, col);
 | 
			
		||||
                            let lbls = board.highlightBreadboardPin((<BBLoc>w.start).rowCol);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            board.highlightLoc((<BoardLoc>w.start).pin);
 | 
			
		||||
                            board.highlightBoardPin((<BoardLoc>w.start).pin);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (w.end.type == "breadboard") {
 | 
			
		||||
                            let [row, col] = (<BBLoc>w.end).rowCol;
 | 
			
		||||
                            let lbls = board.breadboard.highlightLoc(row, col);
 | 
			
		||||
                            let lbls = board.highlightBreadboardPin((<BBLoc>w.end).rowCol);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            board.highlightLoc((<BoardLoc>w.end).pin);
 | 
			
		||||
                            board.highlightBoardPin((<BoardLoc>w.end).pin);
 | 
			
		||||
                        }
 | 
			
		||||
                        //highlight wire
 | 
			
		||||
                        board.highlightWire(wire);
 | 
			
		||||
@@ -419,14 +402,14 @@ namespace pxsim.instructions {
 | 
			
		||||
            if (cmps) {
 | 
			
		||||
                cmps.forEach(cmpInst => {
 | 
			
		||||
                    let cmp = board.addComponent(cmpInst)
 | 
			
		||||
                    let [row, col]: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn}`];
 | 
			
		||||
                    let rowCol: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn}`];
 | 
			
		||||
                    //last step
 | 
			
		||||
                    if (i === step) {
 | 
			
		||||
                        board.breadboard.highlightLoc(row, col);
 | 
			
		||||
                        board.highlightBreadboardPin(rowCol);
 | 
			
		||||
                        if (cmpInst.visual === "buttonpair") {
 | 
			
		||||
                            //TODO: don't specialize this
 | 
			
		||||
                            let [row2, col2]: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn + 3}`];
 | 
			
		||||
                            board.breadboard.highlightLoc(row2, col2);
 | 
			
		||||
                            let rowCol2: BBRowCol = [`${cmpInst.breadboardStartRow}`, `${cmpInst.breadboardStartColumn + 3}`];
 | 
			
		||||
                            board.highlightBreadboardPin(rowCol2);
 | 
			
		||||
                        }
 | 
			
		||||
                        svg.addClass(cmp.element, "notgrayed");
 | 
			
		||||
                    }
 | 
			
		||||
@@ -498,9 +481,9 @@ namespace pxsim.instructions {
 | 
			
		||||
        let panel = mkPanel();
 | 
			
		||||
 | 
			
		||||
        //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);
 | 
			
		||||
        panel.appendChild(board.hostElement);
 | 
			
		||||
        panel.appendChild(board.getView());
 | 
			
		||||
 | 
			
		||||
        //number
 | 
			
		||||
        let numDiv = document.createElement("div");
 | 
			
		||||
@@ -568,9 +551,9 @@ namespace pxsim.instructions {
 | 
			
		||||
    function updateFrontPanel(props: BoardProps): [HTMLElement, BoardProps] {
 | 
			
		||||
        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);
 | 
			
		||||
        panel.appendChild(board.hostElement);
 | 
			
		||||
        panel.appendChild(board.getView());
 | 
			
		||||
 | 
			
		||||
        return [panel, props];
 | 
			
		||||
    }
 | 
			
		||||
@@ -579,9 +562,9 @@ namespace pxsim.instructions {
 | 
			
		||||
 | 
			
		||||
        let panel = mkPanel();
 | 
			
		||||
        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);
 | 
			
		||||
        panel.appendChild(board.hostElement);
 | 
			
		||||
        panel.appendChild(board.getView());
 | 
			
		||||
 | 
			
		||||
        return panel;
 | 
			
		||||
    }
 | 
			
		||||
@@ -646,11 +629,14 @@ namespace pxsim.instructions {
 | 
			
		||||
        const cmpDefs = PART_DEFINITIONS;
 | 
			
		||||
 | 
			
		||||
        //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({
 | 
			
		||||
            boardDef: boardDef,
 | 
			
		||||
            cmpDefs: cmpDefs,
 | 
			
		||||
            cmpList: parts,
 | 
			
		||||
            cmpList: activeComponents,
 | 
			
		||||
            fnArgs: fnArgs,
 | 
			
		||||
            getBBCoord: dummyBreadboard.getCoord.bind(dummyBreadboard)
 | 
			
		||||
        });
 | 
			
		||||
@@ -669,7 +655,7 @@ namespace pxsim.instructions {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //final
 | 
			
		||||
        let finalPanel = mkFinalPanel(props);
 | 
			
		||||
        document.body.appendChild(finalPanel);
 | 
			
		||||
        // let finalPanel = mkFinalPanel(props);
 | 
			
		||||
        // document.body.appendChild(finalPanel);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										254
									
								
								sim/microbit.ts
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								sim/microbit.ts
									
									
									
									
									
								
							@@ -1,4 +1,121 @@
 | 
			
		||||
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 pins4onMids = pins4onXs.map(x => x + 5);
 | 
			
		||||
    const littlePinDist = pins4onMids[1] - pins4onMids[0];
 | 
			
		||||
@@ -83,9 +200,10 @@ namespace pxsim.visuals {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export interface IBoardProps {
 | 
			
		||||
        runtime: pxsim.Runtime;
 | 
			
		||||
        runtime?: pxsim.Runtime;
 | 
			
		||||
        theme?: IBoardTheme;
 | 
			
		||||
        disableTilt?: boolean;
 | 
			
		||||
        wireframe?: boolean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const pointerEvents = !!(window as any).PointerEvent ? {
 | 
			
		||||
@@ -132,15 +250,20 @@ namespace pxsim.visuals {
 | 
			
		||||
        private pinNmToCoord: Map<Coord> = {};
 | 
			
		||||
 | 
			
		||||
        constructor(public props: IBoardProps) {
 | 
			
		||||
            this.board = this.props.runtime.board as pxsim.DalBoard;
 | 
			
		||||
            this.board.updateSubscribers.push(() => this.updateState());
 | 
			
		||||
 | 
			
		||||
            this.recordPinCoords();
 | 
			
		||||
            this.buildDom();
 | 
			
		||||
            if (props && props.wireframe)
 | 
			
		||||
                svg.addClass(this.element, "sim-wireframe");
 | 
			
		||||
 | 
			
		||||
            this.updateTheme();
 | 
			
		||||
            this.updateState();
 | 
			
		||||
            this.attachEvents();
 | 
			
		||||
            if (props && props.theme)
 | 
			
		||||
                this.updateTheme();
 | 
			
		||||
 | 
			
		||||
            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> {
 | 
			
		||||
@@ -157,6 +280,10 @@ namespace pxsim.visuals {
 | 
			
		||||
            return this.pinNmToCoord[pinNm];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public highlightPin(pinNm: string): void {
 | 
			
		||||
            //TODO: for instructions
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public getPinDist(): number {
 | 
			
		||||
            return littlePinDist * 1.7;
 | 
			
		||||
        }
 | 
			
		||||
@@ -438,118 +565,7 @@ namespace pxsim.visuals {
 | 
			
		||||
                "height": MB_HEIGHT + "px",
 | 
			
		||||
            });
 | 
			
		||||
            this.style = <SVGStyleElement>svg.child(this.element, "style", {});
 | 
			
		||||
            this.style.textContent = `
 | 
			
		||||
                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.style.textContent = MB_STYLE;
 | 
			
		||||
 | 
			
		||||
            this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
 | 
			
		||||
            this.g = <SVGGElement>svg.elt("g");
 | 
			
		||||
 
 | 
			
		||||
@@ -209,5 +209,6 @@ namespace pxsim.visuals {
 | 
			
		||||
        getView(): SVGAndSize<SVGSVGElement>;
 | 
			
		||||
        getCoord(pinNm: string): Coord;
 | 
			
		||||
        getPinDist(): number;
 | 
			
		||||
        highlightPin(pinNm: string): void;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,15 @@
 | 
			
		||||
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 {
 | 
			
		||||
        private components: IBoardComponent<any>[] = [];
 | 
			
		||||
        private wireFactory: WireFactory;
 | 
			
		||||
@@ -11,33 +22,27 @@ namespace pxsim.visuals {
 | 
			
		||||
        private defs: SVGDefsElement;
 | 
			
		||||
        private state: DalBoard;
 | 
			
		||||
 | 
			
		||||
        constructor(state: DalBoard, boardDef: BoardDefinition, cmpsList: string[], cmpDefs: Map<PartDefinition>, fnArgs: any) {
 | 
			
		||||
            this.state = state;
 | 
			
		||||
            let onboardCmps = boardDef.onboardComponents || [];
 | 
			
		||||
            let activeComponents = (cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
 | 
			
		||||
        constructor(opts: BoardHostOpts) {
 | 
			
		||||
            this.state = opts.state;
 | 
			
		||||
            let onboardCmps = opts.boardDef.onboardComponents || [];
 | 
			
		||||
            let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
 | 
			
		||||
            activeComponents.sort();
 | 
			
		||||
 | 
			
		||||
            // boardDef.visual === "microbit"
 | 
			
		||||
            this.boardView = new visuals.MicrobitBoardSvg({
 | 
			
		||||
                runtime: runtime,
 | 
			
		||||
                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%";
 | 
			
		||||
            const VIEW_HEIGHT = "100%";
 | 
			
		||||
            let maxWidth = opts.maxWidth || "100%";
 | 
			
		||||
            let maxHeight = opts.maxHeight || "100%";
 | 
			
		||||
 | 
			
		||||
            if (0 < activeComponents.length) {
 | 
			
		||||
                this.breadboard = new Breadboard();
 | 
			
		||||
            let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard;
 | 
			
		||||
            if (useBreadboard) {
 | 
			
		||||
                this.breadboard = new Breadboard({
 | 
			
		||||
                    wireframe: opts.wireframe,
 | 
			
		||||
                });
 | 
			
		||||
                let composition = composeSVG({
 | 
			
		||||
                    el1: this.boardView.getView(),
 | 
			
		||||
                    scaleUnit1: this.boardView.getPinDist(),
 | 
			
		||||
@@ -45,8 +50,8 @@ namespace pxsim.visuals {
 | 
			
		||||
                    scaleUnit2: this.breadboard.getPinDist(),
 | 
			
		||||
                    margin: [0, 0, 10, 0],
 | 
			
		||||
                    middleMargin: 80,
 | 
			
		||||
                    maxWidth: VIEW_WIDTH,
 | 
			
		||||
                    maxHeight: VIEW_HEIGHT,
 | 
			
		||||
                    maxWidth: maxWidth,
 | 
			
		||||
                    maxHeight: maxHeight,
 | 
			
		||||
                });
 | 
			
		||||
                let under = composition.under;
 | 
			
		||||
                let over = composition.over;
 | 
			
		||||
@@ -62,9 +67,9 @@ namespace pxsim.visuals {
 | 
			
		||||
                this.wireFactory = new WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
 | 
			
		||||
 | 
			
		||||
                let allocRes = allocateDefinitions({
 | 
			
		||||
                    boardDef: boardDef,
 | 
			
		||||
                    cmpDefs: cmpDefs,
 | 
			
		||||
                    fnArgs: fnArgs,
 | 
			
		||||
                    boardDef: opts.boardDef,
 | 
			
		||||
                    cmpDefs: opts.cmpDefs,
 | 
			
		||||
                    fnArgs: opts.fnArgs,
 | 
			
		||||
                    getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
 | 
			
		||||
                    cmpList: activeComponents,
 | 
			
		||||
                });
 | 
			
		||||
@@ -74,14 +79,37 @@ namespace pxsim.visuals {
 | 
			
		||||
                let el = this.boardView.getView().el;
 | 
			
		||||
                this.view = el;
 | 
			
		||||
                svg.hydrate(this.view, {
 | 
			
		||||
                    width: VIEW_WIDTH,
 | 
			
		||||
                    height: VIEW_HEIGHT,
 | 
			
		||||
                    width: maxWidth,
 | 
			
		||||
                    height: maxHeight,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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 {
 | 
			
		||||
            return this.view;
 | 
			
		||||
        }
 | 
			
		||||
@@ -120,7 +148,7 @@ namespace pxsim.visuals {
 | 
			
		||||
                let builtinVisual = cmpDesc.visual as string;
 | 
			
		||||
                let cnstr = builtinComponentSimVisual[builtinVisual];
 | 
			
		||||
                let stateFn = builtinComponentSimState[builtinVisual];
 | 
			
		||||
                let cmp = cnstr();
 | 
			
		||||
                cmp = cnstr();
 | 
			
		||||
                cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
 | 
			
		||||
                this.components.push(cmp);
 | 
			
		||||
                this.view.appendChild(cmp.element);
 | 
			
		||||
 
 | 
			
		||||
@@ -280,6 +280,10 @@ namespace pxsim.visuals {
 | 
			
		||||
        el: SVGRectElement,
 | 
			
		||||
        group?: string
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    export interface BreadboardOpts {
 | 
			
		||||
        wireframe?: boolean,
 | 
			
		||||
    }
 | 
			
		||||
    export class Breadboard {
 | 
			
		||||
        public bb: SVGSVGElement;
 | 
			
		||||
        private styleEl: SVGStyleElement;
 | 
			
		||||
@@ -293,8 +297,11 @@ namespace pxsim.visuals {
 | 
			
		||||
        private rowColToPin: Map<Map<GridPin>> = {};
 | 
			
		||||
        private rowColToLbls: Map<Map<GridLabel[]>> = {};
 | 
			
		||||
 | 
			
		||||
        constructor() {
 | 
			
		||||
        constructor(opts: BreadboardOpts) {
 | 
			
		||||
            this.buildDom();
 | 
			
		||||
 | 
			
		||||
            if (opts.wireframe)
 | 
			
		||||
                svg.addClass(this.bb, "sim-bb-outline");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public highlightLoc(row: string, col: string) {
 | 
			
		||||
        public highlightLoc(rowCol: BBRowCol) {
 | 
			
		||||
            let [row, col] = rowCol;
 | 
			
		||||
            let pin = this.rowColToPin[row][col];
 | 
			
		||||
            let {cx, cy} = pin;
 | 
			
		||||
            let lbls = this.rowColToLbls[row][col];
 | 
			
		||||
 
 | 
			
		||||
@@ -232,7 +232,7 @@ namespace pxsim.visuals {
 | 
			
		||||
            this.componentDefs = props.componentDefinitions;
 | 
			
		||||
 | 
			
		||||
            // breadboard
 | 
			
		||||
            this.breadboard = new Breadboard()
 | 
			
		||||
            this.breadboard = new Breadboard({})
 | 
			
		||||
            this.g.appendChild(this.breadboard.bb);
 | 
			
		||||
            let bbSize = this.breadboard.getSVGAndSize();
 | 
			
		||||
            let [bbWidth, bbHeight] = [bbSize.w, bbSize.h];
 | 
			
		||||
 
 | 
			
		||||
@@ -67,14 +67,18 @@ namespace pxsim.visuals {
 | 
			
		||||
        colorClass?: string,
 | 
			
		||||
        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 [cx, cy] = cp;
 | 
			
		||||
        let offset = WIRE_PART_CURVE_OFF;
 | 
			
		||||
        let p1: visuals.Coord = [cx - offset, cy - WIRE_PART_LENGTH / 2];
 | 
			
		||||
        let p2: visuals.Coord = [cx + offset, cy + WIRE_PART_LENGTH / 2];
 | 
			
		||||
        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 e2 = mkOpenJumperEnd(p2, false, clr);
 | 
			
		||||
        g.appendChild(s.el);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user