logicsimulator/ts/basecomponent.ts

291 lines
8.2 KiB
TypeScript

// Base class for all component for the LogicSimulator
// Copyright (C) 2022 Sascha Nitsch
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
interface BaseComponentParam {
scale?: number;
scaleX?: number;
scaleY?: number;
rotate?: number;
mirror?: string;
extraclass?: string;
label?: ComponentLabel | string;
ref?: ComponentLabel;
}
interface ComponentLabel {
x: number;
y: number;
text: string;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class BaseComponent {
protected simulator: Simulator;
protected x: number;
protected y: number;
protected id: string;
protected element: SVGGElement | null;
protected startdelay: number;
public zIndex: number;
protected scaleX: number;
protected scaleY: number;
protected rotate: number;
private mirror: string;
protected extraclass: string;
protected breadboard: BaseComponent | null = null;
protected pin = '';
protected col = 0;
private ref: ComponentLabel | undefined;
private label: ComponentLabel | undefined;
protected pins: Map<string, TriState>;
protected stateMapBool: Map<string, boolean> = new Map<string, boolean>();
protected stateMapWire: Map<string, WireState> = new Map<string, WireState>();
private fallback = new TriState(this, 0, 0, WireState.float);
constructor(simulator: Simulator, id: string, param: BaseComponentParam = {}) {
this.simulator = simulator;
this.x = 0;
this.y = 0;
this.id = id;
this.element = null;
this.startdelay = 0;
this.scaleX = param.scale || 1;
this.scaleY = param.scale || 1;
this.rotate = param.rotate || 0;
this.mirror = param.mirror || '';
this.extraclass = param.extraclass || '';
this.label = typeof param.label !== 'string' ? <ComponentLabel>param.label : undefined;
this.ref = param.ref;
this.pins = new Map<string, TriState>();
if (this.mirror !== '') {
switch (this.mirror) {
case 'x':
this.scaleX *= -1;
break;
case 'y':
this.scaleY *= -1;
}
}
this.zIndex = 1;
}
getPin(name: string): TriState {
const pin = this.pins.get(name);
if (pin === undefined) {
return this.fallback;
}
return pin;
}
getStateBool(name: string): boolean {
const state = this.stateMapBool.get(name);
if (state === undefined) {
console.log('undefined state bool entry', name);
return false;
}
return state;
}
getStateWire(name: string): WireState {
const state = this.stateMapWire.get(name);
if (state === undefined) {
console.log('undefined state wire entry', name);
return WireState.float;
}
return state;
}
setPosition(x: number | BaseComponent, y: number | string) {
if (typeof x === 'number' && typeof y === 'number') {
this.x = x;
this.y = y;
} else if (x instanceof BaseComponent && typeof y === 'string') {
const p = x.getPos(y);
this.x = p[0];
this.y = p[1];
if (this.scaleY === 1 && this.scaleX === 1) {
this.scaleX = x.getScaleX();
this.scaleY = x.getScaleY();
}
this.breadboard = x;
this.pin = y;
this.col = parseInt(y.substr(1));
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setup(_canvas: SVGElement) {
console.log('Basecomponent::setup(canvas) called');
}
doSetup(id: string, canvas: SVGElement) {
this.element = document.createElementNS('http://www.w3.org/2000/svg', 'g');
const tid = <HTMLObjectElement>document.getElementById(id);
if (tid === null || tid.contentDocument === null) return;
const root = tid.contentDocument.getElementById('root');
if (root === null) return;
const inner = <HTMLElement>root.cloneNode(true);
inner.removeAttribute('id');
let c = inner.getAttribute('class') || '';
if (this.extraclass !== '') {
c += ' ' + this.extraclass;
}
this.element.setAttribute('class', c);
inner.removeAttribute('class');
this.element.appendChild(inner);
let transform = 'translate(' + this.x + ',' + this.y + ')';
if (this.scaleX !== 1 || this.scaleY !== 1) {
transform += ' scale(' + this.scaleX + ',' + this.scaleY + ')';
}
if (this.rotate) {
transform += ' rotate(' + this.rotate + ')';
}
inner.setAttribute('transform', transform);
this.element.id = this.id;
let label;
let ref;
let txt;
if (this.label) {
label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', (this.label.x * this.scaleX + this.x).toString());
label.setAttribute('y', (this.label.y * this.scaleY + this.y).toString());
label.setAttribute('class', 'label');
txt = document.createTextNode(this.label.text ? this.label.text : this.id);
label.appendChild(txt);
this.element.appendChild(label);
}
if (this.ref) {
ref = document.createElementNS('http://www.w3.org/2000/svg', 'text');
ref.setAttribute('x', (this.ref.x * this.scaleX + this.x).toString());
ref.setAttribute('y', (this.ref.y * this.scaleY + this.y).toString());
ref.setAttribute('class', 'ref');
txt = document.createTextNode(this.ref.text);
ref.appendChild(txt);
this.element.appendChild(ref);
}
canvas.appendChild(this.element);
let SVGRect;
let rect;
if (this.label && label) {
const SVGRect = label.getBBox();
rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', SVGRect.x.toString());
rect.setAttribute('y', SVGRect.y.toString());
rect.setAttribute('width', SVGRect.width.toString());
rect.setAttribute('height', SVGRect.height.toString());
rect.setAttribute('class', 'labelbg');
this.element.insertBefore(rect, label);
}
if (this.ref && ref) {
SVGRect = ref.getBBox();
rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', SVGRect.x.toString());
rect.setAttribute('y', SVGRect.y.toString());
rect.setAttribute('width', SVGRect.width.toString());
rect.setAttribute('height', SVGRect.height.toString());
rect.setAttribute('class', 'labelbg');
this.element.insertBefore(rect, ref);
}
}
//do nothing
finalize() {}
// do nothing
io() {}
//do nothing
update(): boolean {
return false;
}
showLabel(x: number, y: number, text: string | null) {
if (text) {
this.label = { x: x, y: y, text: text };
} else {
this.label = { x: x, y: y, text: '' };
}
}
showRef(x: number, y: number, text: string) {
this.ref = { x: x, y: y, text: text };
}
setStartDelay(delay: number) {
this.startdelay = delay;
}
close() {
// do nothing
}
binary(input: WireState, pull: boolean): boolean {
let ret = this.tri(input);
if (typeof ret !== 'boolean') {
ret = pull;
}
return ret;
}
tri(input: WireState): boolean | null {
if (input === WireState.high || input === WireState.up) {
return true;
}
if (input === WireState.low || input === WireState.down) {
return false;
}
return null;
}
asint(input: boolean, int: number): number {
return input ? int : 0;
}
getX(): number {
return this.x;
}
getY(): number {
return this.y;
}
getScaleX(): number {
return this.scaleX;
}
getScaleY(): number {
return this.scaleY;
}
getRotate(): number {
return this.rotate;
}
getId(): string {
return this.id;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getPos(_column: string): number[] {
return [0, 0];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
connected(_triState: TriState): void {}
}