curves for wires & importing logic gates
This commit is contained in:
parent
443d049db6
commit
b06a4c7441
22 changed files with 1117 additions and 588 deletions
src/ts/common/componentManager
|
@ -1,38 +1,38 @@
|
|||
import { Singleton } from "@eix/utils";
|
||||
import { Component } from "../component";
|
||||
import { Subject, BehaviorSubject, fromEvent } from "rxjs";
|
||||
import { svg, SVGTemplateResult, html } from "lit-html";
|
||||
import { subscribe } from "lit-rx";
|
||||
import { Screen } from "../screen.ts";
|
||||
import { ManagerState, ComponentTemplate } from "./interfaces";
|
||||
import { Store } from "../store";
|
||||
import { KeyboardInput } from "@eix/input"
|
||||
import { success, error } from "toastr"
|
||||
import { ComponentTemplateStore } from "./componentTemplateStore";
|
||||
import { alertOptions } from "./alertOptions";
|
||||
import { WireManager } from "../wires";
|
||||
import { runCounter } from "../component/runCounter";
|
||||
import { Settings } from "../store/settings";
|
||||
import { download } from "./download";
|
||||
import { modal } from "../modals";
|
||||
import { map } from "rxjs/operators";
|
||||
import { persistent } from "../store/persistent";
|
||||
import { MDCTextField } from '@material/textfield';
|
||||
|
||||
const defaultName = "default"
|
||||
import { Singleton } from '@eix/utils'
|
||||
import { Component } from '../component'
|
||||
import { Subject, BehaviorSubject, fromEvent } from 'rxjs'
|
||||
import { svg, SVGTemplateResult, html } from 'lit-html'
|
||||
import { subscribe } from 'lit-rx'
|
||||
import { Screen } from '../screen.ts'
|
||||
import { ManagerState, ComponentTemplate } from './interfaces'
|
||||
import { Store } from '../store'
|
||||
import { KeyboardInput } from '@eix/input'
|
||||
import { success, error } from 'toastr'
|
||||
import { ComponentTemplateStore } from './componentTemplateStore'
|
||||
import { alertOptions } from './alertOptions'
|
||||
import { WireManager } from '../wires'
|
||||
import { runCounter } from '../component/runCounter'
|
||||
import { Settings } from '../store/settings'
|
||||
import { download } from './download'
|
||||
import { modal } from '../modals'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { persistent } from '../store/persistent'
|
||||
import { MDCTextField } from '@material/textfield'
|
||||
import { importComponent } from '../componentImporter/importComponent'
|
||||
|
||||
const defaultName = 'default'
|
||||
|
||||
@Singleton
|
||||
export class ComponentManager {
|
||||
public components: Component[] = []
|
||||
public svgs = new Subject<SVGTemplateResult>()
|
||||
public placeholder = new BehaviorSubject("Create simulation")
|
||||
public barAlpha = new BehaviorSubject<string>("0");
|
||||
public placeholder = new BehaviorSubject('Create simulation')
|
||||
public barAlpha = new BehaviorSubject<string>('0')
|
||||
public wireManager = new WireManager()
|
||||
public onTop: Component
|
||||
public templateStore = new ComponentTemplateStore()
|
||||
|
||||
private temporaryCommnad = ""
|
||||
private temporaryCommnad = ''
|
||||
private clicked = false
|
||||
private ignoreKeyDowns = false
|
||||
|
||||
|
@ -42,66 +42,79 @@ export class ComponentManager {
|
|||
offset: number
|
||||
scale: [number, number]
|
||||
} = {
|
||||
offset: 50,
|
||||
scale: [100, 100]
|
||||
}
|
||||
offset: 50,
|
||||
scale: [100, 100]
|
||||
}
|
||||
|
||||
private commandHistoryStore = new Store<string>("commandHistory")
|
||||
private store = new Store<ManagerState>("simulationStates")
|
||||
private commandHistoryStore = new Store<string>('commandHistory')
|
||||
private store = new Store<ManagerState>('simulationStates')
|
||||
|
||||
private saveEvent = new KeyboardInput("s")
|
||||
private createEvent = new KeyboardInput("m")
|
||||
private closeInputEvent = new KeyboardInput("enter")
|
||||
private ctrlEvent = new KeyboardInput("ctrl")
|
||||
private palleteEvent = new KeyboardInput("p")
|
||||
private undoEvent = new KeyboardInput("z")
|
||||
private shiftEvent = new KeyboardInput("shift")
|
||||
private refreshEvent = new KeyboardInput("r")
|
||||
private clearEvent = new KeyboardInput("delete")
|
||||
private upEvent = new KeyboardInput("up")
|
||||
private downEvent = new KeyboardInput("down")
|
||||
private saveEvent = new KeyboardInput('s')
|
||||
private createEvent = new KeyboardInput('m')
|
||||
private closeInputEvent = new KeyboardInput('enter')
|
||||
private ctrlEvent = new KeyboardInput('ctrl')
|
||||
private palleteEvent = new KeyboardInput('p')
|
||||
private undoEvent = new KeyboardInput('z')
|
||||
private shiftEvent = new KeyboardInput('shift')
|
||||
private refreshEvent = new KeyboardInput('r')
|
||||
private gEvent = new KeyboardInput('g')
|
||||
private clearEvent = new KeyboardInput('delete')
|
||||
private upEvent = new KeyboardInput('up')
|
||||
private downEvent = new KeyboardInput('down')
|
||||
|
||||
@persistent<ComponentManager, string>(defaultName, "main")
|
||||
@persistent<ComponentManager, string>(defaultName, 'main')
|
||||
public name: string
|
||||
public alertOptions = alertOptions
|
||||
|
||||
private commandHistory: string[] = []
|
||||
private commands: {
|
||||
[key: string]: (ctx: ComponentManager, args: string[], flags: string[]) => any
|
||||
[key: string]: (
|
||||
ctx: ComponentManager,
|
||||
args: string[],
|
||||
flags: string[]
|
||||
) => any
|
||||
} = {
|
||||
clear(ctx: ComponentManager) {
|
||||
ctx.clear()
|
||||
},
|
||||
save(ctx: ComponentManager) {
|
||||
ctx.save()
|
||||
},
|
||||
ls(ctx: ComponentManager) {
|
||||
const data = ctx.store.ls()
|
||||
const message = data.join("\n")
|
||||
clear(ctx: ComponentManager) {
|
||||
ctx.clear()
|
||||
},
|
||||
save(ctx: ComponentManager) {
|
||||
ctx.save()
|
||||
},
|
||||
ls(ctx: ComponentManager) {
|
||||
const data = ctx.store.ls()
|
||||
const message = data.join('\n')
|
||||
|
||||
success(message, "", ctx.alertOptions)
|
||||
},
|
||||
help(ctx: ComponentManager) {
|
||||
success(`Usage: <command> <br>
|
||||
success(message, '', ctx.alertOptions)
|
||||
},
|
||||
help(ctx: ComponentManager) {
|
||||
success(
|
||||
`Usage: <command> <br>
|
||||
Where <command> is one of:
|
||||
<ul>
|
||||
${Object.keys(ctx.commands).map(val => `
|
||||
${Object.keys(ctx.commands)
|
||||
.map(
|
||||
val => `
|
||||
<li>${val}</li>
|
||||
`).join("")}
|
||||
`
|
||||
)
|
||||
.join('')}
|
||||
</ul>
|
||||
`, "", ctx.alertOptions)
|
||||
},
|
||||
refresh(ctx: ComponentManager) {
|
||||
ctx.refresh()
|
||||
},
|
||||
rewind(ctx:ComponentManager){
|
||||
localStorage.clear()
|
||||
success("Succesfully cleared localStorage!","",ctx.alertOptions)
|
||||
},
|
||||
ctp: this.templateStore.commands.template,
|
||||
settings: this.settings.commands,
|
||||
download
|
||||
}
|
||||
`,
|
||||
'',
|
||||
ctx.alertOptions
|
||||
)
|
||||
},
|
||||
refresh(ctx: ComponentManager) {
|
||||
ctx.refresh()
|
||||
},
|
||||
rewind(ctx: ComponentManager) {
|
||||
localStorage.clear()
|
||||
success('Succesfully cleared localStorage!', '', ctx.alertOptions)
|
||||
},
|
||||
ctp: this.templateStore.commands.template,
|
||||
settings: this.settings.commands,
|
||||
download
|
||||
}
|
||||
private inputMode: string
|
||||
|
||||
public gates = this.templateStore.store.lsChanges
|
||||
|
@ -110,24 +123,24 @@ export class ComponentManager {
|
|||
public file: {
|
||||
[key: string]: () => void
|
||||
} = {
|
||||
clear: () => this.clear(),
|
||||
clean: () => this.smartClear(),
|
||||
save: () => this.save(),
|
||||
undo: () => this.refresh(),
|
||||
download: () => download(this, [], []),
|
||||
delete: () => this.delete(this.name),
|
||||
refresh: () => this.silentRefresh(true)
|
||||
}
|
||||
clear: () => this.clear(),
|
||||
clean: () => this.smartClear(),
|
||||
save: () => this.save(),
|
||||
undo: () => this.refresh(),
|
||||
download: () => download(this, [], []),
|
||||
delete: () => this.delete(this.name),
|
||||
refresh: () => this.silentRefresh(true)
|
||||
}
|
||||
|
||||
public shortcuts: {
|
||||
[key: string]: string
|
||||
} = {
|
||||
clear: "shift delete",
|
||||
clean: "delete",
|
||||
save: "ctrl s",
|
||||
undo: "ctrl z",
|
||||
refresh: "ctrl r"
|
||||
}
|
||||
clear: 'shift delete',
|
||||
clean: 'delete',
|
||||
save: 'ctrl s',
|
||||
undo: 'ctrl z',
|
||||
refresh: 'ctrl r'
|
||||
}
|
||||
|
||||
constructor() {
|
||||
runCounter.increase()
|
||||
|
@ -136,22 +149,22 @@ export class ComponentManager {
|
|||
|
||||
this.refresh()
|
||||
|
||||
fromEvent(document.body, "keydown").subscribe((e: KeyboardEvent) => {
|
||||
if (this.barAlpha.value == "1") {
|
||||
const elem = document.getElementById("nameInput")
|
||||
fromEvent(document.body, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||
if (this.barAlpha.value == '1') {
|
||||
const elem = document.getElementById('nameInput')
|
||||
elem.focus()
|
||||
}
|
||||
else if (!this.ignoreKeyDowns) {
|
||||
} else if (!this.ignoreKeyDowns) {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
fromEvent(document.body, "keyup").subscribe((e: MouseEvent) => {
|
||||
if (this.barAlpha.value === "1") {
|
||||
if (this.closeInputEvent.value)
|
||||
this.create()
|
||||
else if (this.inputMode === "command") {
|
||||
const elem = <HTMLInputElement>document.getElementById("nameInput")
|
||||
fromEvent(document.body, 'keyup').subscribe((e: MouseEvent) => {
|
||||
if (this.barAlpha.value === '1') {
|
||||
if (this.closeInputEvent.value) this.create()
|
||||
else if (this.inputMode === 'command') {
|
||||
const elem = <HTMLInputElement>(
|
||||
document.getElementById('nameInput')
|
||||
)
|
||||
if (this.upEvent.value) {
|
||||
document.body.focus()
|
||||
e.preventDefault()
|
||||
|
@ -159,10 +172,12 @@ export class ComponentManager {
|
|||
|
||||
if (index) {
|
||||
//save drafts
|
||||
if (index === -1)
|
||||
this.temporaryCommnad = elem.value
|
||||
if (index === -1) this.temporaryCommnad = elem.value
|
||||
|
||||
const newIndex = (index === -1) ? this.commandHistory.length - 1 : index - 1
|
||||
const newIndex =
|
||||
index === -1
|
||||
? this.commandHistory.length - 1
|
||||
: index - 1
|
||||
elem.value = this.commandHistory[newIndex]
|
||||
}
|
||||
}
|
||||
|
@ -173,36 +188,36 @@ export class ComponentManager {
|
|||
|
||||
if (index > -1) {
|
||||
const maxIndex = this.commandHistory.length - 1
|
||||
elem.value = (index === maxIndex) ? this.temporaryCommnad : this.commandHistory[index + 1]
|
||||
elem.value =
|
||||
index === maxIndex
|
||||
? this.temporaryCommnad
|
||||
: this.commandHistory[index + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (this.ctrlEvent.value) {
|
||||
if (this.createEvent.value) {
|
||||
this.prepareNewSimulation()
|
||||
}
|
||||
else if (this.shiftEvent.value && this.palleteEvent.value) {
|
||||
} else if (
|
||||
this.shiftEvent.value &&
|
||||
this.palleteEvent.value
|
||||
) {
|
||||
this.preInput()
|
||||
this.inputMode = "command"
|
||||
this.placeholder.next("Command palette")
|
||||
}
|
||||
else if (this.saveEvent.value) {
|
||||
this.inputMode = 'command'
|
||||
this.placeholder.next('Command palette')
|
||||
} else if (this.gEvent.value) {
|
||||
this.importGate()
|
||||
} else if (this.saveEvent.value) {
|
||||
this.save()
|
||||
}
|
||||
else if (this.undoEvent.value) {
|
||||
} else if (this.undoEvent.value) {
|
||||
this.refresh()
|
||||
}
|
||||
else if (this.refreshEvent.value) {
|
||||
} else if (this.refreshEvent.value) {
|
||||
this.silentRefresh(true)
|
||||
}
|
||||
}
|
||||
else if (this.clearEvent.value) {
|
||||
if (this.shiftEvent.value)
|
||||
this.clear()
|
||||
else
|
||||
this.smartClear()
|
||||
} else if (this.clearEvent.value) {
|
||||
if (this.shiftEvent.value) this.clear()
|
||||
else this.smartClear()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -212,22 +227,20 @@ export class ComponentManager {
|
|||
this.update()
|
||||
// this.save()
|
||||
})
|
||||
if (this.saves.value.length === 0)
|
||||
this.save()
|
||||
|
||||
if (this.saves.value.length === 0) this.save()
|
||||
}
|
||||
|
||||
private initEmptyGate(name: string) {
|
||||
const obj: ComponentTemplate = {
|
||||
inputs: 1,
|
||||
name,
|
||||
version: "1.0.0",
|
||||
version: '1.0.0',
|
||||
outputs: 1,
|
||||
activation: "",
|
||||
activation: '',
|
||||
editable: true,
|
||||
material: {
|
||||
mode: "color",
|
||||
data: "blue"
|
||||
mode: 'color',
|
||||
data: 'blue'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,44 +251,57 @@ export class ComponentManager {
|
|||
|
||||
public newGate() {
|
||||
this.preInput()
|
||||
this.inputMode = "gate"
|
||||
this.placeholder.next("Gate name")
|
||||
this.inputMode = 'gate'
|
||||
this.placeholder.next('Gate name')
|
||||
}
|
||||
|
||||
public importGate() {
|
||||
this.preInput()
|
||||
this.inputMode = 'importGate'
|
||||
this.placeholder.next('Gate url')
|
||||
}
|
||||
|
||||
public prepareNewSimulation() {
|
||||
this.preInput()
|
||||
this.inputMode = "create"
|
||||
this.placeholder.next("Create simulation")
|
||||
this.inputMode = 'create'
|
||||
this.placeholder.next('Create simulation')
|
||||
}
|
||||
|
||||
private preInput() {
|
||||
const elem = <HTMLInputElement>document.getElementById("nameInput")
|
||||
elem.value = ""
|
||||
this.barAlpha.next("1")
|
||||
const elem = <HTMLInputElement>document.getElementById('nameInput')
|
||||
elem.value = ''
|
||||
this.barAlpha.next('1')
|
||||
}
|
||||
|
||||
private async create() {
|
||||
const elem = <HTMLInputElement>document.getElementById("nameInput")
|
||||
this.barAlpha.next("0")
|
||||
const elem = <HTMLInputElement>document.getElementById('nameInput')
|
||||
this.barAlpha.next('0')
|
||||
|
||||
if (this.inputMode === "create") {
|
||||
if (this.inputMode === 'create') {
|
||||
await this.createEmptySimulation(elem.value)
|
||||
success(`Succesfully created simulation ${elem.value}`, "", this.alertOptions)
|
||||
success(
|
||||
`Succesfully created simulation ${elem.value}`,
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
} else if (this.inputMode === 'command') this.eval(elem.value)
|
||||
else if (this.inputMode === 'gate') this.initEmptyGate(elem.value)
|
||||
else if (this.inputMode === 'importGate') {
|
||||
importComponent(this, elem.value)
|
||||
}
|
||||
}
|
||||
|
||||
else if (this.inputMode === "command")
|
||||
this.eval(elem.value)
|
||||
|
||||
else if (this.inputMode === "gate")
|
||||
this.initEmptyGate(elem.value)
|
||||
public succes(message: string) {
|
||||
success(message, '', this.alertOptions)
|
||||
}
|
||||
|
||||
private async handleDuplicateModal(name: string) {
|
||||
const result = await modal({
|
||||
title: "Warning",
|
||||
content: html`There was already a simulation called ${name},
|
||||
are you sure you want to override it?
|
||||
All your work will be lost!`
|
||||
title: 'Warning',
|
||||
content: html`
|
||||
There was already a simulation called ${name}, are you sure you
|
||||
want to override it? All your work will be lost!
|
||||
`
|
||||
})
|
||||
|
||||
return result
|
||||
|
@ -286,46 +312,80 @@ export class ComponentManager {
|
|||
const gate = this.templateStore.store.get(name)
|
||||
|
||||
modal({
|
||||
no: "",
|
||||
yes: "save",
|
||||
no: '',
|
||||
yes: 'save',
|
||||
title: `Edit ${name}`,
|
||||
content: html`${html`
|
||||
<br>
|
||||
<div class="mdc-text-field mdc-text-field--textarea">
|
||||
<textarea id="codeArea" class="mdc-text-field__input js" rows="8" cols="40">${
|
||||
gate.activation
|
||||
}</textarea>
|
||||
<div class="mdc-notched-outline">
|
||||
<div class="mdc-notched-outline__leading"></div>
|
||||
<div class="mdc-notched-outline__notch">
|
||||
<label for="textarea" class="mdc-floating-label">Activation function</label>
|
||||
content: html`
|
||||
${html`
|
||||
<br />
|
||||
<div class="mdc-text-field mdc-text-field--textarea">
|
||||
<textarea
|
||||
id="codeArea"
|
||||
class="mdc-text-field__input js"
|
||||
rows="8"
|
||||
cols="40"
|
||||
>
|
||||
${gate.activation}</textarea
|
||||
>
|
||||
<div class="mdc-notched-outline">
|
||||
<div class="mdc-notched-outline__leading"></div>
|
||||
<div class="mdc-notched-outline__notch">
|
||||
<label for="textarea" class="mdc-floating-label"
|
||||
>Activation function</label
|
||||
>
|
||||
</div>
|
||||
<div class="mdc-notched-outline__trailing"></div>
|
||||
</div>
|
||||
<div class="mdc-notched-outline__trailing"></div>
|
||||
</div>
|
||||
</div><br><br>
|
||||
<div class="mdc-text-field" id="inputCount">
|
||||
<input type="number" id="my-text-field" class="mdc-text-field__input inputCount-i" value=${gate.inputs}>
|
||||
<label class="mdc-floating-label" for="my-text-field">Inputs</label>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div><br><br>
|
||||
<div class="mdc-text-field" id="outputCount">
|
||||
<input type="number" id="my-text-field" class="mdc-text-field__input outputCount-i" value=${gate.outputs}>
|
||||
<label class="mdc-floating-label" for="my-text-field">Outputs</label>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div><br><br>
|
||||
<div class="mdc-text-field" id="color">
|
||||
<input type="string" id="my-text-field" class="mdc-text-field__input color-i" value=${gate.material.data}>
|
||||
<label class="mdc-floating-label" for="my-text-field">Outputs</label>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div><br>
|
||||
`}`
|
||||
<br /><br />
|
||||
<div class="mdc-text-field" id="inputCount">
|
||||
<input
|
||||
type="number"
|
||||
id="my-text-field"
|
||||
class="mdc-text-field__input inputCount-i"
|
||||
value=${gate.inputs}
|
||||
/>
|
||||
<label class="mdc-floating-label" for="my-text-field"
|
||||
>Inputs</label
|
||||
>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class="mdc-text-field" id="outputCount">
|
||||
<input
|
||||
type="number"
|
||||
id="my-text-field"
|
||||
class="mdc-text-field__input outputCount-i"
|
||||
value=${gate.outputs}
|
||||
/>
|
||||
<label class="mdc-floating-label" for="my-text-field"
|
||||
>Outputs</label
|
||||
>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class="mdc-text-field" id="color">
|
||||
<input
|
||||
type="string"
|
||||
id="my-text-field"
|
||||
class="mdc-text-field__input color-i"
|
||||
value=${gate.material.data}
|
||||
/>
|
||||
<label class="mdc-floating-label" for="my-text-field"
|
||||
>Color</label
|
||||
>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div>
|
||||
<br />
|
||||
`}
|
||||
`
|
||||
}).then(val => {
|
||||
this.ignoreKeyDowns = false
|
||||
const elems: (HTMLInputElement | HTMLTextAreaElement)[] = [
|
||||
document.querySelector("#codeArea"),
|
||||
document.querySelector(".inputCount-i"),
|
||||
document.querySelector(".outputCount-i"),
|
||||
document.querySelector(".color-i")
|
||||
document.querySelector('#codeArea'),
|
||||
document.querySelector('.inputCount-i'),
|
||||
document.querySelector('.outputCount-i'),
|
||||
document.querySelector('.color-i')
|
||||
]
|
||||
const data = elems.map(val => val.value)
|
||||
|
||||
|
@ -334,21 +394,25 @@ export class ComponentManager {
|
|||
activation: data[0],
|
||||
inputs: Number(data[1]),
|
||||
outputs: Number(data[2]),
|
||||
material:{
|
||||
mode: "color",
|
||||
material: {
|
||||
mode: 'color',
|
||||
data: data[3]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
new MDCTextField(document.querySelector('.mdc-text-field'));
|
||||
new MDCTextField(document.querySelector('#outputCount'));
|
||||
new MDCTextField(document.querySelector('#inputCount'));
|
||||
new MDCTextField(document.querySelector('#color'));
|
||||
new MDCTextField(document.querySelector('.mdc-text-field'))
|
||||
new MDCTextField(document.querySelector('#outputCount'))
|
||||
new MDCTextField(document.querySelector('#inputCount'))
|
||||
new MDCTextField(document.querySelector('#color'))
|
||||
}
|
||||
|
||||
public add(template: string, position?: [number, number]) {
|
||||
const pos = position ? position : [...Array(2)].fill(this.standard.offset * this.components.length) as [number, number]
|
||||
const pos = position
|
||||
? position
|
||||
: ([...Array(2)].fill(
|
||||
this.standard.offset * this.components.length
|
||||
) as [number, number])
|
||||
|
||||
this.components.push(new Component(template, pos, this.standard.scale))
|
||||
this.update()
|
||||
|
@ -356,17 +420,20 @@ export class ComponentManager {
|
|||
|
||||
public async delete(name: string) {
|
||||
const res = await modal({
|
||||
title: "Are you sure?",
|
||||
content: html`Deleting a simulations is ireversible, and all work will be lost!`
|
||||
title: 'Are you sure?',
|
||||
content: html`
|
||||
Deleting a simulations is ireversible, and all work will be
|
||||
lost!
|
||||
`
|
||||
})
|
||||
|
||||
if (res) {
|
||||
if (this.name === name) {
|
||||
if (this.saves.value.length > 1) {
|
||||
this.switchTo(this.saves.value.find(val => val !== name))
|
||||
}
|
||||
else {
|
||||
let newName = (name === defaultName) ? `${defaultName}(1)` : defaultName
|
||||
} else {
|
||||
let newName =
|
||||
name === defaultName ? `${defaultName}(1)` : defaultName
|
||||
await this.createEmptySimulation(newName)
|
||||
this.switchTo(newName)
|
||||
}
|
||||
|
@ -384,15 +451,18 @@ export class ComponentManager {
|
|||
scale: [1, 1]
|
||||
})
|
||||
|
||||
if (name !== this.name)
|
||||
this.save()
|
||||
if (name !== this.name) this.save()
|
||||
this.name = name
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
return new Promise(async (res) => {//get wheater theres already a simulation with that name
|
||||
if (this.store.get(name) && await this.handleDuplicateModal(name) ||
|
||||
!this.store.get(name)) {
|
||||
return new Promise(async res => {
|
||||
//get wheater theres already a simulation with that name
|
||||
if (
|
||||
(this.store.get(name) &&
|
||||
(await this.handleDuplicateModal(name))) ||
|
||||
!this.store.get(name)
|
||||
) {
|
||||
create()
|
||||
res(true)
|
||||
}
|
||||
|
@ -402,38 +472,53 @@ export class ComponentManager {
|
|||
public switchTo(name: string) {
|
||||
const data = this.store.get(name)
|
||||
if (!data)
|
||||
error(`An error occured when trying to load ${name}`, "", this.alertOptions)
|
||||
error(
|
||||
`An error occured when trying to load ${name}`,
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
|
||||
this.name = name
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
eval(command: string) {
|
||||
if (!this.commandHistory.includes(command)) // no duplicates
|
||||
if (!this.commandHistory.includes(command))
|
||||
// no duplicates
|
||||
this.commandHistory.push(command)
|
||||
|
||||
while (this.commandHistory.length > 10) // max of 10 elements
|
||||
while (
|
||||
this.commandHistory.length > 10 // max of 10 elements
|
||||
)
|
||||
this.commandHistory.shift()
|
||||
|
||||
const words = command.split(" ")
|
||||
const words = command.split(' ')
|
||||
|
||||
if (words[0] in this.commands) {
|
||||
const remaining = words.slice(1)
|
||||
const flags = remaining.filter(val => val[0] == "-")
|
||||
const args = remaining.filter(val => val[0] != "-")
|
||||
const flags = remaining.filter(val => val[0] == '-')
|
||||
const args = remaining.filter(val => val[0] != '-')
|
||||
this.commands[words[0]](this, args, flags)
|
||||
}
|
||||
else
|
||||
error(`Command ${words} doesn't exist. Run help to get a list of all commands.`,
|
||||
"", this.alertOptions)
|
||||
} else
|
||||
error(
|
||||
`Command ${words} doesn't exist. Run help to get a list of all commands.`,
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
}
|
||||
|
||||
public smartClear() {
|
||||
this.components = this.components.filter(({ id }) =>
|
||||
this.wireManager.wires.find(val => val.input.of.id == id || val.output.of.id == id)
|
||||
this.wireManager.wires.find(
|
||||
val => val.input.of.id == id || val.output.of.id == id
|
||||
)
|
||||
)
|
||||
this.update()
|
||||
success("Succesfully cleared all unconnected components", "", this.alertOptions)
|
||||
success(
|
||||
'Succesfully cleared all unconnected components',
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
}
|
||||
|
||||
public clear() {
|
||||
|
@ -441,7 +526,7 @@ export class ComponentManager {
|
|||
this.wireManager.dispose()
|
||||
this.update()
|
||||
|
||||
success("Succesfully cleared all components", "", this.alertOptions)
|
||||
success('Succesfully cleared all components', '', this.alertOptions)
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
|
@ -454,7 +539,11 @@ export class ComponentManager {
|
|||
|
||||
this.update()
|
||||
|
||||
success("Succesfully refreshed to the latest save", "", this.alertOptions)
|
||||
success(
|
||||
'Succesfully refreshed to the latest save',
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
}
|
||||
|
||||
update() {
|
||||
|
@ -488,12 +577,10 @@ export class ComponentManager {
|
|||
// if (false) { }
|
||||
if (toAddOnTop >= 0) {
|
||||
this.top(this.components[toAddOnTop])
|
||||
}
|
||||
|
||||
else if (outsideComponents && this.clicked) {
|
||||
} else if (outsideComponents && this.clicked) {
|
||||
const mousePosition = [e.clientX, e.clientY]
|
||||
const delta = mousePosition.map((value, index) =>
|
||||
this.screen.mousePosition[index] - value
|
||||
const delta = mousePosition.map(
|
||||
(value, index) => this.screen.mousePosition[index] - value
|
||||
) as [number, number]
|
||||
this.screen.move(...delta)
|
||||
}
|
||||
|
@ -503,7 +590,11 @@ export class ComponentManager {
|
|||
public silentRefresh(verboose = false) {
|
||||
this.loadState(this.state)
|
||||
if (verboose)
|
||||
success("Succesfully reloaded all components", "", this.alertOptions)
|
||||
success(
|
||||
'Succesfully reloaded all components',
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
}
|
||||
|
||||
public top(component: Component) {
|
||||
|
@ -523,57 +614,67 @@ export class ComponentManager {
|
|||
toRemoveDuplicatesFor = component
|
||||
}
|
||||
|
||||
const stroke = subscribe(component.clickedChanges.pipe(map(
|
||||
val => val ? "yellow" : "black"
|
||||
)))
|
||||
const stroke = subscribe(
|
||||
component.clickedChanges.pipe(
|
||||
map(val => (val ? 'yellow' : 'black'))
|
||||
)
|
||||
)
|
||||
|
||||
return svg`
|
||||
<g>
|
||||
${component.pinsSvg(10, 20)}
|
||||
${component.pinsSvg(10, 20, "output")}
|
||||
${component.pinsSvg(10, 20, 'output')}
|
||||
|
||||
<g @mousedown=${ (e: MouseEvent) => component.handleClick(e)}
|
||||
<g @mousedown=${(e: MouseEvent) => component.handleClick(e)}
|
||||
@touchstart=${(e: MouseEvent) => component.handleClick(e)}
|
||||
@mouseup=${mouseupHandler}
|
||||
@touchend=${mouseupHandler}>
|
||||
<rect width=${ subscribe(component.width)}
|
||||
height=${ subscribe(component.height)}
|
||||
x=${ subscribe(component.x)}
|
||||
y=${ subscribe(component.y)}
|
||||
<rect width=${subscribe(component.width)}
|
||||
height=${subscribe(component.height)}
|
||||
x=${subscribe(component.x)}
|
||||
y=${subscribe(component.y)}
|
||||
stroke=${stroke}
|
||||
fill=${(component.material.mode === "standard_image") ?
|
||||
"rgba(0,0,0,0)" :
|
||||
subscribe(component.material.color)}
|
||||
fill=${
|
||||
component.material.mode !== 'color'
|
||||
? 'rgba(0,0,0,0)'
|
||||
: subscribe(component.material.color)
|
||||
}
|
||||
rx=20
|
||||
ry=20>
|
||||
</rect>
|
||||
${(component.material.mode === "standard_image") ? component.material.innerHTML(
|
||||
subscribe(component.x),
|
||||
subscribe(component.y),
|
||||
subscribe(component.width),
|
||||
subscribe(component.height)) : ""}
|
||||
${
|
||||
component.material.mode !== 'color'
|
||||
? component.material.innerHTML(
|
||||
subscribe(component.x),
|
||||
subscribe(component.y),
|
||||
subscribe(component.width),
|
||||
subscribe(component.height)
|
||||
)
|
||||
: ''
|
||||
}
|
||||
</g>
|
||||
</g>
|
||||
`});
|
||||
`
|
||||
})
|
||||
|
||||
if (toRemoveDuplicatesFor)
|
||||
this.removeDuplicates(toRemoveDuplicatesFor)
|
||||
if (toRemoveDuplicatesFor) this.removeDuplicates(toRemoveDuplicatesFor)
|
||||
|
||||
return svg`${this.wireManager.svg} ${result}`
|
||||
}
|
||||
|
||||
private removeDuplicates(component: Component) {
|
||||
let instances = this.components
|
||||
.map((value, index) => (value == component) ? index : null)
|
||||
.map((value, index) => (value == component ? index : null))
|
||||
.filter(value => value)
|
||||
instances.pop()
|
||||
|
||||
this.components = this.components
|
||||
.filter((_, index) => instances.indexOf(index) != -1)
|
||||
this.components = this.components.filter(
|
||||
(_, index) => instances.indexOf(index) != -1
|
||||
)
|
||||
}
|
||||
|
||||
get state(): ManagerState {
|
||||
const components = Array.from((new Set(this.components)).values())
|
||||
const components = Array.from(new Set(this.components).values())
|
||||
return {
|
||||
components: components.map(value => value.state),
|
||||
position: this.screen.position as [number, number],
|
||||
|
@ -591,17 +692,24 @@ export class ComponentManager {
|
|||
}
|
||||
|
||||
private loadState(state: ManagerState) {
|
||||
if (!state.wires) //old state
|
||||
if (!state.wires)
|
||||
//old state
|
||||
return
|
||||
|
||||
this.wireManager.dispose()
|
||||
this.clicked = false
|
||||
this.components = state.components.map(value => Component.fromState(value))
|
||||
this.components = state.components.map(value =>
|
||||
Component.fromState(value)
|
||||
)
|
||||
this.onTop = null
|
||||
|
||||
state.wires.forEach(val => {
|
||||
this.wireManager.start = this.getComponentById(val.from.owner).outputPins[val.from.index]
|
||||
this.wireManager.end = this.getComponentById(val.to.owner).inputPins[val.to.index]
|
||||
this.wireManager.start = this.getComponentById(
|
||||
val.from.owner
|
||||
).outputPins[val.from.index]
|
||||
this.wireManager.end = this.getComponentById(
|
||||
val.to.owner
|
||||
).inputPins[val.to.index]
|
||||
this.wireManager.tryResolving()
|
||||
})
|
||||
|
||||
|
@ -614,10 +722,14 @@ export class ComponentManager {
|
|||
|
||||
save() {
|
||||
for (let i = 0; i < this.commandHistory.length; i++) {
|
||||
const element = this.commandHistory[i];
|
||||
const element = this.commandHistory[i]
|
||||
this.commandHistoryStore.set(i.toString(), element)
|
||||
}
|
||||
this.store.set(this.name, this.state)
|
||||
success(`Saved the simulation ${this.name} succesfully!`, "", this.alertOptions)
|
||||
success(
|
||||
`Saved the simulation ${this.name} succesfully!`,
|
||||
'',
|
||||
this.alertOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue