pxt-calliope/sim/visuals/ledmatrix.ts

135 lines
4.8 KiB
TypeScript

/// <reference path="../../node_modules/pxt-core/built/pxtsim.d.ts"/>
namespace pxsim.visuals {
export function mkLedMatrixSvg(xy: Coord, rows: number, cols: number):
{ el: SVGGElement, y: number, x: number, w: number, h: number, leds: SVGElement[], ledsOuter: SVGElement[], background: SVGElement } {
let result: { el: SVGGElement, y: number, x: number, w: number, h: number, leds: SVGElement[], ledsOuter: SVGElement[], background: SVGElement }
= { el: null, y: 0, x: 0, w: 0, h: 0, leds: [], ledsOuter: [], background: null };
result.el = <SVGGElement>svg.elt("g");
let width = cols * PIN_DIST;
let height = rows * PIN_DIST;
let ledRad = Math.round(PIN_DIST * .35);
let spacing = PIN_DIST;
let padding = (spacing - 2 * ledRad) / 2.0;
let [x, y] = xy;
let left = x - (ledRad + padding);
let top = y - (ledRad + padding);
result.x = left;
result.y = top;
result.w = width;
result.h = height;
result.background = svg.child(result.el, "rect", { class: "sim-display", x: left, y: top, width: width, height: height })
// ledsOuter
result.leds = [];
result.ledsOuter = [];
let hoverRad = ledRad * 1.2;
for (let i = 0; i < rows; ++i) {
let y = top + ledRad + i * spacing + padding;
for (let j = 0; j < cols; ++j) {
let x = left + ledRad + j * spacing + padding;
result.ledsOuter.push(svg.child(result.el, "circle", { class: "sim-led-back", cx: x, cy: y, r: ledRad }));
result.leds.push(svg.child(result.el, "circle", { class: "sim-led", cx: x, cy: y, r: hoverRad, title: `(${j},${i})` }));
}
}
//default theme
svg.fill(result.background, defaultLedMatrixTheme.background);
svg.fills(result.leds, defaultLedMatrixTheme.ledOn);
svg.fills(result.ledsOuter, defaultLedMatrixTheme.ledOff);
//turn off LEDs
result.leds.forEach(l => (<SVGStyleElement><any>l).style.opacity = 0 + "");
return result;
}
export interface ILedMatrixTheme {
background?: string;
ledOn?: string;
ledOff?: string;
}
export var defaultLedMatrixTheme: ILedMatrixTheme = {
background: "#000",
ledOn: "#ff5f5f",
ledOff: "#DDD",
};
export const LED_MATRIX_STYLE = `
.sim-led-back:hover {
stroke:#a0a0a0;
stroke-width:3px;
}
.sim-led:hover {
stroke:#ff7f7f;
stroke-width:3px;
}
`
export class LedMatrixView implements IBoardPart<LedMatrixState> {
private background: SVGElement;
private ledsOuter: SVGElement[];
private leds: SVGElement[];
private state: LedMatrixState;
private bus: EventBus;
public element: SVGElement;
public defs: SVGElement[];
private theme: ILedMatrixTheme;
private DRAW_SIZE = 8;
private ACTIVE_SIZE = 5;
public style = LED_MATRIX_STYLE;
public init(bus: EventBus, state: LedMatrixState) {
this.bus = bus;
this.state = state;
this.theme = defaultLedMatrixTheme;
this.defs = [];
this.element = this.buildDom();
}
public moveToCoord(xy: Coord) {
translateEl(this.element, xy);
}
public updateTheme() {
svg.fill(this.background, this.theme.background);
svg.fills(this.leds, this.theme.ledOn);
svg.fills(this.ledsOuter, this.theme.ledOff);
}
public updateState() {
if (this.state.disabled) {
this.leds.forEach((led, i) => {
let sel = (<SVGStyleElement><any>led)
sel.style.opacity = 0 + "";
});
return;
}
const bw = this.state.displayMode == pxsim.DisplayMode.bw
const img = this.state.image;
this.leds.forEach((led, i) => {
let sel = (<SVGStyleElement><any>led)
let dx = i % this.DRAW_SIZE;
let dy = (i - dx) / this.DRAW_SIZE;
if (dx < this.ACTIVE_SIZE && dy < this.ACTIVE_SIZE) {
let j = dx + dy * this.ACTIVE_SIZE;
sel.style.opacity = ((bw ? img.data[j] > 0 ? 255 : 0 : img.data[j]) / 255.0) + "";
} else {
sel.style.opacity = 0 + "";
}
})
}
public buildDom() {
let res = mkLedMatrixSvg([0, 0], this.DRAW_SIZE, this.DRAW_SIZE);
let display = res.el;
this.background = res.background;
this.leds = res.leds;
this.ledsOuter = res.ledsOuter;
return display;
}
}
}