diff --git a/docs/static/hardware/.gitignore b/docs/static/hardware/.gitignore index 3f724c25..09ade3ad 100644 --- a/docs/static/hardware/.gitignore +++ b/docs/static/hardware/.gitignore @@ -1,4 +1,4 @@ # don't check in until OSS request is approved -neopixel-black-60-vert.svg sparkfun-* -raspberrypi-* \ No newline at end of file +raspberrypi-* +arduino-* \ No newline at end of file diff --git a/pxtarget.json b/pxtarget.json index 2b5c0f04..c4113729 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -79,9 +79,9 @@ "partsAspectRatio": 0.69, "builtinParts": { "accelerometer": true, - "buttonpair": true, - "ledmatrix": true, - "speaker": true, + "buttonpair": false, + "ledmatrix": false, + "speaker": false, "bluetooth": true, "thermometer": true, "compass": true diff --git a/sim/dalboard.ts b/sim/dalboard.ts index 8baae2eb..78fdf4dd 100644 --- a/sim/dalboard.ts +++ b/sim/dalboard.ts @@ -71,11 +71,7 @@ namespace pxsim { initAsync(msg: SimulatorRunMessage): Promise { let options = (msg.options || {}) as RuntimeOptions; - //TODO: read from pxt.json/pxttarget.json - let boardDef = MICROBIT_DEF; - // let boardDef = ARDUINO_ZERO; - // let boardDef = SPARKFUN_PHOTON; - // let boardDef = RASPBERRYPI_MODELB; + let boardDef = CURRENT_BOARD; //TODO: read from pxt.json/pxttarget.json let cmpsList = msg.parts; let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json diff --git a/sim/definitions.ts b/sim/definitions.ts index 1aed3faf..38e1ee35 100644 --- a/sim/definitions.ts +++ b/sim/definitions.ts @@ -7,10 +7,11 @@ namespace pxsim { export interface PinBlockDefinition { x: number, y: number, + labelPosition: "above" | "below"; labels: string[] } export interface BoardImageDefinition { - image?: string, + image: string, outlineImage?: string, width: number, height: number, @@ -25,6 +26,8 @@ namespace pxsim { threeVoltPins: string[], attachPowerOnRight?: boolean, onboardComponents?: string[] + useCrocClips?: boolean, + marginWhenBreadboarding?: [number, number, number, number], } export interface FactoryFunctionPinAlloc { type: "factoryfunction", @@ -44,9 +47,8 @@ namespace pxsim { image: string, width: number, height: number, - left: number, - top: number, pinDist: number, + firstPin: [number, number], } export interface PartDefinition { visual: string | PartVisualDefinition, @@ -100,6 +102,8 @@ namespace pxsim { threeVoltPins: ["+3v3"], attachPowerOnRight: true, onboardComponents: ["buttonpair", "ledmatrix"], + useCrocClips: true, + marginWhenBreadboarding: [0, 0, 80, 0], } export const RASPBERRYPI_MODELB: BoardDefinition = { visual: { @@ -109,8 +113,8 @@ namespace pxsim { height: 230, pinDist: 9, pinBlocks: [ - { x: 5, y: 31, 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: 31, labelPosition: "above", labels: ["3V3", "SDA", "SCL", "#4", "--", "#17", "#21", "#22", "--", "MOSI", "MISO", "SCLK", "--"]}, + { x: 5, y: 39, labelPosition: "below", labels: ["5V", "--", "GND", "TXD", "RXD", "#18", "--", "#23", "#24", "--", "#25", "CS0", "CS1"]} ], }, gpioPinBlocks: [ @@ -139,6 +143,7 @@ namespace pxsim { }, groundPins: ["GND"], threeVoltPins: ["3V3"], + marginWhenBreadboarding: [20, 0, 40, 0], } export const SPARKFUN_PHOTON: BoardDefinition = { visual: { @@ -148,10 +153,10 @@ namespace pxsim { height: 202.4, pinDist: 9.5, pinBlocks: [ - {x: 72, y: 6, 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: 107, y: 188, labels: [" ", " ", "RESET", "3.3V", "V-USB", "GND1", "GND2", "VIN"]}, - {x: 193, y: 188, labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, + {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, labelPosition: "below", labels: ["D7", "D6", "D5", "D4", "~D3", "~D2", "~TX", "~RX"]}, + {x: 107, y: 188, labelPosition: "above", labels: [" ", " ", "RESET", "3.3V", "V-USB", "GND1", "GND2", "VIN"]}, + {x: 193, y: 188, labelPosition: "above", labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, ], }, gpioPinBlocks: [ @@ -181,6 +186,7 @@ namespace pxsim { }, groundPins: ["GND0", "GND1", "GND2"], threeVoltPins: ["3.3V"], + marginWhenBreadboarding: [20, 0, 40, 0], } export const ARDUINO_ZERO: BoardDefinition = { visual: { @@ -190,10 +196,10 @@ namespace pxsim { height: 762, pinDist: 35.5, pinBlocks: [ - {x: 276.8, y: 17.8, 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: 411.7, y: 704.6, labels: ["ATN", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN"]}, - {x: 732.9, y: 704.6, labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, + {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, labelPosition: "below", labels: ["7", "~6", "~5", "~4", "~3", "2", "TX->1", "RX<-0"]}, + {x: 411.7, y: 704.6, labelPosition: "above", labels: ["ATN", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN"]}, + {x: 732.9, y: 704.6, labelPosition: "above", labels: ["A0", "A1", "A2", "A3", "A4", "A5"]}, ], }, gpioPinBlocks: [ @@ -224,8 +230,9 @@ namespace pxsim { }, groundPins: ["GND0", "GND1", "GND2"], threeVoltPins: ["3.3V"], + marginWhenBreadboarding: [20, 0, 40, 0], } - + export const PART_DEFINITIONS: Map = { "ledmatrix": { visual: "ledmatrix", @@ -287,8 +294,7 @@ namespace pxsim { image: "/static/hardware/speaker.svg", width: 500, height: 500, - left: -180, - top: -135, + firstPin: [180, 135], pinDist: 70, }, breadboardColumnsNeeded: 5, @@ -299,8 +305,8 @@ namespace pxsim { }, assemblyStep: 0, wires: [ - {start: ["breadboard", "j", 1], end: ["GPIO", 0], color: "white", assemblyStep: 1}, - {start: ["breadboard", "j", 3], end: "ground", color: "white", assemblyStep: 1}, + {start: ["breadboard", "j", 1], end: ["GPIO", 0], color: "#ff80fa", 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), "neopixel": (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy), }; + + //TODO: add multiple board support + //export const CURRENT_BOARD = MICROBIT_DEF; + export const CURRENT_BOARD = ARDUINO_ZERO; } \ No newline at end of file diff --git a/sim/instructions/instructions.ts b/sim/instructions/instructions.ts index 9ae5a649..de295bf7 100644 --- a/sim/instructions/instructions.ts +++ b/sim/instructions/instructions.ts @@ -128,10 +128,18 @@ namespace pxsim.instructions { cmpHeight?: number, cmpScale?: number }; - function mkBoardImgSvg(def: BoardImageDefinition): visuals.SVGElAndSize { - return new visuals.MicrobitBoardSvg({ - theme: visuals.randomTheme() - }).getView(); + function mkBoardImgSvg(def: string | BoardImageDefinition): visuals.SVGElAndSize { + let boardView: visuals.BoardView; + if (def === "microbit") { + boardView = new visuals.MicrobitBoardSvg({ + theme: visuals.randomTheme() + }) + } else { + boardView = new visuals.GenericBoardSvg({ + visualDef: def + }) + } + return boardView.getView(); } function mkBBSvg(): visuals.SVGElAndSize { let bb = new visuals.Breadboard({}); @@ -432,7 +440,7 @@ namespace pxsim.instructions { let panel = mkPanel(); // board and breadboard - let boardImg = mkBoardImgSvg(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}); panel.appendChild(board); let bbRaw = mkBBSvg(); @@ -633,7 +641,7 @@ ${tsPackage} style.textContent += STYLE; - const boardDef = MICROBIT_DEF; + const boardDef = CURRENT_BOARD; const cmpDefs = PART_DEFINITIONS; //props diff --git a/sim/simlib.ts b/sim/simlib.ts index 334cc44f..03276157 100644 --- a/sim/simlib.ts +++ b/sim/simlib.ts @@ -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 { + let scaleFn = mkScaleFn(opts.imageUnitDist, opts.targetUnitDist); + let w = scaleFn(opts.width); + let h = scaleFn(opts.height); + let img = 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 function findDistSqrd(a: Coord, b: Coord): number { let x = a[0] - b[0]; diff --git a/sim/visuals/boardhost.ts b/sim/visuals/boardhost.ts index 9a44d849..cdc0c405 100644 --- a/sim/visuals/boardhost.ts +++ b/sim/visuals/boardhost.ts @@ -21,32 +21,43 @@ namespace pxsim.visuals { private style: SVGStyleElement; private defs: SVGDefsElement; private state: DalBoard; + private useCrocClips: boolean; constructor(opts: BoardHostOpts) { this.state = opts.state; let onboardCmps = opts.boardDef.onboardComponents || []; let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0); activeComponents.sort(); + this.useCrocClips = opts.boardDef.useCrocClips; - this.boardView = new visuals.MicrobitBoardSvg({ - runtime: runtime, - theme: visuals.randomTheme(), - disableTilt: false, - wireframe: opts.wireframe, - }); + if (opts.boardDef.visual === "microbit") { + this.boardView = new visuals.MicrobitBoardSvg({ + runtime: runtime, + theme: visuals.randomTheme(), + 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; if (useBreadboard) { this.breadboard = new Breadboard({ wireframe: opts.wireframe, }); + let bMarg = opts.boardDef.marginWhenBreadboarding || [0, 0, 40, 0]; let composition = composeSVG({ el1: this.boardView.getView(), scaleUnit1: this.boardView.getPinDist(), el2: this.breadboard.getSVGAndSize(), scaleUnit2: this.breadboard.getPinDist(), - margin: [0, 0, 20, 0], - middleMargin: 80, + margin: [bMarg[0], bMarg[1], 20, bMarg[3]], + middleMargin: bMarg[2], maxWidth: opts.maxWidth, maxHeight: opts.maxHeight, }); @@ -166,7 +177,7 @@ namespace pxsim.visuals { return cmp; } 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) { let {powerWires, components} = basicWiresAndCmpsAndWires; diff --git a/sim/visuals/genericboard.ts b/sim/visuals/genericboard.ts index 2194537f..59437f70 100644 --- a/sim/visuals/genericboard.ts +++ b/sim/visuals/genericboard.ts @@ -3,46 +3,6 @@ /// namespace pxsim.visuals { - const svg = pxsim.svg; - - export interface IBoardSvgProps { - runtime: pxsim.Runtime; - boardDef: BoardDefinition; - disableTilt?: boolean; - activeComponents: string[]; - fnArgs?: any; - componentDefinitions: Map; - } - - 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 = ` .noselect { -webkit-touch-callout: none; /* iOS Safari */ @@ -53,52 +13,6 @@ namespace pxsim.visuals { user-select: none; /* Non-prefixed version, currently 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 { fill:#999; @@ -178,202 +92,72 @@ namespace pxsim.visuals { 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; - export class GenericBoardSvg /*TODO: implements BoardView*/ { - public hostElement: SVGSVGElement; + export class GenericBoardSvg implements BoardView { + private element: SVGSVGElement; private style: SVGStyleElement; private defs: SVGDefsElement; private g: SVGGElement; - public board: pxsim.DalBoard; - public background: SVGElement; - private components: IBoardComponent[]; - public breadboard: Breadboard; - private underboard: SVGGElement; - public boardDef: BoardDefinition; - private boardDim: ComputedBoardDimensions; - public componentDefs: Map; - private boardEdges: number[]; + private background: SVGElement; + private width: number; + private height: number; private id: number; - public bbX: number; - public bbY: number; - private boardTopEdge: number; - private boardBotEdge: number; - private wireFactory: WireFactory; - //truth + + // pins & labels + //(truth) private allPins: GridPin[] = []; private allLabels: GridLabel[] = []; - //cache + //(cache) private pinNmToLbl: Map = {}; private pinNmToPin: Map = {}; - constructor(public props: IBoardSvgProps) { + constructor(public props: GenericBoardProps) { + //TODO: handle wireframe mode this.id = nextBoardId++; - this.boardDef = props.boardDef; - this.boardDim = getBoardDimensions(this.boardDef.visual); - this.board = this.props.runtime.board as pxsim.DalBoard; - this.board.updateView = () => this.updateState(); - this.hostElement = svg.elt("svg") - svg.hydrate(this.hostElement, { + let visDef = props.visualDef; + let imgHref = props.wireframe ? visDef.outlineImage : visDef.image; + let boardImgAndSize = mkImageSVG({ + image: imgHref, + width: visDef.width, + 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 = svg.elt("svg"); + svg.hydrate(this.element, { "version": "1.0", - "viewBox": `0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`, - "enable-background": `new 0 0 ${VIEW_WIDTH} ${VIEW_HEIGHT}`, + "viewBox": `0 0 ${this.width} ${this.height}`, "class": `sim sim-board-id-${this.id}`, "x": "0px", "y": "0px" }); - this.style = svg.child(this.hostElement, "style", {}); + if (props.wireframe) + svg.addClass(this.element, "sim-board-outline") + this.style = svg.child(this.element, "style", {}); this.style.textContent += BOARD_SYTLE; - this.defs = svg.child(this.hostElement, "defs", {}); + this.defs = svg.child(this.element, "defs", {}); this.g = svg.elt("g"); - this.hostElement.appendChild(this.g); - this.underboard = 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 = (loc).rowCol; - coord = this.getBBCoord(rowCol); - } else { - let pinNm = (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 = 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 { - let cmp: IBoardComponent = 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 = [`${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" }) + this.element.appendChild(this.g); // main board - this.background = svg.child(this.g, "image", - { class: "sim-board", x: this.boardDim.xOff, y: this.boardDim.yOff, width: this.boardDim.width, height: this.boardDim.height, - "href": `${(this.boardDef.visual).image}`}); - let backgroundCover = this.mkGrayCover(this.boardDim.xOff, this.boardDim.yOff, this.boardDim.width, this.boardDim.height); + this.g.appendChild(img); + this.background = img; + svg.hydrate(img, { class: "sim-board" }); + let backgroundCover = this.mkGrayCover(0, 0, this.width, this.height); this.g.appendChild(backgroundCover); // ----- pins @@ -398,8 +182,8 @@ namespace pxsim.visuals { return {el: el, w: width, h: width, x: 0, y: 0}; } const mkPinBlockGrid = (pinBlock: PinBlockDefinition, blockIdx: number) => { - let xOffset = this.boardDim.xOff + this.boardDim.scaleFn(pinBlock.x) + PIN_DIST / 2.0; - let yOffset = this.boardDim.yOff + this.boardDim.scaleFn(pinBlock.y) + PIN_DIST / 2.0; + let xOffset = scaleFn(pinBlock.x) + PIN_DIST / 2.0; + let yOffset = scaleFn(pinBlock.y) + PIN_DIST / 2.0; let rowCount = 1; let colCount = pinBlock.labels.length; let getColName = (colIdx: number) => pinBlock.labels[colIdx]; @@ -422,9 +206,11 @@ namespace pxsim.visuals { svg.addClass(gridRes.g, "sim-board-pin-group"); return gridRes; }; - let pinBlocks = (this.boardDef.visual).pinBlocks.map(mkPinBlockGrid); - pinBlocks.forEach(blk => blk.allPins.forEach(p => { + let pinBlocks = visDef.pinBlocks.map(mkPinBlockGrid); + let pinToBlockDef: PinBlockDefinition[] = []; + pinBlocks.forEach((blk, blkIdx) => blk.allPins.forEach((p, pIdx) => { this.allPins.push(p); + pinToBlockDef.push(visDef.pinBlocks[blkIdx]); })); //tooltip this.allPins.forEach(p => { @@ -443,15 +229,12 @@ namespace pxsim.visuals { }); // ----- 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 let lblY: number; let lblX: number; - let edges = [this.boardTopEdge, this.boardBotEdge]; - let distFromTopBot = edges.map(e => Math.abs(e - pinY)); - let closestEdgeIdx = distFromTopBot.reduce((pi, n, ni) => n < distFromTopBot[pi] ? ni : pi, 0); - let topEdge = closestEdgeIdx == 0; - if (topEdge) { + + if (pos === "below") { let lblLen = size * 0.25 * txt.length; lblX = pinX; lblY = pinY + 12 + lblLen; @@ -463,16 +246,17 @@ namespace pxsim.visuals { let el = mkTxt(lblX, lblY, size, -90, txt); return el; }; - const mkLabel = (pinX: number, pinY: number, txt: string): GridLabel => { - let el = mkLabelTxtEl(pinX, pinY, PIN_LBL_SIZE, txt); + const mkLabel = (pinX: number, pinY: number, txt: string, pos: "above" | "below"): GridLabel => { + let el = mkLabelTxtEl(pinX, pinY, PIN_LBL_SIZE, txt, pos); 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"); let label: GridLabel = {el: el, hoverEl: hoverEl, txt: txt}; return label; } - this.allLabels = this.allPins.map(p => { - return mkLabel(p.cx, p.cy, p.col); + this.allLabels = this.allPins.map((p, pIdx) => { + let blk = pinToBlockDef[pIdx]; + return mkLabel(p.cx, p.cy, p.col, blk.labelPosition); }); //attach labels 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 = svg.elt("rect"); + svg.hydrate(rect, {x: x, y: y, width: w, height: h, class: "gray-cover"}); + return rect; + } + + + public getView(): SVGAndSize { + 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 pin = this.pinNmToPin[pinNm]; if (lbl && pin) { @@ -496,20 +302,5 @@ namespace pxsim.visuals { svg.addClass(pin.hoverEl, "highlight"); } } - - public highlightWire(wire: Wire) { - //underboard wires - wire.wires.forEach(e => { - (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"); - }); - } } } \ No newline at end of file