// 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 . 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; protected stateMapBool: Map = new Map(); protected stateMapWire: Map = new Map(); 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' ? param.label : undefined; this.ref = param.ref; this.pins = new Map(); 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 = document.getElementById(id); if (tid === null || tid.contentDocument === null) return; const root = tid.contentDocument.getElementById('root'); if (root === null) return; const inner = 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 {} }