new part definitions (#247)

* working on new part definitions

* draft of new part definitions

* updates comments

* starting new allocator

* starting from the old allocator

* alloc internals renaming

* alloc minor renaming

* alloc internal renaming

* progress on new parts definition

* progress on new part defs allocator

* refactors BBLoc; progress on new allocator

* more progress on new allocator

* finishing new allocator

* deleting old allocator

* moves new allocator and part definitions

* porting to new part definitions

* refactors instructions for new definitions

* debugging new allocator

* fixes ground and power wire colros

* fixing new part definition bugs

* fixes wire end offsets; fixes NeoPixel placement

* fixes colorGroup issue

* fixes led matrix wiring

* naming tweaks

* fixes instructions regressions

* typo
This commit is contained in:
Daryl Zuniga
2016-09-09 01:23:39 -07:00
committed by Peli de Halleux
parent d8fc11a688
commit c63e2c85f1
13 changed files with 753 additions and 557 deletions

View File

@ -2,8 +2,8 @@ namespace pxsim.visuals {
export interface BoardHostOpts {
state: DalBoard,
boardDef: BoardDefinition,
cmpsList?: string[],
cmpDefs: Map<PartDefinition>,
partsList?: string[],
partDefs: Map<PartDefinition>,
fnArgs: any,
forceBreadboard?: boolean,
maxWidth?: string,
@ -11,13 +11,15 @@ namespace pxsim.visuals {
wireframe?: boolean
}
export class BoardHost {
private components: IBoardComponent<any>[] = [];
private parts: IBoardPart<any>[] = [];
private wireFactory: WireFactory;
private breadboard: Breadboard;
private fromBBCoord: (xy: Coord) => Coord;
private fromMBCoord: (xy: Coord) => Coord;
private boardView: BoardView;
private view: SVGSVGElement;
private partGroup: SVGGElement;
private partOverGroup: SVGGElement;
private style: SVGStyleElement;
private defs: SVGDefsElement;
private state: DalBoard;
@ -26,7 +28,7 @@ namespace pxsim.visuals {
constructor(opts: BoardHostOpts) {
this.state = opts.state;
let onboardCmps = opts.boardDef.onboardComponents || [];
let activeComponents = (opts.cmpsList || []).filter(c => onboardCmps.indexOf(c) < 0);
let activeComponents = (opts.partsList || []).filter(c => onboardCmps.indexOf(c) < 0);
activeComponents.sort();
this.useCrocClips = opts.boardDef.useCrocClips;
@ -68,6 +70,8 @@ namespace pxsim.visuals {
this.fromMBCoord = composition.toHostCoord1;
this.fromBBCoord = composition.toHostCoord2;
let pinDist = composition.scaleUnit;
this.partGroup = over;
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
@ -76,16 +80,18 @@ namespace pxsim.visuals {
let allocRes = allocateDefinitions({
boardDef: opts.boardDef,
cmpDefs: opts.cmpDefs,
partDefs: opts.partDefs,
fnArgs: opts.fnArgs,
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
cmpList: activeComponents,
partsList: activeComponents,
});
this.addAll(allocRes);
} else {
let el = this.boardView.getView().el;
this.view = el;
this.partGroup = <SVGGElement>svg.child(this.view, "g");
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
if (opts.maxWidth)
svg.hydrate(this.view, { width: opts.maxWidth });
if (opts.maxHeight)
@ -99,7 +105,7 @@ namespace pxsim.visuals {
this.boardView.highlightPin(pinNm);
}
public highlightBreadboardPin(rowCol: BBRowCol) {
public highlightBreadboardPin(rowCol: BBLoc) {
this.breadboard.highlightLoc(rowCol);
}
@ -120,10 +126,10 @@ namespace pxsim.visuals {
}
private updateState() {
this.components.forEach(c => c.updateState());
this.parts.forEach(c => c.updateState());
}
private getBBCoord(rowCol: BBRowCol) {
private getBBCoord(rowCol: BBLoc) {
let bbCoord = this.breadboard.getCoord(rowCol);
return this.fromBBCoord(bbCoord);
}
@ -134,7 +140,7 @@ namespace pxsim.visuals {
public getLocCoord(loc: Loc): Coord {
let coord: Coord;
if (loc.type === "breadboard") {
let rowCol = (<BBLoc>loc).rowCol;
let rowCol = (<BBLoc>loc);
coord = this.getBBCoord(rowCol);
} else {
let pinNm = (<BoardLoc>loc).pin;
@ -147,47 +153,62 @@ namespace pxsim.visuals {
return coord;
}
public addComponent(cmpDesc: CmpInst): IBoardComponent<any> {
let cmp: IBoardComponent<any> = null;
public addPart(partInst: PartInst): IBoardPart<any> {
let part: IBoardPart<any> = null;
let colOffset = 0;
if (typeof cmpDesc.visual === "string") {
let builtinVisual = cmpDesc.visual as string;
let cnstr = builtinComponentSimVisual[builtinVisual];
let stateFn = builtinComponentSimState[builtinVisual];
cmp = cnstr();
cmp.init(this.state.bus, stateFn(this.state), this.view, cmpDesc.microbitPins, cmpDesc.otherArgs);
if (partInst.simulationBehavior) {
//TODO: seperate simulation behavior from builtin visual
let builtinBehavior = partInst.simulationBehavior;
let cnstr = builtinComponentSimVisual[builtinBehavior];
let stateFn = builtinComponentSimState[builtinBehavior];
part = cnstr();
part.init(this.state.bus, stateFn(this.state), this.view, partInst.params);
} else {
let vis = cmpDesc.visual as PartVisualDefinition;
cmp = new GenericPart(vis);
colOffset = vis.extraColumnOffset || 0;
let vis = partInst.visual as PartVisualDefinition;
part = new GenericPart(vis);
}
this.components.push(cmp);
this.view.appendChild(cmp.element);
if (cmp.defs)
cmp.defs.forEach(d => this.defs.appendChild(d));
this.style.textContent += cmp.style || "";
let rowCol = <BBRowCol>[`${cmpDesc.breadboardStartRow}`, `${colOffset + cmpDesc.breadboardStartColumn}`];
this.parts.push(part);
this.partGroup.appendChild(part.element);
if (part.overElement)
this.partOverGroup.appendChild(part.overElement);
if (part.defs)
part.defs.forEach(d => this.defs.appendChild(d));
this.style.textContent += part.style || "";
let colIdx = partInst.startColumnIdx;
let rowIdx = partInst.startRowIdx;
let row = getRowName(rowIdx);
let col = getColumnName(colIdx);
let xOffset = partInst.bbFit.xOffset / partInst.visual.pinDistance;
let yOffset = partInst.bbFit.yOffset / partInst.visual.pinDistance;
let rowCol = <BBLoc>{
type: "breadboard",
row: row,
col: col,
xOffset: xOffset,
yOffset: yOffset
};
let coord = this.getBBCoord(rowCol);
cmp.moveToCoord(coord);
part.moveToCoord(coord);
let getCmpClass = (type: string) => `sim-${type}-cmp`;
let cls = getCmpClass(name);
svg.addClass(cmp.element, cls);
svg.addClass(cmp.element, "sim-cmp");
cmp.updateTheme();
cmp.updateState();
return cmp;
let cls = getCmpClass(partInst.name);
svg.addClass(part.element, cls);
svg.addClass(part.element, "sim-cmp");
part.updateTheme();
part.updateState();
return part;
}
public addWire(inst: WireInst): Wire {
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
}
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 addAll(allocRes: AllocatorResult) {
allocRes.partsAndWires.forEach(pAndWs => {
let part = pAndWs.part;
if (part)
this.addPart(part)
let wires = pAndWs.wires;
if (wires)
wires.forEach(w => this.addWire(w));
})
}
}
}

View File

@ -107,10 +107,10 @@ namespace pxsim.visuals {
}
`
// Pin rows and coluns
const MID_ROWS = 10;
export const BREADBOARD_MID_ROWS = 10;
export const BREADBOARD_MID_COLS = 30;
const MID_ROW_GAPS = [4, 4];
const MID_ROW_AND_GAPS = MID_ROWS + MID_ROW_GAPS.length;
const MID_COLS = 30;
const MID_ROW_AND_GAPS = BREADBOARD_MID_ROWS + MID_ROW_GAPS.length;
const BAR_ROWS = 2;
const BAR_COLS = 25;
const POWER_ROWS = BAR_ROWS * 2;
@ -118,14 +118,14 @@ namespace pxsim.visuals {
const BAR_COL_GAPS = [4, 9, 14, 19];
const BAR_COL_AND_GAPS = BAR_COLS + BAR_COL_GAPS.length;
// Essential dimensions
const WIDTH = PIN_DIST * (MID_COLS + 3);
const WIDTH = PIN_DIST * (BREADBOARD_MID_COLS + 3);
const HEIGHT = PIN_DIST * (MID_ROW_AND_GAPS + POWER_ROWS + 5.5);
const MID_RATIO = 2.0 / 3.0;
const BAR_RATIO = (1.0 - MID_RATIO) * 0.5;
const MID_HEIGHT = HEIGHT * MID_RATIO;
const BAR_HEIGHT = HEIGHT * BAR_RATIO;
// Pin grids
const MID_GRID_WIDTH = (MID_COLS - 1) * PIN_DIST;
const MID_GRID_WIDTH = (BREADBOARD_MID_COLS - 1) * PIN_DIST;
const MID_GRID_HEIGHT = (MID_ROW_AND_GAPS - 1) * PIN_DIST;
const MID_GRID_X = (WIDTH - MID_GRID_WIDTH) / 2.0;
const MID_GRID_Y = BAR_HEIGHT + (MID_HEIGHT - MID_GRID_HEIGHT) / 2.0;
@ -152,6 +152,10 @@ namespace pxsim.visuals {
const SMALL_CHANNEL_HEIGHT = PIN_DIST * 0.05;
// Background
const BACKGROUND_ROUNDING = PIN_DIST * 0.3;
// Row and column helpers
const alphabet = "abcdefghij".split("").reverse();
export function getColumnName(colIdx: number): string { return `${colIdx + 1}` };
export function getRowName(rowIdx: number): string { return alphabet[rowIdx] };
export interface GridPin {
el: SVGElement,
@ -321,12 +325,14 @@ namespace pxsim.visuals {
return null;
return pin;
}
public getCoord(rowCol: BBRowCol): Coord {
let [row, col] = rowCol;
public getCoord(rowCol: BBLoc): Coord {
let {row, col, xOffset, yOffset} = rowCol;
let pin = this.getPin(row, col);
if (!pin)
return null;
return [pin.cx, pin.cy];
let xOff = (xOffset || 0) * PIN_DIST;
let yOff = (yOffset || 0) * PIN_DIST;
return [pin.cx + xOff, pin.cy + yOff];
}
public getPinDist() {
@ -371,14 +377,11 @@ namespace pxsim.visuals {
mkChannel(BAR_HEIGHT + MID_HEIGHT, SMALL_CHANNEL_HEIGHT);
//-----pins
const getMidTopOrBot = (rowIdx: number) => rowIdx < MID_ROWS / 2.0 ? "b" : "t";
const getMidTopOrBot = (rowIdx: number) => rowIdx < BREADBOARD_MID_ROWS / 2.0 ? "b" : "t";
const getBarTopOrBot = (colIdx: number) => colIdx < POWER_COLS / 2.0 ? "b" : "t";
const alphabet = "abcdefghij".split("").reverse();
const getColName = (colIdx: number) => `${colIdx + 1}`;
const getMidRowName = (rowIdx: number) => alphabet[rowIdx];
const getMidGroupName = (rowIdx: number, colIdx: number) => {
let botOrTop = getMidTopOrBot(rowIdx);
let colNm = getColName(colIdx);
let colNm = getColumnName(colIdx);
return `${botOrTop}${colNm}`;
};
const getBarRowName = (rowIdx: number) => rowIdx === 0 ? "-" : "+";
@ -392,13 +395,13 @@ namespace pxsim.visuals {
let midGridRes = mkGrid({
xOffset: MID_GRID_X,
yOffset: MID_GRID_Y,
rowCount: MID_ROWS,
colCount: MID_COLS,
rowCount: BREADBOARD_MID_ROWS,
colCount: BREADBOARD_MID_COLS,
pinDist: PIN_DIST,
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getMidRowName,
getColName: getColName,
getRowName: getRowName,
getColName: getColumnName,
getGroupName: getMidGroupName,
rowIdxsWithGap: MID_ROW_GAPS,
});
@ -415,7 +418,7 @@ namespace pxsim.visuals {
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColName,
getColName: getColumnName,
getGroupName: getBarGroupName,
colIdxsWithGap: BAR_COL_GAPS,
});
@ -433,7 +436,7 @@ namespace pxsim.visuals {
mkPin: mkBBPin,
mkHoverPin: mkBBHoverPin,
getRowName: getBarRowName,
getColName: getColName,
getColName: getColumnName,
getGroupName: getBarGroupName,
colIdxsWithGap: BAR_COL_GAPS.map(g => g + BAR_COLS),
});
@ -460,39 +463,39 @@ namespace pxsim.visuals {
const mkBBLabelAtPin = (row: string, col: string, xOffset: number, yOffset: number, txt: string, group?: string): GridLabel => {
let size = PIN_LBL_SIZE;
let rotation = LBL_ROTATION;
let loc = this.getCoord([row, col]);
let loc = this.getCoord({type: "breadboard", row: row, col: col});
let [cx, cy] = loc;
let t = mkBBLabel(cx + xOffset, cy + yOffset, size, rotation, txt, group);
return t;
}
//columns
for (let colIdx = 0; colIdx < MID_COLS; colIdx++) {
let colNm = getColName(colIdx);
for (let colIdx = 0; colIdx < BREADBOARD_MID_COLS; colIdx++) {
let colNm = getColumnName(colIdx);
//top
let rowTIdx = 0;
let rowTNm = getMidRowName(rowTIdx);
let rowTNm = getRowName(rowTIdx);
let groupT = getMidGroupName(rowTIdx, colIdx);
let lblT = mkBBLabelAtPin(rowTNm, colNm, 0, -PIN_DIST, colNm, groupT);
this.allLabels.push(lblT);
//bottom
let rowBIdx = MID_ROWS - 1;
let rowBNm = getMidRowName(rowBIdx);
let rowBIdx = BREADBOARD_MID_ROWS - 1;
let rowBNm = getRowName(rowBIdx);
let groupB = getMidGroupName(rowBIdx, colIdx);
let lblB = mkBBLabelAtPin(rowBNm, colNm, 0, +PIN_DIST, colNm, groupB);
this.allLabels.push(lblB);
}
//rows
for (let rowIdx = 0; rowIdx < MID_ROWS; rowIdx++) {
let rowNm = getMidRowName(rowIdx);
for (let rowIdx = 0; rowIdx < BREADBOARD_MID_ROWS; rowIdx++) {
let rowNm = getRowName(rowIdx);
//top
let colTIdx = 0;
let colTNm = getColName(colTIdx);
let colTNm = getColumnName(colTIdx);
let lblT = mkBBLabelAtPin(rowNm, colTNm, -PIN_DIST, 0, rowNm);
this.allLabels.push(lblT);
//top
let colBIdx = MID_COLS - 1;
let colBNm = getColName(colBIdx);
let colBIdx = BREADBOARD_MID_COLS - 1;
let colBNm = getColumnName(colBIdx);
let lblB = mkBBLabelAtPin(rowNm, colBNm, +PIN_DIST, 0, rowNm);
this.allLabels.push(lblB);
}
@ -635,8 +638,8 @@ namespace pxsim.visuals {
return {el: this.bb, y: 0, x: 0, w: WIDTH, h: HEIGHT};
}
public highlightLoc(rowCol: BBRowCol) {
let [row, col] = rowCol;
public highlightLoc(rowCol: BBLoc) {
let {row, col} = rowCol;
let pin = this.rowColToPin[row][col];
let {cx, cy} = pin;
let lbls = this.rowColToLbls[row][col];

View File

@ -92,7 +92,7 @@ namespace pxsim.visuals {
pointer-events:none;
}
`;
export class ButtonPairView implements IBoardComponent<ButtonPairState> {
export class ButtonPairView implements IBoardPart<ButtonPairState> {
public element: SVGElement;
public defs: SVGElement[];
public style = BUTTON_PAIR_STYLE;

View File

@ -5,13 +5,13 @@ namespace pxsim.visuals {
image: partVisual.image,
width: partVisual.width,
height: partVisual.height,
imageUnitDist: partVisual.pinDist,
imageUnitDist: partVisual.pinDistance,
targetUnitDist: PIN_DIST
});
return imgAndSize;
}
export class GenericPart implements IBoardComponent<any> {
export class GenericPart implements IBoardPart<any> {
public style: string = "";
public element: SVGElement;
defs: SVGElement[] = [];
@ -19,11 +19,6 @@ namespace pxsim.visuals {
constructor(partVisual: PartVisualDefinition) {
let imgAndSize = mkGenericPartSVG(partVisual);
let img = imgAndSize.el;
let scaleFn = mkScaleFn(partVisual.pinDist, PIN_DIST);
let [pinX, pinY] = partVisual.firstPin;
let left = -scaleFn(pinX);
let top = -scaleFn(pinY);
translateEl(img, [left, top]); // So that 0,0 is on the first pin
this.element = svg.elt("g");
this.element.appendChild(img);
}
@ -33,7 +28,7 @@ namespace pxsim.visuals {
}
//unused
init(bus: EventBus, state: any, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void { }
init(bus: EventBus, state: any, svgEl: SVGSVGElement): void { }
updateState(): void { }
updateTheme(): void { }
}

View File

@ -68,7 +68,7 @@ namespace pxsim.visuals {
}
`
export class LedMatrixView implements IBoardComponent<LedMatrixState> {
export class LedMatrixView implements IBoardPart<LedMatrixState> {
private background: SVGElement;
private ledsOuter: SVGElement[];
private leds: SVGElement[];

View File

@ -107,13 +107,12 @@ namespace pxsim.visuals {
public cy: number;
constructor(xy: Coord = [0, 0]) {
let circle = <SVGElement>svg.elt("rect");
let el = <SVGElement>svg.elt("rect");
let r = PIXEL_RADIUS;
let [cx, cy] = xy;
let y = cy - r;
let x = 0;
svg.hydrate(circle, { x: "-50%", y: y, width: "100%", height: r*2, class: "sim-neopixel" });
this.el = circle;
svg.hydrate(el, { x: "-50%", y: y, width: "100%", height: r * 2, class: "sim-neopixel" });
this.el = el;
this.cy = cy;
}
@ -192,9 +191,15 @@ namespace pxsim.visuals {
}
};
function gpioPinToPinNumber(gpioPin: string): number {
let pinNumStr = gpioPin.split("P")[1];
let pinNum = Number(pinNumStr) + 7 /*MICROBIT_ID_IO_P0; TODO: don't hardcode this, import enums.d.ts*/;
function digitalPinToPinNumber(gpioPin: string): number {
const MICROBIT_ID_IO_P0 = 7; //TODO: don't hardcode this, import enums.d.ts
if (gpioPin == "*") {
return MICROBIT_ID_IO_P0;
}
let pinSplit = gpioPin.split("DigitalPin.P");
U.assert(pinSplit.length === 2, "Unknown format for pin (for NeoPixel): " + gpioPin);
let pinNumStr = pinSplit[1];
let pinNum = Number(pinNumStr) + MICROBIT_ID_IO_P0;
return pinNum
}
function parseNeoPixelMode(modeStr: string): NeoPixelMode {
@ -214,7 +219,7 @@ namespace pxsim.visuals {
return mode;
}
export class NeoPixelView implements IBoardComponent<NeoPixelState> {
export class NeoPixelView implements IBoardPart<NeoPixelState> {
public style: string = `
.sim-neopixel-canvas {
}
@ -232,6 +237,7 @@ namespace pxsim.visuals {
}
`;
public element: SVGElement;
public overElement: SVGElement;
public defs: SVGElement[];
private state: NeoPixelState;
private canvas: NeoPixelCanvas;
@ -241,22 +247,24 @@ namespace pxsim.visuals {
private pin: number;
private mode: NeoPixelMode;
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, gpioPins: string[], otherArgs: string[]): void {
U.assert(otherArgs.length === 1, "NeoPixels assumes a RGB vs RGBW mode is passed to it");
let modeStr = otherArgs[0];
public init(bus: EventBus, state: NeoPixelState, svgEl: SVGSVGElement, otherParams: Map<string>): void {
U.assert(!!otherParams["mode"], "NeoPixels assumes a RGB vs RGBW mode is passed to it");
U.assert(!!otherParams["pin"], "NeoPixels assumes a pin is passed to it");
let modeStr = otherParams["mode"];
this.mode = parseNeoPixelMode(modeStr);
this.state = state;
this.stripGroup = <SVGGElement>svg.elt("g");
this.element = this.stripGroup;
let pinStr = gpioPins[0];
this.pin = gpioPinToPinNumber(pinStr);
let pinStr = otherParams["pin"];
this.pin = digitalPinToPinNumber(pinStr);
this.lastLocation = [0, 0];
let part = mkNeoPixelPart();
this.part = part;
this.stripGroup.appendChild(part.el);
let canvas = new NeoPixelCanvas(this.pin);
this.canvas = canvas;
let canvasG = svg.child(this.stripGroup, "g", { class: "sim-neopixel-canvas-parent" });
let canvasG = svg.elt("g", { class: "sim-neopixel-canvas-parent" });
this.overElement = canvasG;
canvasG.appendChild(canvas.canvas);
this.updateStripLoc();
}
@ -268,6 +276,7 @@ namespace pxsim.visuals {
}
private updateStripLoc() {
let [x, y] = this.lastLocation;
U.assert(typeof x === "number" && typeof y === "number", "invalid x,y for NeoPixel strip");
this.canvas.setLoc([x + CANVAS_LEFT, y + CANVAS_TOP]);
svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
}

View File

@ -455,7 +455,7 @@ namespace pxsim.visuals {
let wireEls: Wire;
if (withCrocs && end.type == "dalboard") {
let boardPin = (<BoardLoc>end).pin;
if (boardPin == "P0" || boardPin == "P1" || boardPin == "P0" || boardPin == "GND" || boardPin == "+3v3" ) {
if (boardPin == "P0" || boardPin == "P1" || boardPin == "P2" || boardPin == "GND" || boardPin == "+3v3" ) {
//HACK
wireEls = this.drawWireWithCrocs(startLoc, endLoc, color);
} else {