Compare commits
10 Commits
7fe2e3361a
...
8a3a31e0c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a3a31e0c0 | |||
| c9d0a36f92 | |||
| d62aab85ea | |||
| c68217b595 | |||
| eba8d51e3e | |||
| 8f33a16843 | |||
| c66ae0854d | |||
| 21084b2de2 | |||
| feb7ea9161 | |||
| 7f82cc6e57 |
@ -16,6 +16,8 @@ export class Calculation {
|
|||||||
private potentials: number[] = [];
|
private potentials: number[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
readonly number: number,
|
||||||
|
readonly pivot: Junction,
|
||||||
readonly parts: Part[],
|
readonly parts: Part[],
|
||||||
readonly junctionsWithoutPivot: Junction[],
|
readonly junctionsWithoutPivot: Junction[],
|
||||||
readonly wires: Wire[]) {
|
readonly wires: Wire[]) {
|
||||||
@ -30,7 +32,13 @@ export class Calculation {
|
|||||||
junction.minCircuitVoltage = minCircuitVoltage;
|
junction.minCircuitVoltage = minCircuitVoltage;
|
||||||
junction.maxCircuitVoltage = maxCircuitVoltage;
|
junction.maxCircuitVoltage = maxCircuitVoltage;
|
||||||
junction.voltage = this.getPotential(junctionsWithoutPivot.indexOf(junction));
|
junction.voltage = this.getPotential(junctionsWithoutPivot.indexOf(junction));
|
||||||
|
console.debug(" junction", junction.fullName, junction.voltage, 'V');
|
||||||
});
|
});
|
||||||
|
pivot.minCircuitVoltage = minCircuitVoltage;
|
||||||
|
pivot.maxCircuitVoltage = maxCircuitVoltage;
|
||||||
|
pivot.voltage = 0;
|
||||||
|
const junctionCountIncludingPivot = junctionsWithoutPivot.length + 1;
|
||||||
|
console.debug(` => Circuit #${number} (${parts.length} parts, ${junctionCountIncludingPivot} junctions, ${wires.length} wires)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private matrixInit(size: number) {
|
private matrixInit(size: number) {
|
||||||
@ -67,6 +75,7 @@ export class Calculation {
|
|||||||
const potentialEnd = this.getPotential(indexEnd);
|
const potentialEnd = this.getPotential(indexEnd);
|
||||||
const potentialDifference = potentialStart === null || potentialEnd === null ? 0 : potentialEnd - potentialStart;
|
const potentialDifference = potentialStart === null || potentialEnd === null ? 0 : potentialEnd - potentialStart;
|
||||||
wire.current = conductance * potentialDifference;
|
wire.current = conductance * potentialDifference;
|
||||||
|
console.debug(" wire", wire.toString(), wire.current, 'A');
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPotential(index: number): number | null {
|
private getPotential(index: number): number | null {
|
||||||
@ -97,31 +106,32 @@ export class Calculation {
|
|||||||
|
|
||||||
static calculate(circuit: Circuit): Calculation[] {
|
static calculate(circuit: Circuit): Calculation[] {
|
||||||
const restParts = [...circuit.parts];
|
const restParts = [...circuit.parts];
|
||||||
console.debug("Recalculating circuit...")
|
console.debug("Calculating circuit...")
|
||||||
const circuits: Calculation[] = [];
|
const circuits: Calculation[] = [];
|
||||||
let circuitNumber = 0;
|
let circuitCount = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
const battery = restParts.filter(p => p instanceof Battery)[0];
|
const pivot = restParts.filter(p => p instanceof Battery)[0];
|
||||||
if (!battery) {
|
if (!pivot) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const circuit = this.calculateDisjunct(battery, restParts, circuitNumber++);
|
const circuit = this.calculateDisjunct(pivot.minus, restParts, circuitCount++);
|
||||||
circuits.push(circuit);
|
circuits.push(circuit);
|
||||||
}
|
}
|
||||||
|
console.debug(`Found ${circuitCount} disjunct circuits.`);
|
||||||
if (restParts.length > 0) {
|
if (restParts.length > 0) {
|
||||||
console.debug(`found ${restParts.length} not connected to any battery`);
|
console.debug(`Got ${restParts.length} unconnected parts.`);
|
||||||
}
|
}
|
||||||
return circuits;
|
return circuits;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static calculateDisjunct(battery: Battery, restParts: Part[], circuitNumber: number): Calculation {
|
private static calculateDisjunct(pivot: Junction, restParts: Part[], circuitNumber: number): Calculation {
|
||||||
console.debug(` Circuit #${circuitNumber}:`)
|
console.debug(` Circuit #${circuitNumber}:`)
|
||||||
|
console.debug(" pivot:", pivot.fullName);
|
||||||
|
|
||||||
const foundParts: Part[] = [battery];
|
const foundParts: Part[] = [pivot.part];
|
||||||
restParts.splice(restParts.indexOf(battery), 1);
|
restParts.splice(restParts.indexOf(pivot.part), 1);
|
||||||
|
|
||||||
const pivot = battery.minus;
|
const foundJunctionsWithoutPivot: Junction[] = [];
|
||||||
const foundJunctions: Junction[] = [];
|
|
||||||
const todo: Junction[] = [pivot];
|
const todo: Junction[] = [pivot];
|
||||||
|
|
||||||
const foundWires: Wire[] = [];
|
const foundWires: Wire[] = [];
|
||||||
@ -137,21 +147,20 @@ export class Calculation {
|
|||||||
}
|
}
|
||||||
const destinationJunction = wire.traverse(sourceJunction);
|
const destinationJunction = wire.traverse(sourceJunction);
|
||||||
const destinationPart = destinationJunction.part;
|
const destinationPart = destinationJunction.part;
|
||||||
const newJunction = destinationJunction !== pivot && !foundJunctions.includes(destinationJunction);
|
const newJunctionAndNotPivot = destinationJunction !== pivot && !foundJunctionsWithoutPivot.includes(destinationJunction);
|
||||||
console.debug(` ${newJunction ? "[NEW]" : "[___]"} ${wire}`)
|
console.debug(` ${newJunctionAndNotPivot ? "[NEW]" : "[___]"} Wire ${wire}`)
|
||||||
if (newJunction) {
|
if (newJunctionAndNotPivot) {
|
||||||
const newPart = !foundParts.includes(destinationPart);
|
const newPart = !foundParts.includes(destinationPart);
|
||||||
if (newPart) {
|
if (newPart) {
|
||||||
foundParts.push(destinationPart);
|
foundParts.push(destinationPart);
|
||||||
restParts.splice(restParts.indexOf(battery), 1);
|
restParts.splice(restParts.indexOf(destinationPart), 1);
|
||||||
}
|
}
|
||||||
todo.push(destinationJunction);
|
todo.push(destinationJunction);
|
||||||
foundJunctions.push(destinationJunction);
|
foundJunctionsWithoutPivot.push(destinationJunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const junctionCountIncludingPivot = foundJunctions.length + 1;
|
return new Calculation(circuitNumber, pivot, foundParts, foundJunctionsWithoutPivot, foundWires);
|
||||||
console.debug(` => Circuit #${circuitNumber} (${foundParts.length} parts, ${junctionCountIncludingPivot} junctions, ${foundWires.length} wires)`);
|
|
||||||
return new Calculation(foundParts, foundJunctions, foundWires);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,101 @@
|
|||||||
import {Part} from "../parts/Part";
|
import {Part} from "../parts/Part";
|
||||||
import {Wire} from "../wire/Wire";
|
import {Wire} from "../wire/Wire";
|
||||||
import {Calculation} from './Calculation';
|
import {Calculation, RESISTANCE_MIN} from './Calculation';
|
||||||
|
import {Battery} from '../parts/battery/Battery';
|
||||||
|
import {Light} from '../parts/light/Light';
|
||||||
|
import {Relay} from '../parts/relay/Relay';
|
||||||
|
import {Junction} from '../junction/Junction';
|
||||||
|
import {Switch} from '../parts/switch/Switch';
|
||||||
|
|
||||||
export class Circuit {
|
export class Circuit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly uuid: string,
|
readonly uuid: string,
|
||||||
public name: string,
|
public name: string,
|
||||||
readonly parts: Part[],
|
readonly parts: Part[] = [],
|
||||||
readonly wires: Wire[],
|
readonly wires: Wire[] = [],
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
static new() {
|
static new() {
|
||||||
return new Circuit(
|
return new Circuit(self.crypto.randomUUID(), "Unbenannt");
|
||||||
self.crypto.randomUUID(),
|
|
||||||
"Unbenannt",
|
|
||||||
[],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calculate() {
|
calculate() {
|
||||||
Calculation.calculate(this);
|
for (let i = 0; i < 50; i++) {
|
||||||
|
Calculation.calculate(this);
|
||||||
|
const changedParts = [];
|
||||||
|
for (const part of this.parts) {
|
||||||
|
if (part.loop()) {
|
||||||
|
changedParts.push(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changedParts.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Recalculating due to parts changed:", changedParts.map(p => p.name).join(", "));
|
||||||
|
}
|
||||||
|
console.error("Too many recalculations!");
|
||||||
|
this.resetCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetCalculations() {
|
||||||
|
this.parts.forEach(part => part.resetCalculations());
|
||||||
|
this.wires.forEach(wire => wire.resetCalculations());
|
||||||
|
}
|
||||||
|
|
||||||
|
newBattery(rasterX: number, rasterY: number, name: string | undefined = undefined, voltage: number = 1.5, resistance: number = 0.5): Battery {
|
||||||
|
name = this.generateName(name, "Batterie");
|
||||||
|
return this.add(new Battery(this, rasterX, rasterY, name, voltage, resistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
newLight(rasterX: number, rasterY: number, name: string | undefined = undefined, voltageMax: number = 3, resistance: number = 100): Light {
|
||||||
|
name = this.generateName(name, "Licht");
|
||||||
|
return this.add(new Light(this, rasterX, rasterY, name, voltageMax, resistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
newRelay(rasterX: number, rasterY: number, name: string | undefined = undefined, voltageMin: number = 1.0, voltageMax: number = 3, resistance: number = 150): Relay {
|
||||||
|
name = this.generateName(name, "Relais");
|
||||||
|
return this.add(new Relay(this, rasterX, rasterY, name, voltageMin, voltageMax, resistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
newSwitch(rasterX: number, rasterY: number, name: string | undefined = undefined, momentary: boolean = false): Switch {
|
||||||
|
name = this.generateName(name, "Relais");
|
||||||
|
return this.add(new Switch(this, rasterX, rasterY, name, momentary));
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateName(name: string | undefined, baseName: string) {
|
||||||
|
if (name !== undefined) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
let counter = 1;
|
||||||
|
while (true) {
|
||||||
|
name = `${baseName} #${counter++}`;
|
||||||
|
if (!this.parts.some(p => p.name === name)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private add<T extends Part>(part: T): T {
|
||||||
|
this.parts.push(part);
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(start: Junction, end: Junction) {
|
||||||
|
const wire = new Wire(start, end, RESISTANCE_MIN);
|
||||||
|
this.wires.push(wire);
|
||||||
|
console.log(`Wire connected: ${wire}`);
|
||||||
|
this.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(wire: Wire) {
|
||||||
|
this.wires.splice(this.wires.indexOf(wire), 1);
|
||||||
|
wire.disconnect();
|
||||||
|
console.log("Wire disconnected: ", wire);
|
||||||
|
this.calculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,14 @@
|
|||||||
import {Circuit} from "./Circuit";
|
import {Circuit} from "./Circuit";
|
||||||
import {Battery} from '../parts/battery/Battery';
|
|
||||||
import {Light} from '../parts/light/Light';
|
|
||||||
import {Wire} from '../wire/Wire';
|
|
||||||
import {RESISTANCE_MIN} from './Calculation';
|
|
||||||
|
|
||||||
const battery = new Battery(1, 3, "Batterie", 3, 0.5);
|
export const DEMO_001 = create();
|
||||||
|
|
||||||
const light = new Light(1, 1, "Licht", 3, 150);
|
function create() {
|
||||||
|
const circuit = new Circuit("DEMO_001", "1. Batterie und Licht");
|
||||||
export const DEMO_001 = new Circuit(
|
const battery = circuit.newBattery(1, 3);
|
||||||
"DEMO_001",
|
const light = circuit.newLight(1, 1);
|
||||||
"1. Batterie und Licht",
|
circuit.parts.push(battery);
|
||||||
[battery, light,],
|
circuit.parts.push(light);
|
||||||
[
|
circuit.connect(battery.minus, light.a);
|
||||||
new Wire(battery.minus, light.a, RESISTANCE_MIN, ""),
|
circuit.connect(battery.plus, light.b);
|
||||||
new Wire(battery.plus, light.b, RESISTANCE_MIN, ""),
|
return circuit;
|
||||||
],
|
}
|
||||||
);
|
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
import {Circuit} from "./Circuit";
|
import {Circuit} from "./Circuit";
|
||||||
import {Battery} from '../parts/battery/Battery';
|
|
||||||
import {Light} from '../parts/light/Light';
|
|
||||||
import {Wire} from '../wire/Wire';
|
|
||||||
import {RESISTANCE_MIN} from './Calculation';
|
|
||||||
|
|
||||||
const battery0 = new Battery(1, 3, "Batterie 1", 1.5, 0.5);
|
export const DEMO_002 = create();
|
||||||
const battery1 = new Battery(3, 3, "Batterie 2", 1.5, 0.5);
|
|
||||||
const light0 = new Light(1, 1, "Licht 1", 3, 150);
|
|
||||||
const light1 = new Light(3, 1, "Licht 2", 3, 150);
|
|
||||||
const light2 = new Light(2, 5, "Licht 3", 3, 150);
|
|
||||||
|
|
||||||
export const DEMO_002 = new Circuit(
|
function create() {
|
||||||
"DEMO_001",
|
const circuit = new Circuit("DEMO_002", "2. Reihe und Parallel");
|
||||||
"2. Reihe und Parallel",
|
const battery0 = circuit.newBattery(1, 3);
|
||||||
[battery0, battery1, light0, light1, light2],
|
const battery1 = circuit.newBattery(3, 3);
|
||||||
[
|
const light0 = circuit.newLight(1, 1);
|
||||||
new Wire(light0.a, battery0.minus, RESISTANCE_MIN, ""),
|
const light1 = circuit.newLight(3, 1);
|
||||||
new Wire(light0.b, light1.a, RESISTANCE_MIN, ""),
|
const light2 = circuit.newLight(2, 5);
|
||||||
new Wire(light1.b, battery1.plus, RESISTANCE_MIN, ""),
|
circuit.connect(light0.a, battery0.minus);
|
||||||
new Wire(battery0.plus, battery1.minus, RESISTANCE_MIN, ""),
|
circuit.connect(light0.b, light1.a);
|
||||||
new Wire(light2.a, battery0.minus, RESISTANCE_MIN, ""),
|
circuit.connect(light1.b, battery1.plus);
|
||||||
new Wire(light2.b, battery1.plus, RESISTANCE_MIN, ""),
|
circuit.connect(battery0.plus, battery1.minus);
|
||||||
],
|
circuit.connect(light2.a, battery0.minus);
|
||||||
);
|
circuit.connect(light2.b, battery1.plus);
|
||||||
|
return circuit;
|
||||||
|
}
|
||||||
|
|||||||
22
src/main/angular/src/app/editor/circuit/DEMO_003.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {Circuit} from "./Circuit";
|
||||||
|
|
||||||
|
export const DEMO_003 = create();
|
||||||
|
|
||||||
|
function create() {
|
||||||
|
const circuit = new Circuit("DEMO_003", "3. Relais");
|
||||||
|
const batteryLight = circuit.newBattery(2, 0, "Licht Batterie");
|
||||||
|
const lightInactive = circuit.newLight(2, 1, "Licht Inaktiv");
|
||||||
|
const lightActive = circuit.newLight(3, 2, "Licht Aktiv");
|
||||||
|
const relay = circuit.newRelay(1, 2, "Relais");
|
||||||
|
const relaySwitch = circuit.newSwitch(0, 3, "Taster", true);
|
||||||
|
const batteryRelay = circuit.newBattery(1, 4, "Relais Batterie");
|
||||||
|
circuit.connect(batteryLight.plus, lightActive.b);
|
||||||
|
circuit.connect(batteryLight.plus, lightInactive.b);
|
||||||
|
circuit.connect(batteryLight.minus, relay.common);
|
||||||
|
circuit.connect(lightInactive.a, relay.inactive);
|
||||||
|
circuit.connect(lightActive.a, relay.active);
|
||||||
|
circuit.connect(batteryRelay.minus, relaySwitch.common);
|
||||||
|
circuit.connect(relaySwitch.active, relay.coilA);
|
||||||
|
circuit.connect(batteryRelay.plus, relay.coilB);
|
||||||
|
return circuit;
|
||||||
|
}
|
||||||
@ -1,7 +1,5 @@
|
|||||||
import {Point} from '../Point';
|
import {Point} from '../Point';
|
||||||
import {Part, RASTER} from '../parts/Part';
|
import {Part, RASTER} from '../parts/Part';
|
||||||
import {Battery} from '../parts/battery/Battery';
|
|
||||||
import {Light} from '../parts/light/Light';
|
|
||||||
import {Circuit} from './Circuit';
|
import {Circuit} from './Circuit';
|
||||||
|
|
||||||
export class Parts {
|
export class Parts {
|
||||||
@ -41,29 +39,4 @@ export class Parts {
|
|||||||
this.dragCursor = Point.fromEvent($event);
|
this.dragCursor = Point.fromEvent($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattery(rasterX: number, rasterY: number, voltage: number): Battery {
|
|
||||||
return this.add(new Battery(rasterX, rasterY, this.generateName("Batterie"), voltage, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
newLight(rasterX: number, rasterY: number): Light {
|
|
||||||
return this.add(new Light(rasterX, rasterY, this.generateName("Licht"), 3, 150));
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateName(baseName: string) {
|
|
||||||
let counter = 1;
|
|
||||||
let name: string;
|
|
||||||
while (true) {
|
|
||||||
name = `${baseName} #${counter++}`;
|
|
||||||
if (!this.circuit.parts.some(p => p.name === name)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private add<T extends Part>(part: T): T {
|
|
||||||
this.circuit.parts.push(part);
|
|
||||||
return part;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import {Point} from '../Point';
|
import {Point} from '../Point';
|
||||||
import {Junction} from '../junction/Junction';
|
import {Junction} from '../junction/Junction';
|
||||||
import {Wire} from '../wire/Wire';
|
|
||||||
import {MessageService} from '../message/message.service';
|
import {MessageService} from '../message/message.service';
|
||||||
|
|
||||||
import {Circuit} from './Circuit';
|
import {Circuit} from './Circuit';
|
||||||
@ -68,7 +67,15 @@ export class Wires {
|
|||||||
this.updateDragCursor($event);
|
this.updateDragCursor($event);
|
||||||
if ($event.button === 0) {
|
if ($event.button === 0) {
|
||||||
if (this.dragStartJunction !== null && this.dragEndJunction !== null) {
|
if (this.dragStartJunction !== null && this.dragEndJunction !== null) {
|
||||||
this.connect(this.dragStartJunction, this.dragEndJunction);
|
if (this.dragStartJunction !== this.dragEndJunction) {
|
||||||
|
if (!this.dragEndDuplicate) {
|
||||||
|
this.circuit.connect(this.dragStartJunction, this.dragEndJunction);
|
||||||
|
} else {
|
||||||
|
this.messageService.warn("Diese Verbindung existiert bereits.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Not connecting junction with itself.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.dragReset();
|
this.dragReset();
|
||||||
}
|
}
|
||||||
@ -90,27 +97,4 @@ export class Wires {
|
|||||||
this.dragEndDuplicate = false;
|
this.dragEndDuplicate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(start: Junction, end: Junction) {
|
|
||||||
if (start === end) {
|
|
||||||
console.log("Not connecting junction with itself.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.dragEndDuplicate) {
|
|
||||||
this.messageService.warn("Diese Verbindung existiert bereits.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const wire = new Wire(start, end, 0);
|
|
||||||
this.circuit.wires.push(wire);
|
|
||||||
console.log(`Wire connected: ${wire}`);
|
|
||||||
this.circuit.calculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(wire: Wire) {
|
|
||||||
this.circuit.wires.splice(this.circuit.wires.indexOf(wire), 1);
|
|
||||||
wire.start.wires.splice(wire.start.wires.indexOf(wire), 1);
|
|
||||||
wire.end.wires.splice(wire.end.wires.indexOf(wire), 1);
|
|
||||||
console.log("Wire disconnected: ", wire);
|
|
||||||
this.circuit.calculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,31 @@
|
|||||||
.circuit {
|
.circuit {
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
.wireBack {
|
||||||
user-select: none;
|
stroke-linecap: round;
|
||||||
|
stroke-width: 11px;
|
||||||
.wire {
|
stroke: black;
|
||||||
stroke-width: 9px;
|
pointer-events: none;
|
||||||
stroke-linecap: round;
|
}
|
||||||
pointer-events: none;
|
|
||||||
}
|
.wire {
|
||||||
|
stroke-width: 9px;
|
||||||
.wireBack {
|
stroke-dasharray: 0 20;
|
||||||
stroke-width: 11px;
|
stroke-linecap: round;
|
||||||
stroke: black;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wireOpen {
|
.wireOpen {
|
||||||
stroke: blue;
|
stroke: blue;
|
||||||
stroke-dasharray: 5px 15px;
|
stroke-dasharray: 5px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wireEnd {
|
.wireEnd {
|
||||||
stroke: green;
|
stroke: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wireDuplicate {
|
.wireDuplicate {
|
||||||
stroke: red;
|
stroke: red;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,27 +2,7 @@
|
|||||||
|
|
||||||
<g inner-part *ngFor="let part of circuit.parts" [part]="part" [parts]="parts" [wires]="wires"></g>
|
<g inner-part *ngFor="let part of circuit.parts" [part]="part" [parts]="parts" [wires]="wires"></g>
|
||||||
|
|
||||||
<ng-container *ngFor="let wire of circuit.wires">
|
<g inner-wire *ngFor="let wire of circuit.wires" [globalWire]="wire"></g>
|
||||||
<line
|
|
||||||
class="wire wireBack"
|
|
||||||
[attr.x1]="wire.start.pixelX + 'px'"
|
|
||||||
[attr.y1]="wire.start.pixelY + 'px'"
|
|
||||||
[attr.x2]="wire.end.pixelX + 'px'"
|
|
||||||
[attr.y2]="wire.end.pixelY + 'px'"
|
|
||||||
></line>
|
|
||||||
<line
|
|
||||||
class="wire"
|
|
||||||
[attr.stroke]="voltageColor(wire)"
|
|
||||||
[attr.x1]="wire.start.pixelX + 'px'"
|
|
||||||
[attr.y1]="wire.start.pixelY + 'px'"
|
|
||||||
[attr.x2]="wire.end.pixelX + 'px'"
|
|
||||||
[attr.y2]="wire.end.pixelY + 'px'"
|
|
||||||
stroke-dasharray="1, 15"
|
|
||||||
>
|
|
||||||
<animate *ngIf="wire.current < 0" attributeName="stroke-dashoffset" from="0" to="16" [attr.dur]="(1 / wire.absCurrent) / 200" repeatCount="indefinite"/>
|
|
||||||
<animate *ngIf="wire.current > 0" attributeName="stroke-dashoffset" from="16" to="0" [attr.dur]="(1 / wire.absCurrent) / 200" repeatCount="indefinite"/>
|
|
||||||
</line>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="wires.dragStart && wires.dragCursor && wires.dragStartJunction">
|
<ng-container *ngIf="wires.dragStart && wires.dragCursor && wires.dragStartJunction">
|
||||||
<ng-container *ngIf="wires.dragEnd && wires.dragEndJunction">
|
<ng-container *ngIf="wires.dragEnd && wires.dragEndJunction">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.1 KiB |
@ -4,10 +4,9 @@ import {MessageService} from '../message/message.service';
|
|||||||
import {Wires} from './Wires';
|
import {Wires} from './Wires';
|
||||||
import {Parts} from './Parts';
|
import {Parts} from './Parts';
|
||||||
import {PartComponent} from '../parts/part.component';
|
import {PartComponent} from '../parts/part.component';
|
||||||
import {Wire} from '../wire/Wire';
|
|
||||||
import {fadeColor} from '../colorHelpers';
|
|
||||||
|
|
||||||
import {Circuit} from './Circuit';
|
import {Circuit} from './Circuit';
|
||||||
|
import {WireComponent} from '../part-wire/wire.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-circuit',
|
selector: 'app-circuit',
|
||||||
@ -15,6 +14,7 @@ import {Circuit} from './Circuit';
|
|||||||
NgForOf,
|
NgForOf,
|
||||||
NgIf,
|
NgIf,
|
||||||
PartComponent,
|
PartComponent,
|
||||||
|
WireComponent,
|
||||||
],
|
],
|
||||||
templateUrl: './circuit.component.svg',
|
templateUrl: './circuit.component.svg',
|
||||||
styleUrl: './circuit.component.less'
|
styleUrl: './circuit.component.less'
|
||||||
@ -55,15 +55,4 @@ export class CircuitComponent {
|
|||||||
this.parts.mouseUp($event);
|
this.parts.mouseUp($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
voltageColor(wire: Wire) {
|
|
||||||
if (wire.start.voltage === null || wire.start.minCircuitVoltage === null || wire.start.maxCircuitVoltage === null) {
|
|
||||||
return 'gray';
|
|
||||||
}
|
|
||||||
const ratio = (wire.start.voltage - wire.start.minCircuitVoltage) / (wire.start.maxCircuitVoltage - wire.start.minCircuitVoltage);
|
|
||||||
if (ratio < 0.5) {
|
|
||||||
return fadeColor(ratio * 2, '#008cff', 'magenta');
|
|
||||||
}
|
|
||||||
return fadeColor((ratio - 0.5) * 2, 'magenta', 'red');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
<app-circuit [circuit]="circuit"></app-circuit>
|
<div class="layout">
|
||||||
|
|
||||||
|
<div class="menubar">
|
||||||
|
<app-menubar [current]="circuit" (load)="circuit = $event"></app-menubar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="circuit">
|
||||||
|
<app-circuit [circuit]="circuit"></app-circuit>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-messages></app-messages>
|
<app-messages></app-messages>
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
.layout {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.menubar {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.circuit {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,14 +1,16 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {CircuitComponent} from './circuit/circuit.component';
|
import {CircuitComponent} from './circuit/circuit.component';
|
||||||
import {MessagesComponent} from './message/messages/messages.component';
|
import {MessagesComponent} from './message/messages/messages.component';
|
||||||
|
import {MenubarComponent} from './menubar/menubar.component';
|
||||||
import {Circuit} from './circuit/Circuit';
|
import {Circuit} from './circuit/Circuit';
|
||||||
import {DEMO_002} from './circuit/DEMO_002';
|
import {DEMO_003} from './circuit/DEMO_003';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-editor',
|
selector: 'app-editor',
|
||||||
imports: [
|
imports: [
|
||||||
CircuitComponent,
|
CircuitComponent,
|
||||||
MessagesComponent
|
MessagesComponent,
|
||||||
|
MenubarComponent
|
||||||
],
|
],
|
||||||
templateUrl: './editor.component.html',
|
templateUrl: './editor.component.html',
|
||||||
styleUrl: './editor.component.less'
|
styleUrl: './editor.component.less'
|
||||||
@ -18,7 +20,7 @@ export class EditorComponent implements OnInit {
|
|||||||
private _circuit: Circuit = Circuit.new();
|
private _circuit: Circuit = Circuit.new();
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.circuit = DEMO_002;
|
this.circuit = DEMO_003;
|
||||||
}
|
}
|
||||||
|
|
||||||
set circuit(circuit: Circuit) {
|
set circuit(circuit: Circuit) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {Wire} from "../wire/Wire";
|
|||||||
import {Part} from '../parts/Part';
|
import {Part} from '../parts/Part';
|
||||||
import {Rect} from '../../Rect';
|
import {Rect} from '../../Rect';
|
||||||
import {selectTowardsRoot} from '../../selectTowardsRoot';
|
import {selectTowardsRoot} from '../../selectTowardsRoot';
|
||||||
|
import {fadeColor} from '../colorHelpers';
|
||||||
|
|
||||||
export const JUNCTION_RADIUS_PERCENT = 15;
|
export const JUNCTION_RADIUS_PERCENT = 15;
|
||||||
|
|
||||||
@ -9,9 +10,9 @@ export class Junction {
|
|||||||
|
|
||||||
readonly uuid: string = self.crypto.randomUUID();
|
readonly uuid: string = self.crypto.randomUUID();
|
||||||
|
|
||||||
readonly percentX: number;
|
readonly centerPercentX: number;
|
||||||
|
|
||||||
readonly percentY: number;
|
readonly centerPercentY: number;
|
||||||
|
|
||||||
readonly wires: Wire[] = [];
|
readonly wires: Wire[] = [];
|
||||||
|
|
||||||
@ -25,15 +26,15 @@ export class Junction {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly part: Part,
|
public readonly part: Part,
|
||||||
percentX: number,
|
public readonly percentX: number,
|
||||||
percentY: number,
|
public readonly percentY: number,
|
||||||
readonly name: string) {
|
readonly name: string) {
|
||||||
this.percentX = percentX - JUNCTION_RADIUS_PERCENT;
|
this.centerPercentX = percentX - JUNCTION_RADIUS_PERCENT;
|
||||||
this.percentY = percentY - JUNCTION_RADIUS_PERCENT;
|
this.centerPercentY = percentY - JUNCTION_RADIUS_PERCENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fullName(): string {
|
get fullName(): string {
|
||||||
return `'${this.part.name}' '${this.name}'`;
|
return `"${this.part.name} (Pin ${this.name})"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get pixelX(): number {
|
get pixelX(): number {
|
||||||
@ -77,7 +78,7 @@ export class Junction {
|
|||||||
private findRect(): Rect {
|
private findRect(): Rect {
|
||||||
const child = document.getElementById(this.uuid);
|
const child = document.getElementById(this.uuid);
|
||||||
if (!child) {
|
if (!child) {
|
||||||
throw Error(`No HTMLElement found for Junction: uuid=${this.uuid}, name=${this.name}`);
|
throw Error(`No HTMLElement found for Junction: name=${this.fullName}, uuid=${this.uuid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = selectTowardsRoot(child, "svg.circuit");
|
const parent = selectTowardsRoot(child, "svg.circuit");
|
||||||
@ -95,4 +96,19 @@ export class Junction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
voltageColor(): string {
|
||||||
|
if (this.voltage === null || this.minCircuitVoltage === null || this.maxCircuitVoltage === null) {
|
||||||
|
return 'lightgray';
|
||||||
|
}
|
||||||
|
const ratio = (this.voltage - this.minCircuitVoltage) / (this.maxCircuitVoltage - this.minCircuitVoltage);
|
||||||
|
if (ratio < 0.5) {
|
||||||
|
return fadeColor(ratio * 2, '#008cff', 'magenta');
|
||||||
|
}
|
||||||
|
return fadeColor((ratio - 0.5) * 2, 'magenta', 'red');
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCalculations() {
|
||||||
|
this.voltage = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<svg
|
<svg
|
||||||
class="junction"
|
class="junction"
|
||||||
[attr.x]="junction.percentX + '%'"
|
[attr.x]="junction.centerPercentX + '%'"
|
||||||
[attr.y]="junction.percentY + '%'"
|
[attr.y]="junction.centerPercentY + '%'"
|
||||||
[attr.height]="junction.percentH + '%'"
|
[attr.height]="junction.percentH + '%'"
|
||||||
[attr.width]="junction.percentW + '%'"
|
[attr.width]="junction.percentW + '%'"
|
||||||
(mousedown)="wires.mouseDown(junction, $event)"
|
(mousedown)="wires.mouseDown(junction, $event)"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 539 B |
@ -0,0 +1,3 @@
|
|||||||
|
<div class="bar">
|
||||||
|
<div *ngFor="let circuit of circuits" class="item itemLeft" [class.active]="current === circuit" (click)="load.emit(circuit)">{{ circuit.name }}</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
.bar {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
background-color: lightgray;
|
||||||
|
|
||||||
|
.itemLeft {
|
||||||
|
float: left;
|
||||||
|
border-right: 1px solid black;
|
||||||
|
padding: 0.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: palegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
26
src/main/angular/src/app/editor/menubar/menubar.component.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
|
import {NgForOf} from '@angular/common';
|
||||||
|
import {Circuit} from '../circuit/Circuit';
|
||||||
|
import {DEMO_001} from '../circuit/DEMO_001';
|
||||||
|
import {DEMO_002} from '../circuit/DEMO_002';
|
||||||
|
import {DEMO_003} from '../circuit/DEMO_003';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-menubar',
|
||||||
|
imports: [
|
||||||
|
NgForOf
|
||||||
|
],
|
||||||
|
templateUrl: './menubar.component.html',
|
||||||
|
styleUrl: './menubar.component.less'
|
||||||
|
})
|
||||||
|
export class MenubarComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
current!: Circuit;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
load: EventEmitter<Circuit> = new EventEmitter();
|
||||||
|
|
||||||
|
circuits: Circuit[] = [DEMO_001, DEMO_002, DEMO_003];
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
@import "../parts/part.component.less";
|
||||||
|
|
||||||
|
.partWireBack {
|
||||||
|
stroke-width: 5px;
|
||||||
|
}
|
||||||
11
src/main/angular/src/app/editor/part-wire/wire.component.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<svg width="100%" height="100%">
|
||||||
|
|
||||||
|
<line class="wireBack {{extraClassesBack}}" [attr.x1]="x1" [attr.y1]="y1" [attr.x2]="x2" [attr.y2]="y2">
|
||||||
|
<!-- nothing -->
|
||||||
|
</line>
|
||||||
|
|
||||||
|
<line class="wire" [attr.stroke]="color" [attr.x1]="x1" [attr.y1]="y1" [attr.x2]="x2" [attr.y2]="y2">
|
||||||
|
<animate *ngIf="current" attributeName="stroke-dashoffset" [attr.from]="animateFrom" [attr.to]="animateTo" [attr.dur]="animateDuration" repeatCount="indefinite"/>
|
||||||
|
</line>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 462 B |
172
src/main/angular/src/app/editor/part-wire/wire.component.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {ANIMATION_ELECTRON_STEPS, Wire} from '../wire/Wire';
|
||||||
|
import {Junction} from '../junction/Junction';
|
||||||
|
import {fadeColor} from '../colorHelpers';
|
||||||
|
import {NgIf} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'g [inner-wire]',
|
||||||
|
imports: [
|
||||||
|
NgIf
|
||||||
|
],
|
||||||
|
templateUrl: './wire.component.svg',
|
||||||
|
styleUrl: './wire.component.less'
|
||||||
|
})
|
||||||
|
export class WireComponent {
|
||||||
|
|
||||||
|
protected wire: Wire | null = null;
|
||||||
|
|
||||||
|
protected isWirePart: boolean = false;
|
||||||
|
|
||||||
|
protected fromJ: Junction | null = null;
|
||||||
|
|
||||||
|
protected toJ: Junction | null = null;
|
||||||
|
|
||||||
|
private _manualCurrent_: number = 0;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
extraClassesBack: string = "";
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set partWire(wire: Wire) {
|
||||||
|
this.wire = wire;
|
||||||
|
this.isWirePart = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set globalWire(wire: Wire) {
|
||||||
|
this.wire = wire;
|
||||||
|
this.isWirePart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set fromJunction(junction: Junction) {
|
||||||
|
this.fromJ = junction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set toJunction(junction: Junction) {
|
||||||
|
this.toJ = junction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fromX = "";
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fromY = "";
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
toX = "";
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
toY = "";
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set current(current: number) {
|
||||||
|
this._manualCurrent_ = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
get current(): number {
|
||||||
|
if (this.wire) {
|
||||||
|
return this.wire.current;
|
||||||
|
}
|
||||||
|
return this._manualCurrent_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get voltage(): number | null {
|
||||||
|
if (this.wire) {
|
||||||
|
if (this.wire.start.voltage === null || this.wire.end.voltage === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (this.wire.start.voltage + this.wire.end.voltage) / 2;
|
||||||
|
} else if (this.fromJ) {
|
||||||
|
return this.fromJ.voltage;
|
||||||
|
} else if (this.toJ) {
|
||||||
|
return this.toJ.voltage;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get x1(): string {
|
||||||
|
if (this.wire) {
|
||||||
|
if (this.isWirePart) {
|
||||||
|
return this.wire.start.percentX + '%';
|
||||||
|
} else {
|
||||||
|
return this.wire.start.pixelX + 'px';
|
||||||
|
}
|
||||||
|
} else if (this.fromJ) {
|
||||||
|
return this.fromJ.percentX + '%';
|
||||||
|
}
|
||||||
|
return this.fromX;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get y1(): string {
|
||||||
|
if (this.wire) {
|
||||||
|
if (this.isWirePart) {
|
||||||
|
return this.wire.start.percentY + '%';
|
||||||
|
} else {
|
||||||
|
return this.wire.start.pixelY + 'px';
|
||||||
|
}
|
||||||
|
} else if (this.fromJ) {
|
||||||
|
return this.fromJ.percentY + '%';
|
||||||
|
}
|
||||||
|
return this.fromY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get x2(): string {
|
||||||
|
if (this.wire) {
|
||||||
|
if (this.isWirePart) {
|
||||||
|
return this.wire.end.percentX + '%';
|
||||||
|
} else {
|
||||||
|
return this.wire.end.pixelX + 'px';
|
||||||
|
}
|
||||||
|
} else if (this.toJ) {
|
||||||
|
return this.toJ.percentX + '%';
|
||||||
|
}
|
||||||
|
return this.toX;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get y2(): string {
|
||||||
|
if (this.wire) {
|
||||||
|
if (this.isWirePart) {
|
||||||
|
return this.wire.end.percentY + '%';
|
||||||
|
} else {
|
||||||
|
return this.wire.end.pixelY + 'px';
|
||||||
|
}
|
||||||
|
} else if (this.toJ) {
|
||||||
|
return this.toJ.percentY + '%';
|
||||||
|
}
|
||||||
|
return this.toY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get animateFrom(): number {
|
||||||
|
return this.current < 0 ? 0 : ANIMATION_ELECTRON_STEPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get animateTo(): number {
|
||||||
|
return this.current < 0 ? ANIMATION_ELECTRON_STEPS : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get animateDuration(): number {
|
||||||
|
return Math.max(0.05, (1 / Math.abs(this.current)) / 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get color(): string {
|
||||||
|
const voltage = this.voltage;
|
||||||
|
const junction: Junction | null = this.wire ? this.wire.start : this.fromJ ? this.fromJ : this.toJ;
|
||||||
|
if (!junction) {
|
||||||
|
throw new Error("Cannot calculate color if no voltage (or junction) given.");
|
||||||
|
}
|
||||||
|
const vMin = junction.minCircuitVoltage;
|
||||||
|
const vMax = junction.maxCircuitVoltage;
|
||||||
|
if (voltage === null || vMin === null || vMax === null) {
|
||||||
|
return "lightgray";
|
||||||
|
}
|
||||||
|
const ratio = (voltage - vMin) / (vMax - vMin);
|
||||||
|
if (ratio < 0.5) {
|
||||||
|
return fadeColor(ratio * 2, '#008cff', 'magenta');
|
||||||
|
}
|
||||||
|
return fadeColor((ratio - 0.5) * 2, 'magenta', 'red');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import {PartType} from './PartType';
|
|
||||||
import {Junction} from '../junction/Junction';
|
import {Junction} from '../junction/Junction';
|
||||||
|
import {Circuit} from '../circuit/Circuit';
|
||||||
|
import {PartType} from './PartType';
|
||||||
|
|
||||||
export const RASTER = 50;
|
export const RASTER = 50;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ export abstract class Part {
|
|||||||
private readonly _h: number;
|
private readonly _h: number;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
|
readonly circuit: Circuit,
|
||||||
readonly type: PartType,
|
readonly type: PartType,
|
||||||
readonly name: string,
|
readonly name: string,
|
||||||
rasterX: number,
|
rasterX: number,
|
||||||
@ -29,8 +31,6 @@ export abstract class Part {
|
|||||||
this._h = rasterH * 3 * RASTER;
|
this._h = rasterH * 3 * RASTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract get junctions(): Junction[];
|
|
||||||
|
|
||||||
get x(): number {
|
get x(): number {
|
||||||
return this._x;
|
return this._x;
|
||||||
}
|
}
|
||||||
@ -63,4 +63,27 @@ export abstract class Part {
|
|||||||
this.junctions.forEach(junction => junction.updatePosition());
|
this.junctions.forEach(junction => junction.updatePosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loop(): boolean {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
resetCalculations() {
|
||||||
|
this.junctions.forEach(junction => junction.resetCalculations());
|
||||||
|
this.resetCalculations2();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCalculations2(): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseDown() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseUp() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract get junctions(): Junction[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
export enum PartType {
|
export enum PartType {
|
||||||
Battery = 'Battery',
|
Battery = 'Battery',
|
||||||
Light = 'Light',
|
Light = 'Light',
|
||||||
|
Relay = 'Relay',
|
||||||
|
Switch = 'Switch',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {Junction} from '../../junction/Junction';
|
|||||||
import {PartType} from '../PartType';
|
import {PartType} from '../PartType';
|
||||||
import {siPrefix} from '../../siPrefix';
|
import {siPrefix} from '../../siPrefix';
|
||||||
import {Wire} from '../../wire/Wire';
|
import {Wire} from '../../wire/Wire';
|
||||||
|
import {Circuit} from '../../circuit/Circuit';
|
||||||
|
|
||||||
export class Battery extends Part {
|
export class Battery extends Part {
|
||||||
|
|
||||||
@ -11,13 +12,14 @@ export class Battery extends Part {
|
|||||||
readonly plus: Junction = new Junction(this, 85, 50, "+");
|
readonly plus: Junction = new Junction(this, 85, 50, "+");
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
circuit: Circuit,
|
||||||
rasterX: number,
|
rasterX: number,
|
||||||
rasterY: number,
|
rasterY: number,
|
||||||
name: string,
|
name: string,
|
||||||
public voltage: number,
|
public voltage: number,
|
||||||
public resistance: number,
|
public resistance: number,
|
||||||
) {
|
) {
|
||||||
super(PartType.Battery, name, rasterX, rasterY);
|
super(circuit, PartType.Battery, name, rasterX, rasterY);
|
||||||
new Wire(this.minus, this.plus, resistance, "Innenwiderstand");
|
new Wire(this.minus, this.plus, resistance, "Innenwiderstand");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ export class Battery extends Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get voltageStr(): string {
|
get voltageStr(): string {
|
||||||
return siPrefix(this.voltage, 'V', 2);
|
return siPrefix(Math.abs(this.voltage), 'V', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
get current(): number {
|
get current(): number {
|
||||||
@ -34,8 +36,7 @@ export class Battery extends Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get currentStr(): string {
|
get currentStr(): string {
|
||||||
const current = Math.abs(this.current) || 0;
|
return siPrefix(Math.abs(this.current) || 0, 'A', 2);
|
||||||
return siPrefix(current, 'A', 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<svg width="100%" height="100%">
|
<svg width="100%" height="100%">
|
||||||
<text dominant-baseline="hanging" text-anchor="middle" x="50%" y="3%">{{ battery.voltageStr }}</text>
|
<text dominant-baseline="hanging" text-anchor="middle" x="50%" y="3%">{{ battery.voltageStr }}</text>
|
||||||
<line class="partWire" id="wireMinus" x1="15%" x2="40%" y1="50%" y2="50%"></line>
|
<g inner-wire fromX="40%" fromY="50%" [toJunction]="battery.minus" [current]="battery.current" extraClassesBack="partWireBack"></g>
|
||||||
<line class="partWire" id="wirePlus" x1="55%" x2="80%" y1="50%" y2="50%"></line>
|
<g inner-wire [fromJunction]="battery.plus" toX="60%" toY="50%" [current]="battery.current" extraClassesBack="partWireBack"></g>
|
||||||
<rect height="30%" id="symbolMinus" width="10%" x="35%" y="35%"></rect>
|
<rect height="30%" id="symbolMinus" width="10%" x="35%" y="35%"></rect>
|
||||||
<rect height="60%" id="symbolPlus" width="5%" x="55%" y="20%"></rect>
|
<rect height="60%" id="symbolPlus" width="5%" x="55%" y="20%"></rect>
|
||||||
<text text-anchor="middle" x="50%" y="95%">{{ battery.currentStr }}</text>
|
<text text-anchor="middle" x="50%" y="95%">{{ battery.currentStr }}</text>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 632 B |
@ -1,9 +1,12 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {Battery} from './Battery';
|
import {Battery} from './Battery';
|
||||||
|
import {WireComponent} from '../../part-wire/wire.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'g[inner-part-battery]',
|
selector: 'g[inner-part-battery]',
|
||||||
imports: [],
|
imports: [
|
||||||
|
WireComponent
|
||||||
|
],
|
||||||
templateUrl: './battery.component.svg',
|
templateUrl: './battery.component.svg',
|
||||||
styleUrl: './battery.component.less',
|
styleUrl: './battery.component.less',
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {siPrefix} from '../../siPrefix';
|
|||||||
|
|
||||||
import {fadeColor} from '../../colorHelpers';
|
import {fadeColor} from '../../colorHelpers';
|
||||||
import {Wire} from '../../wire/Wire';
|
import {Wire} from '../../wire/Wire';
|
||||||
|
import {Circuit} from '../../circuit/Circuit';
|
||||||
|
|
||||||
export class Light extends Part {
|
export class Light extends Part {
|
||||||
|
|
||||||
@ -13,13 +14,14 @@ export class Light extends Part {
|
|||||||
readonly b = new Junction(this, 85, 50, "B");
|
readonly b = new Junction(this, 85, 50, "B");
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
circuit: Circuit,
|
||||||
rasterX: number,
|
rasterX: number,
|
||||||
rasterY: number,
|
rasterY: number,
|
||||||
name: string,
|
name: string,
|
||||||
public voltageMax: number,
|
public voltageMax: number,
|
||||||
public resistance: number,
|
public resistance: number,
|
||||||
) {
|
) {
|
||||||
super(PartType.Light, name, rasterX, rasterY);
|
super(circuit, PartType.Light, name, rasterX, rasterY);
|
||||||
new Wire(this.a, this.b, resistance, "Glühdraht");
|
new Wire(this.a, this.b, resistance, "Glühdraht");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ export class Light extends Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get voltageStr(): string {
|
get voltageStr(): string {
|
||||||
return siPrefix(this.voltage, 'V', 2);
|
return siPrefix(Math.abs(this.voltage), 'V', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
get current(): number {
|
get current(): number {
|
||||||
@ -50,15 +52,14 @@ export class Light extends Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get currentStr(): string {
|
get currentStr(): string {
|
||||||
const current = Math.abs(this.current) || 0;
|
return siPrefix(Math.abs(this.current) || 0, 'A', 2);
|
||||||
return siPrefix(current, 'A', 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fill(): string {
|
fill(): string {
|
||||||
if (this.defect) {
|
if (this.defect) {
|
||||||
return "#6e4122"
|
return "#6e4122"
|
||||||
}
|
}
|
||||||
return fadeColor(this.voltage / this.voltageMax, "#888", "#FF0");
|
return fadeColor(Math.abs(this.voltage) / this.voltageMax, "#888", "#FF0");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
@import "../circuit/circuit.component.less";
|
||||||
|
|
||||||
.part {
|
.part {
|
||||||
border-radius: 0.51em;
|
border-radius: 0.51em;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@ -18,7 +20,6 @@
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.partWire {
|
.partWireBack {
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
stroke: black;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,16 @@
|
|||||||
(mousedown)="parts.mouseDown(part, $event)"
|
(mousedown)="parts.mouseDown(part, $event)"
|
||||||
>
|
>
|
||||||
|
|
||||||
<rect class="background" height="100%" width="100%" x="0" y="0"></rect>
|
<rect class="background" height="100%" width="100%" x="0" y="0" (mousedown)="part.mouseDown()" (mouseup)="part.mouseUp()"></rect>
|
||||||
|
|
||||||
<g inner-part-battery *ngIf="isBattery(part)" [battery]="asBattery(part)"></g>
|
<g inner-part-battery *ngIf="isBattery(part)" [battery]="asBattery(part)"></g>
|
||||||
|
|
||||||
<g inner-part-light *ngIf="isLight(part)" [light]="asLight(part)"></g>
|
<g inner-part-light *ngIf="isLight(part)" [light]="asLight(part)"></g>
|
||||||
|
|
||||||
|
<g inner-part-relay *ngIf="isRelay(part)" [relay]="asRelay(part)"></g>
|
||||||
|
|
||||||
|
<g inner-part-switch *ngIf="isSwitch(part)" [switch]="asSwitch(part)"></g>
|
||||||
|
|
||||||
<g inner-junction *ngFor="let junction of part.junctions" [junction]="junction" [wires]="wires"></g>
|
<g inner-junction *ngFor="let junction of part.junctions" [junction]="junction" [wires]="wires"></g>
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 831 B |
@ -8,6 +8,10 @@ import {Parts} from '../circuit/Parts';
|
|||||||
import {Wires} from '../circuit/Wires';
|
import {Wires} from '../circuit/Wires';
|
||||||
import {LightComponent} from './light/light.component';
|
import {LightComponent} from './light/light.component';
|
||||||
import {JunctionComponent} from '../junction/junction.component';
|
import {JunctionComponent} from '../junction/junction.component';
|
||||||
|
import {Relay} from './relay/Relay';
|
||||||
|
import {RelayComponent} from './relay/relay.component';
|
||||||
|
import {SwitchComponent} from './switch/switch.component';
|
||||||
|
import {Switch} from './switch/Switch';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'g[inner-part]',
|
selector: 'g[inner-part]',
|
||||||
@ -16,7 +20,9 @@ import {JunctionComponent} from '../junction/junction.component';
|
|||||||
NgIf,
|
NgIf,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
LightComponent,
|
LightComponent,
|
||||||
JunctionComponent
|
JunctionComponent,
|
||||||
|
RelayComponent,
|
||||||
|
SwitchComponent
|
||||||
],
|
],
|
||||||
templateUrl: './part.component.svg',
|
templateUrl: './part.component.svg',
|
||||||
styleUrl: './part.component.less',
|
styleUrl: './part.component.less',
|
||||||
@ -48,4 +54,20 @@ export class PartComponent {
|
|||||||
return part instanceof Light;
|
return part instanceof Light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asRelay(part: Part): Relay {
|
||||||
|
return part as Relay;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRelay(part: Part): boolean {
|
||||||
|
return part instanceof Relay;
|
||||||
|
}
|
||||||
|
|
||||||
|
asSwitch(part: Part): Switch {
|
||||||
|
return part as Switch;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSwitch(part: Part): boolean {
|
||||||
|
return part instanceof Switch;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
92
src/main/angular/src/app/editor/parts/relay/Relay.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import {Part} from "../Part";
|
||||||
|
import {Junction} from '../../junction/Junction';
|
||||||
|
import {PartType} from '../PartType';
|
||||||
|
import {siPrefix} from '../../siPrefix';
|
||||||
|
import {Wire} from '../../wire/Wire';
|
||||||
|
import {RESISTANCE_MIN} from '../../circuit/Calculation';
|
||||||
|
import {Circuit} from '../../circuit/Circuit';
|
||||||
|
|
||||||
|
export class Relay extends Part {
|
||||||
|
|
||||||
|
readonly common = new Junction(this, 15, 50, "COM");
|
||||||
|
|
||||||
|
readonly active = new Junction(this, 85, 50, "NO");
|
||||||
|
|
||||||
|
readonly inactive = new Junction(this, 85, 15, "NC");
|
||||||
|
|
||||||
|
readonly coilA = new Junction(this, 15, 85, "A");
|
||||||
|
|
||||||
|
readonly coilB = new Junction(this, 85, 85, "B");
|
||||||
|
|
||||||
|
readonly coil: Wire;
|
||||||
|
|
||||||
|
contact: Wire = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt");
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
circuit: Circuit,
|
||||||
|
rasterX: number,
|
||||||
|
rasterY: number,
|
||||||
|
name: string,
|
||||||
|
public voltageMin: number,
|
||||||
|
public voltageMax: number,
|
||||||
|
public resistance: number,
|
||||||
|
) {
|
||||||
|
super(circuit, PartType.Relay, name, rasterX, rasterY);
|
||||||
|
this.coil = new Wire(this.coilA, this.coilB, resistance, "Spule");
|
||||||
|
}
|
||||||
|
|
||||||
|
override get junctions(): Junction[] {
|
||||||
|
return [this.coilA, this.coilB, this.common, this.active, this.inactive];
|
||||||
|
}
|
||||||
|
|
||||||
|
get voltage(): number {
|
||||||
|
if (this.coilA.voltage == null || this.coilB.voltage == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return this.coilB.voltage - this.coilA.voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCoilActive(): boolean {
|
||||||
|
return this.voltage >= this.voltageMin && !this.defect;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defect(): boolean {
|
||||||
|
return this.voltage > this.voltageMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
get voltageStr(): string {
|
||||||
|
return siPrefix(Math.abs(this.voltage), 'V', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get current(): number {
|
||||||
|
if (this.coilA.voltage === null || this.coilB.voltage === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (this.coilB.voltage - this.coilA.voltage) / this.resistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentStr(): string {
|
||||||
|
return siPrefix(Math.abs(this.current) || 0, 'A', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
override loop(): boolean {
|
||||||
|
const shouldBeActive = this.isCoilActive;
|
||||||
|
const isActive = this.contact.end === this.active;
|
||||||
|
if (isActive != shouldBeActive) {
|
||||||
|
this.contact.disconnect();
|
||||||
|
if (shouldBeActive) {
|
||||||
|
this.contact = new Wire(this.common, this.active, RESISTANCE_MIN, "Schaltkontakt Aktiv");
|
||||||
|
} else {
|
||||||
|
this.contact = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt Inaktiv");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override resetCalculations2() {
|
||||||
|
this.contact.resetCalculations();
|
||||||
|
this.coil.resetCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
@import "../part.component.less";
|
||||||
|
|
||||||
|
.defect {
|
||||||
|
filter: drop-shadow(0 0 30px black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mechanic {
|
||||||
|
stroke-width: 3px;
|
||||||
|
stroke: dimgray;
|
||||||
|
stroke-dasharray: 5 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coil {
|
||||||
|
stroke: black;
|
||||||
|
stroke-width: 1px;
|
||||||
|
fill: darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coilActive {
|
||||||
|
fill: dodgerblue;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<svg width="100%" height="100%">
|
||||||
|
|
||||||
|
<text dominant-baseline="hanging" x="25%" y="3%">{{ relay.voltageStr }}</text>
|
||||||
|
<text dominant-baseline="hanging" x="25%" y="15%">{{ relay.currentStr }}</text>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!relay.isCoilActive">
|
||||||
|
<line class="mechanic"
|
||||||
|
[attr.x1]="50 + '%'"
|
||||||
|
[attr.y1]="relay.common.percentY + 8 - 20 + '%'"
|
||||||
|
[attr.x2]="50 + '%'"
|
||||||
|
[attr.y2]="65 + '%'"
|
||||||
|
></line>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="relay.isCoilActive">
|
||||||
|
<line class="mechanic"
|
||||||
|
[attr.x1]="50 + '%'"
|
||||||
|
[attr.y1]="relay.common.percentY + '%'"
|
||||||
|
[attr.x2]="50 + '%'"
|
||||||
|
[attr.y2]="65 + '%'"
|
||||||
|
></line>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<g inner-wire [partWire]="relay.contact"></g>
|
||||||
|
|
||||||
|
<g inner-wire [partWire]="relay.coil" extraClassesBack="partWireBack"></g>
|
||||||
|
|
||||||
|
<rect class="coil" [class.coilActive]="relay.isCoilActive" [class.defect]="relay.defect" x="40%" y="65%" width="20%" height="30%"></rect>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 975 B |
@ -0,0 +1,20 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {Relay} from './Relay';
|
||||||
|
import {NgIf} from '@angular/common';
|
||||||
|
import {WireComponent} from '../../part-wire/wire.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'g[inner-part-relay]',
|
||||||
|
imports: [
|
||||||
|
NgIf,
|
||||||
|
WireComponent
|
||||||
|
],
|
||||||
|
templateUrl: './relay.component.svg',
|
||||||
|
styleUrl: './relay.component.less',
|
||||||
|
})
|
||||||
|
export class RelayComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
relay!: Relay;
|
||||||
|
|
||||||
|
}
|
||||||
66
src/main/angular/src/app/editor/parts/switch/Switch.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {Part} from "../Part";
|
||||||
|
import {Junction} from '../../junction/Junction';
|
||||||
|
import {PartType} from '../PartType';
|
||||||
|
import {Wire} from '../../wire/Wire';
|
||||||
|
import {RESISTANCE_MIN} from '../../circuit/Calculation';
|
||||||
|
import {Circuit} from '../../circuit/Circuit';
|
||||||
|
|
||||||
|
export class Switch extends Part {
|
||||||
|
|
||||||
|
readonly common = new Junction(this, 15, 50, "COM");
|
||||||
|
|
||||||
|
readonly active = new Junction(this, 85, 50, "NO");
|
||||||
|
|
||||||
|
readonly inactive = new Junction(this, 85, 15, "NC");
|
||||||
|
|
||||||
|
contact: Wire = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt");
|
||||||
|
|
||||||
|
isActive: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
circuit: Circuit,
|
||||||
|
rasterX: number,
|
||||||
|
rasterY: number,
|
||||||
|
name: string,
|
||||||
|
readonly momentary: boolean,
|
||||||
|
) {
|
||||||
|
super(circuit, PartType.Switch, name, rasterX, rasterY);
|
||||||
|
}
|
||||||
|
|
||||||
|
override get junctions(): Junction[] {
|
||||||
|
return [this.common, this.active, this.inactive];
|
||||||
|
}
|
||||||
|
|
||||||
|
override resetCalculations2() {
|
||||||
|
this.contact.resetCalculations();
|
||||||
|
}
|
||||||
|
|
||||||
|
override mouseDown() {
|
||||||
|
if (this.momentary) {
|
||||||
|
this.setIsActive(true);
|
||||||
|
} else {
|
||||||
|
this.setIsActive(!this.isActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override mouseUp() {
|
||||||
|
if (this.momentary) {
|
||||||
|
this.setIsActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsActive(isActive: boolean) {
|
||||||
|
if (this.isActive === isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isActive = isActive;
|
||||||
|
this.contact.disconnect();
|
||||||
|
if (this.isActive) {
|
||||||
|
this.contact = new Wire(this.common, this.active, RESISTANCE_MIN, "Schaltkontakt (Aktiv)");
|
||||||
|
} else {
|
||||||
|
this.contact = new Wire(this.common, this.inactive, RESISTANCE_MIN, "Schaltkontakt (Inaktiv)");
|
||||||
|
}
|
||||||
|
this.circuit.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
@import "../part.component.less";
|
||||||
|
|
||||||
|
.defect {
|
||||||
|
filter: drop-shadow(0 0 30px black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mechanic {
|
||||||
|
stroke-width: 3px;
|
||||||
|
stroke: dimgray;
|
||||||
|
stroke-dasharray: 5 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coil {
|
||||||
|
stroke: black;
|
||||||
|
stroke-width: 1px;
|
||||||
|
fill: darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coilActive {
|
||||||
|
fill: dodgerblue;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="100%" height="100%">
|
||||||
|
<g inner-wire [partWire]="switch.contact"></g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 89 B |
@ -0,0 +1,18 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {Switch} from './Switch';
|
||||||
|
import {WireComponent} from '../../part-wire/wire.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'g[inner-part-switch]',
|
||||||
|
imports: [
|
||||||
|
WireComponent
|
||||||
|
],
|
||||||
|
templateUrl: './switch.component.svg',
|
||||||
|
styleUrl: './switch.component.less',
|
||||||
|
})
|
||||||
|
export class SwitchComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
switch!: Switch;
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,6 +4,8 @@ export function siPrefix(value: number, unit: string, minDigits: number): string
|
|||||||
if (value === 0) {
|
if (value === 0) {
|
||||||
return `0${unit}`;
|
return `0${unit}`;
|
||||||
}
|
}
|
||||||
|
const negative = value < 0;
|
||||||
|
value = negative ? -value : value;
|
||||||
const exp0 = Math.log10(value);
|
const exp0 = Math.log10(value);
|
||||||
const group = Math.floor(exp0 / 3);
|
const group = Math.floor(exp0 / 3);
|
||||||
const index = group + 5;
|
const index = group + 5;
|
||||||
@ -14,5 +16,5 @@ export function siPrefix(value: number, unit: string, minDigits: number): string
|
|||||||
const hasDigits = Math.floor(Math.log10(newValue)) + 1;
|
const hasDigits = Math.floor(Math.log10(newValue)) + 1;
|
||||||
const decimals = Math.max(0, minDigits - hasDigits);
|
const decimals = Math.max(0, minDigits - hasDigits);
|
||||||
const newValueStr2 = newValue.toFixed(decimals);
|
const newValueStr2 = newValue.toFixed(decimals);
|
||||||
return `${newValueStr2}${prefix}${unit}`;
|
return `${negative ? '-' : ''}${newValueStr2}${prefix}${unit}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import {Junction} from "../junction/Junction";
|
import {Junction} from "../junction/Junction";
|
||||||
import {RESISTANCE_MIN} from '../circuit/Calculation';
|
import {RESISTANCE_MIN} from '../circuit/Calculation';
|
||||||
|
import {fadeColor} from '../colorHelpers';
|
||||||
|
|
||||||
|
export const ANIMATION_ELECTRON_STEPS = 20;
|
||||||
|
|
||||||
export class Wire {
|
export class Wire {
|
||||||
|
|
||||||
@ -11,22 +14,23 @@ export class Wire {
|
|||||||
public resistance: number,
|
public resistance: number,
|
||||||
public name: string | null = null,
|
public name: string | null = null,
|
||||||
) {
|
) {
|
||||||
this.start.wires.push(this);
|
|
||||||
this.end.wires.push(this);
|
|
||||||
if (this.resistance === 0) {
|
if (this.resistance === 0) {
|
||||||
this.resistance = RESISTANCE_MIN;
|
this.resistance = RESISTANCE_MIN;
|
||||||
}
|
}
|
||||||
|
this.start.wires.push(this);
|
||||||
|
this.end.wires.push(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
get absCurrent(): number {
|
disconnect() {
|
||||||
return Math.abs(this.current);
|
this.start.wires.splice(this.start.wires.indexOf(this), 1);
|
||||||
|
this.end.wires.splice(this.end.wires.indexOf(this), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
if (this.start.part === this.end.part && this.name !== null) {
|
if (this.start.part === this.end.part && this.name !== null) {
|
||||||
return `'${this.start.part}' "${this.name}"`;
|
return `"${this.start.part}" "${this.name}"`;
|
||||||
}
|
}
|
||||||
return `${this.name !== null ? this.name + ' ' : ''}${this.start.fullName} ==> ${this.end.fullName}`;
|
return `${this.start.fullName} ==> ${this.end.fullName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(junction: Junction) {
|
traverse(junction: Junction) {
|
||||||
@ -39,4 +43,19 @@ export class Wire {
|
|||||||
throw new Error(`Wire is not connected to given Junction: wire=${this}, junction=${junction}`);
|
throw new Error(`Wire is not connected to given Junction: wire=${this}, junction=${junction}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCalculations() {
|
||||||
|
this.current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
voltageColor(): string {
|
||||||
|
if (this.start.voltage === null || this.end.voltage === null || this.start.minCircuitVoltage === null || this.start.maxCircuitVoltage === null) {
|
||||||
|
return 'lightgray';
|
||||||
|
}
|
||||||
|
const ratio = ((this.end.voltage + this.start.voltage) / 2 - this.start.minCircuitVoltage) / (this.start.maxCircuitVoltage - this.start.minCircuitVoltage);
|
||||||
|
if (ratio < 0.5) {
|
||||||
|
return fadeColor(ratio * 2, '#008cff', 'magenta');
|
||||||
|
}
|
||||||
|
return fadeColor((ratio - 0.5) * 2, 'magenta', 'red');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,13 @@ body {
|
|||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
div {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||