291 lines
8.2 KiB
TypeScript
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 {}
|
|
}
|