😁 Added too much stuff to explain!

This commit is contained in:
Matei Adriel 2019-05-27 16:35:34 +03:00
parent df26cb6ddb
commit 900272a9e1
23 changed files with 899 additions and 58 deletions

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
12.2.0

View file

@ -18,6 +18,8 @@
}, },
"homepage": "https://github.com/neverix/html5-game-template#readme", "homepage": "https://github.com/neverix/html5-game-template#readme",
"devDependencies": { "devDependencies": {
"@types/file-saver": "^2.0.1",
"@types/toastr": "^2.1.37",
"css-loader": "^2.1.0", "css-loader": "^2.1.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
@ -34,11 +36,14 @@
"webpack-dev-server": "^3.2.0" "webpack-dev-server": "^3.2.0"
}, },
"dependencies": { "dependencies": {
"@eix/input": "git+https://github.com/eix-js/input.git",
"@eix/utils": "git+https://github.com/eix-js/utils.git", "@eix/utils": "git+https://github.com/eix-js/utils.git",
"file-saver": "^2.0.2",
"haunted": "^4.3.0", "haunted": "^4.3.0",
"lit-html": "^1.0.0", "lit-html": "^1.0.0",
"lit-rx": "0.0.2", "lit-rx": "0.0.2",
"prelude-ts": "^0.8.2", "prelude-ts": "^0.8.2",
"rxjs": "^6.5.2" "rxjs": "^6.5.2",
"toastr": "^2.1.4"
} }
} }

View file

@ -1,3 +1,5 @@
@import "./toastr.scss";
html, body { html, body {
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -7,8 +9,52 @@ html, body {
} }
svg { svg {
background-color: #444444; background-color: #222222;
height: 100%; height: 100%;
width: 100%; width: 100%;
display: block; display: block;
} }
.createBar {
z-index:10;
position: absolute;
top:0px;
left:0px;
width:100%;
height:100%;
background-color: rgba(0,0,0,0.5);
transition: all 0.6s ease-in-out 0s;
.topContainer {
height: 30%;
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
div{
height:25%;
width:75%;
input{
background-color: #444444;
color: white;
border: none;
font-size: 250%;
height:100%;
width:100%;
padding: 1%;
font-family: "roboto";
}
}
}
opacity: 0;
visibility: hidden;
}
.createBar#shown{
opacity: 1;
visibility: visible;
}
.toasts{
background-color: #000000;
box-shadow: 0 0 0px black !important;
}

1
src/scss/toastr.scss Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,29 +1,98 @@
import { Vector } from "prelude-ts" import { Vector } from "prelude-ts"
import { Subject, BehaviorSubject } from "rxjs"; import { Subject, BehaviorSubject, Subscription, timer } from "rxjs";
import { ComponentState } from "./interfaces"; import { ComponentState, activationContext } from "./interfaces";
import { map } from "rxjs/operators"; import { map, debounce } from "rxjs/operators";
import { Screen } from "../screen.ts"; import { Screen } from "../screen.ts";
import { ComponentTemplateStore } from "../componentManager/componentTemplateStore";
import { svg } from "lit-html";
import { subscribe } from "lit-rx";
import { Pin } from "../pin";
import { success, error } from "toastr"
import { alertOptions } from "../componentManager/alertOptions";
import { WireManager } from "../wires";
import { runCounter } from "./runCounter";
export class Component { export class Component {
private static store = new ComponentTemplateStore()
private static screen = new Screen() private static screen = new Screen()
private static wireManager = new WireManager()
private static lastId = runCounter.get() + 1
public position = new BehaviorSubject<number[]>(null) public position = new BehaviorSubject<number[]>(null)
public scale = new BehaviorSubject<number[]>(null) public scale = new BehaviorSubject<number[]>(null)
public clicked = false public clicked = false
private mouserDelta: number[] private mouserDelta: number[]
private strokeColor = "#888888"
private inputs: number
private outputs: number
private activation: (ctx: activationContext) => any
private subscriptions:Subscription[] = []
constructor(public activationType: string, public inputPins: Pin[] = []
public outputPins: Pin[] = []
public id: number
constructor(private template: string,
position: [number, number] = [0, 0], position: [number, number] = [0, 0],
scale: [number, number] = [0, 0]) { scale: [number, number] = [0, 0],
id? : number) {
//set initial props
this.position.next(position) this.position.next(position)
this.scale.next(scale) this.scale.next(scale)
//set the correct id
this.id = (typeof id === "number") ? id : Component.lastId++
//load template
const data = Component.store.store.get(template)
if (!data)
throw new Error(`Template ${template} doesnt exist`)
this.inputs = data.inputs
this.outputs = data.outputs
this.inputPins = [...Array(this.inputs)].fill(true).map(val => new Pin(false, this))
this.outputPins = [...Array(this.outputs)].fill(true).map(val => new Pin(true, this))
this.activation = new Function(`return (ctx) => {
try{
${data.activation}
}
catch(err){
ctx.error(err,"",ctx.alertOptions)
}
}`)()
this.inputPins.forEach(val => {
const subscription = val.valueChanges.pipe(debounce(() => timer(1000 / 60)))
.subscribe(val => this.activate())
this.subscriptions.push(subscription)
})
this.activate()
} }
handleMouseUp(e: MouseEvent) { public dispose(){
this.subscriptions.forEach(val => val.unsubscribe())
}
public handleMouseUp(e: MouseEvent) {
this.clicked = false this.clicked = false
} }
private activate() {
this.activation({
outputs: this.outputPins,
inputs: this.inputPins,
succes: (mes: string) => { success(mes, "", alertOptions) },
error: (mes: string) => { error(mes, "", alertOptions) }
} as activationContext)
}
move(e: MouseEvent) { move(e: MouseEvent) {
const mousePosition = Component.screen.getWorldPosition(e.clientX, e.clientY) const mousePosition = Component.screen.getWorldPosition(e.clientX, e.clientY)
this.position.next(mousePosition.map((value, index) => this.position.next(mousePosition.map((value, index) =>
@ -40,11 +109,16 @@ export class Component {
this.clicked = true this.clicked = true
} }
handlePinClick(e: MouseEvent, pin: Pin) {
Component.wireManager.add(pin)
}
get state(): ComponentState { get state(): ComponentState {
return { return {
position: this.position.value as [number, number], position: this.position.value as [number, number],
scale: this.position.value as [number, number], scale: this.scale.value as [number, number],
activationType: this.activationType template: this.template,
id: this.id
} }
} }
@ -70,7 +144,57 @@ export class Component {
)) ))
} }
pinsSvg(pinScale: number, pinLength = 20, mode = "input") {
const stroke = 3
return ((mode === "input") ? this.inputPins : this.outputPins)
.map((val, index) => {
const y = subscribe(this.piny(mode === "input",index))
const x = subscribe(this.pinx(mode === "input",pinLength))
const linex = subscribe(this.x.pipe(map(val =>
val + ((mode === "input") ? -pinLength : pinLength + this.scale.value[0])
)))
const middleX = subscribe(this.x.pipe(map(val =>
val + this.scale.value[0] / 2
)))
return svg`
<line stroke=${this.strokeColor} y1=${y} y2=${y}
x1=${(mode === "input") ? linex : middleX}
x2=${(mode !== "input") ? linex : middleX}
stroke-width=${stroke}></line>
<circle fill=${subscribe(val.svgColor)}
stroke=${this.strokeColor}
r=${pinScale}
cx=${x}
cy=${y} stroke-width=${stroke}
@click=${(e: MouseEvent) => this.handlePinClick(e, val)}
></circle>
`})
}
public pinx(mode = true, pinLength = 15){
return this.x.pipe(
map(val => val + (
(mode) ?
-pinLength :
this.scale.value[0] + pinLength
))
)
}
public piny(mode = true, index: number){
const space = this.scale.value[1] / (mode ? this.inputs : this.outputs)
return this.y.pipe(
map(val => val + space * (2 * index + 1) / 2)
)
}
static fromState(state: ComponentState) { static fromState(state: ComponentState) {
return new Component(state.activationType, state.position, state.scale) return new Component(state.template, state.position, state.scale, state.id)
} }
} }

View file

@ -1,5 +1,15 @@
import { Pin } from "../pin";
export interface ComponentState { export interface ComponentState {
position: [number,number] position: [number,number]
scale: [number,number] scale: [number,number]
activationType: string template: string
id: number
}
export interface activationContext {
inputs: Pin[]
outputs: Pin[]
succes: (mes: string) => any
error: (mes:string) => any
} }

View file

@ -0,0 +1,14 @@
import { Store } from "../store";
export const runCounter = {
store: new Store<number>("runCounter"),
get(){
return runCounter.store.get("main")
},
increase(){
runCounter.store.set("main", runCounter.store.get("main") + 1)
}
}
if (!runCounter.get())
runCounter.store.set("main",1)

View file

@ -0,0 +1,4 @@
export const alertOptions = {
positionClass: "toast-bottom-right",
toastClass: "toasts"
}

View file

@ -1,22 +1,222 @@
import { Singleton } from "@eix/utils"; import { Singleton } from "@eix/utils";
import { Component } from "../component/component"; import { Component } from "../component";
import { Subject } from "rxjs"; import { Subject, BehaviorSubject, fromEvent } from "rxjs";
import { svg, SVGTemplateResult } from "lit-html"; import { svg, SVGTemplateResult } from "lit-html";
import { subscribe } from "lit-rx"; import { subscribe } from "lit-rx";
import { Screen } from "../screen.ts"; import { Screen } from "../screen.ts";
import { MnanagerState } from "./interfaces"; import { ManagerState } 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";
@Singleton @Singleton
export class ComponentManager { export class ComponentManager {
public components: Component[] = [] public components: Component[] = []
public svgs = new Subject<SVGTemplateResult[]>() public svgs = new Subject<SVGTemplateResult>()
public placeholder = new BehaviorSubject("Create simulation")
private temporaryCommnad = ""
private onTop: Component private onTop: Component
private clicked = false private clicked = false
private screen = new Screen() private screen = new Screen()
private wireManager = new WireManager()
private templateStore = new ComponentTemplateStore()
private settings = new Settings()
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 shiftEvent = new KeyboardInput("shift")
private refreshEvent = new KeyboardInput("r")
private clearEvent = new KeyboardInput("c")
private upEvent = new KeyboardInput("up")
private downEvent = new KeyboardInput("down")
public name = "current"
public alertOptions = alertOptions
private commandHistory: string[] = []
private commands: {
[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")
success(message, "", ctx.alertOptions)
},
help(ctx: ComponentManager) {
success(`Usage: &ltcommand> <br>
Where &ltcommand> is one of:
<ul>
${Object.keys(ctx.commands).map(val => `
<li>${val}</li>
`).join("")}
</ul>
`, "", ctx.alertOptions)
},
refresh(ctx: ComponentManager) {
ctx.refresh()
},
ctp: this.templateStore.commands.template,
settings: this.settings.commands,
download
}
private inputMode: string
public barAlpha = new BehaviorSubject<string>("0");
constructor() { constructor() {
runCounter.increase()
this.svgs.next(this.render()) this.svgs.next(this.render())
this.refresh()
fromEvent(document.body, "keydown").subscribe((e: KeyboardEvent) => {
if (this.barAlpha.value == "1") {
const elem = document.getElementById("nameInput")
elem.focus()
}
else {
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")
if (this.upEvent.value) {
document.body.focus()
e.preventDefault()
const index = this.commandHistory.indexOf(elem.value)
if (index) {
//save drafts
if (index === -1)
this.temporaryCommnad = elem.value
const newIndex = (index === -1) ? this.commandHistory.length - 1 : index - 1
elem.value = this.commandHistory[newIndex]
}
}
if (this.downEvent.value) {
document.body.focus()
e.preventDefault()
const index = this.commandHistory.indexOf(elem.value)
if (index > -1) {
const maxIndex = this.commandHistory.length - 1
elem.value = (index === maxIndex) ? this.temporaryCommnad : this.commandHistory[index + 1]
}
}
}
}
else {
if (this.ctrlEvent.value) {
if (this.createEvent.value) {
this.preInput()
this.inputMode = "create"
this.placeholder.next("Create simulation")
}
else if (this.shiftEvent.value && this.palleteEvent.value) {
this.preInput()
this.inputMode = "command"
this.placeholder.next("Command palette")
}
else if (this.clearEvent.value) {
this.clear()
}
else if (this.saveEvent.value) {
this.save()
}
else if (this.refreshEvent.value) {
this.refresh()
}
}
}
})
this.wireManager.update.subscribe(val => this.update())
}
preInput() {
const elem = <HTMLInputElement>document.getElementById("nameInput")
elem.value = ""
this.barAlpha.next("1")
}
create() {
const elem = <HTMLInputElement>document.getElementById("nameInput")
this.barAlpha.next("0")
if (this.inputMode == "create")
success(`Succesfully created simulation ${elem.value}`, "", this.alertOptions)
else if (this.inputMode == "command")
this.eval(elem.value)
}
eval(command: string) {
if (!this.commandHistory.includes(command)) // no duplicates
this.commandHistory.push(command)
while (this.commandHistory.length > 10) // max of 10 elements
this.commandHistory.shift()
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] != "-")
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)
}
clear() {
this.components = []
this.wireManager.dispose()
this.update()
success("Succesfully cleared all components", "", this.alertOptions)
}
refresh() {
if (this.store.get(this.name)) {
this.loadState(this.store.get(this.name))
}
for (const i of this.commandHistoryStore.ls())
this.commandHistory[Number(i)] = this.commandHistoryStore.get(i)
this.update()
success("Succesfully refreshed to the latest save", "", this.alertOptions)
} }
update() { update() {
@ -64,25 +264,32 @@ export class ComponentManager {
render() { render() {
let toRemoveDuplicatesFor: Component let toRemoveDuplicatesFor: Component
const size = 10
const result = this.components.map(component => svg` const result = this.components.map(component => svg`
<g>
${component.pinsSvg(10, 20)}
${component.pinsSvg(10, 20, "output")}
<rect width=${ subscribe(component.width)} <rect width=${ subscribe(component.width)}
height=${ subscribe(component.height)} height=${ subscribe(component.height)}
x=${ subscribe(component.x)} x=${ subscribe(component.x)}
y=${ subscribe(component.y)} y=${ subscribe(component.y)}
fill="red" fill="red"
stroke="black" stroke="black"
rx=20
ry=20
@mousedown=${ (e: MouseEvent) => component.handleClick(e)} @mousedown=${ (e: MouseEvent) => component.handleClick(e)}
@mouseup=${(e: MouseEvent) => { @mouseup=${(e: MouseEvent) => {
component.handleMouseUp(e) component.handleMouseUp(e)
toRemoveDuplicatesFor = component toRemoveDuplicatesFor = component
}} }}></rect>
> </g>
`); `);
if (toRemoveDuplicatesFor) if (toRemoveDuplicatesFor)
this.removeDuplicates(toRemoveDuplicatesFor) this.removeDuplicates(toRemoveDuplicatesFor)
return result return svg`${this.wireManager.svg} ${result}`
} }
private removeDuplicates(component: Component) { private removeDuplicates(component: Component) {
@ -95,21 +302,47 @@ export class ComponentManager {
.filter((val, index) => instances.indexOf(index) != -1) .filter((val, index) => instances.indexOf(index) != -1)
} }
get state(): MnanagerState { get state(): ManagerState {
const components = Array.from((new Set(this.components)).values()) const components = Array.from((new Set(this.components)).values())
return { return {
components: components.map(value => value.state) components: components.map(value => value.state),
position: this.screen.position as [number, number],
scale: this.screen.scale as [number, number],
wires: this.wireManager.state
} }
} }
loadState(state:MnanagerState) { public getComponentById(id: number) {
return this.components.find(val => val.id === id)
}
loadState(state: ManagerState) {
if (!state.wires) //old state
return
this.wireManager.dispose()
this.clicked = false this.clicked = false
this.components = state.components.map(value => Component.fromState(value)) this.components = state.components.map(value => Component.fromState(value))
this.onTop = null 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.tryResolving()
})
this.screen.scale = state.scale
this.screen.position = state.position
this.update() this.update()
} }
save(){ save(name?: string) {
//TODO: implement for (let i = 0; i < this.commandHistory.length; i++) {
const element = this.commandHistory[i];
this.commandHistoryStore.set(i.toString(), element)
}
this.store.set(name || this.name, this.state)
success("Saved the simulation succesfully!", "", this.alertOptions)
} }
} }

View file

@ -0,0 +1,105 @@
import { Singleton } from "@eix/utils";
import { Store } from "../store";
import { ComponentTemplate } from "./interfaces";
import { ComponentManager } from "./componentManager";
import { success, error } from "toastr"
@Singleton
export class ComponentTemplateStore {
public store = new Store<ComponentTemplate>("componentTemplate")
public commands = {
template: (ctx: ComponentManager, args: string[], flags: string[]) => {
const command = args[0]
switch (command) {
case (undefined):
for (let i of flags) {
if (i === "--version" || i === "-v")
return success("1.0.1", "", ctx.alertOptions)
}
error(`Welcome to the component template program!
To get started, try running this basic commands:
${["--version", "ls"].map(val => `${val}`).join(" ")}
`, "", {
...ctx.alertOptions,
timeOut: 7500
})
break
case ("ls"):
success(`Here is a list of all the current registered component templates (including ics):
<ul>
${this.store.ls().map(val => `
<li>
${val}
</li>
`).join(" ")}
</ul>
`, "", ctx.alertOptions)
break
case ("info"):
if (!args[1])
return error("You need to specify a template name", "", ctx.alertOptions)
const data = this.store.get(args[1])
if (!data)
return error(`Component ${args[1]} doesnt exist`, "", ctx.alertOptions)
const showFunction = flags.find(value =>
value === "-sf" || value === "--showFunctions"
)
success(`
Name: ${data.name} <br>
Inputs: ${data.inputs} <br>
Outputs: ${data.outputs}
${showFunction ? `<br> Activation: ${data.activation}` : ""}
`, "", ctx.alertOptions)
break
default:
error(`${command} is not a valid command for the template program`, "", ctx.alertOptions)
}
}
}
constructor() {
this.store.set("buffer", {
inputs: 1,
outputs: 1,
name: "buffer",
version: "1.0.0",
activation: `
ctx.outputs[0].value = ctx.inputs[0].value
`.trim()
})
this.store.set("not", {
inputs: 1,
outputs: 1,
name: "buffer",
version: "1.0.0",
activation: `
ctx.outputs[0].value = !ctx.inputs[0].value
`.trim()
})
this.store.set("and", {
inputs: 2,
outputs: 1,
name: "and",
version: "1.0.0",
activation: `
ctx.outputs[0].value = ctx.inputs[0].value && ctx.inputs[1].value
`.trim()
})
this.store.set("true", {
inputs: 0,
outputs: 1,
name: "true",
version: "1.0.0",
activation: `
ctx.outputs[0].value = true
`.trim()
})
}
}

View file

@ -0,0 +1,31 @@
import { ComponentManager } from "./componentManager";
import { success } from "toastr"
import { saveAs } from "file-saver"
const version = "1.0.0"
export const download = (ctx: ComponentManager, args: string[], flags:string[]) => {
//important flags
for (let i of flags) {
if (i === "--version" || i === "-v")
return success(`${version}`, "", ctx.alertOptions)
else if (i === "--help" || i === "-h")
return success(`Run "download" to download the save as a json file.
Flags:<ul>
<li>-v or --version to get the version.</li>
<li>-h or --help to get help (ou are reading that right now)</li>
<li>-s or --save to automatically save before downloading</li>
</ul>`,"",ctx.alertOptions)
}
const command = args[0]
if (command === undefined){
if (flags.includes("-s") || flags.includes("--save"))
ctx.save()
const data = JSON.stringify(ctx.state)
saveAs(new Blob([data]), `${ctx.name}.json`)
}
}

View file

@ -1,5 +1,17 @@
import { ComponentState } from "../component/interfaces"; import { ComponentState } from "../component/interfaces";
import { WireState } from "../wires/interface";
export interface MnanagerState { export interface ManagerState {
components: ComponentState[] components: ComponentState[]
scale: [number,number]
position: [number,number]
wires: WireState
}
export interface ComponentTemplate {
name: string
version: string
activation: string
inputs: number
outputs: number
} }

View file

@ -0,0 +1 @@
export * from "./pin"

65
src/ts/common/pin/pin.ts Normal file
View file

@ -0,0 +1,65 @@
import { BehaviorSubject, Subject, Subscription } from "rxjs";
import { map } from "rxjs/operators"
import clamp from "../clamp/clamp";
import { Component } from "../component";
export class Pin {
private static lastId = 0
public pair: Pin
private subscriptions: Subscription[] = []
public id: number
public _value = 0
public color = new BehaviorSubject<[number, number, number, number]>([0, 0, 0,0])
public memory: any = {}
public valueChanges = new Subject<number>()
public svgColor = this.color.pipe(map(val =>
`rgb(${val.join(",")})`
))
constructor(public allowWrite = true, public of: Component) {
this.setValue(0)
this.id = Pin.lastId++
}
get value() {
return this._value
}
set value(value: number) {
if (!this.allowWrite) return
this.setValue(value)
}
public setValue(value: number) {
this._value = clamp(value, 0, 1)
this.valueChanges.next(this._value)
const color: [number, number, number, number] = (value > 0.5) ?
[255, 216, 20, 1] :
[90, 90, 90, 1]
this.color.next((this.pair) ? color : [0,0,0,0])
}
public bindTo(pin: Pin){
this.pair = pin
const subscription = pin.valueChanges.subscribe(val => this.setValue(val))
this.subscriptions.push(subscription)
}
public unbind(pin: Pin) {
if (this.pair == pin){
this.pair = null
this.subscriptions.forEach(val => val.unsubscribe())
}
}
public update(){
this.setValue(this._value)
}
}

View file

@ -11,12 +11,12 @@ export class Screen {
this.getViewBox(...values) this.getViewBox(...values)
)); ));
private scrollStep = 1.3 public position = [0, 0]
private position = [0, 0]
public scale = [2, 2] public scale = [2, 2]
private zoomLimits: [number,number] = [0.1,10] private zoomLimits: [number,number] = [0.1,10]
private scrollStep = 1.3
public mousePosition = [this.width.value / 2, this.height.value / 2] public mousePosition = [this.width.value / 2, this.height.value / 2]
constructor() { constructor() {

View file

@ -0,0 +1 @@
export * from "./store"

View file

@ -0,0 +1,29 @@
import { Singleton } from "@eix/utils";
import { ComponentManager } from "../componentManager";
import { success, error } from "toastr"
@Singleton
export class Settings {
version = "1.0.0"
commands = (ctx: ComponentManager, args: string[], flags: string[]) => {
//important flags
for (let i of flags) {
if (i === "--version" || i === "-v")
return success(`${this.version}`, "", ctx.alertOptions)
}
const command = args[0]
if (command === undefined)
return success(
`Welcome to the settings cli. You can use this to tweak settings in any way imaginable!`,
"",
ctx.alertOptions)
//nothing here
error(`Commands ${args} couldnt be found`,"",ctx.alertOptions)
}
constructor() { }
}

View file

@ -0,0 +1,28 @@
export class Store<T> {
constructor(private name: string){ }
get(key:string):T{
const data = localStorage[`${this.name}/${key}`]
if(data)
return JSON.parse(data).value
return null
}
set(key:string,value:T){
localStorage[`${this.name}/${key}`] = JSON.stringify({ value })
return this
}
ls() {
let keys = []
for (const i in localStorage){
if (i.indexOf(this.name) == 0)
keys.push(i.substr(this.name.length + 1))
}
return keys
}
}

View file

@ -0,0 +1 @@
export * from "./wireManager"

View file

@ -0,0 +1,12 @@
export interface WireStateVal {
from: {
owner: number
index: number
},
to: {
owner: number
index: number
}
}
export type WireState = WireStateVal[]

View file

@ -0,0 +1,15 @@
import { Pin } from "../pin";
export class Wire {
constructor (public input:Pin,public output:Pin){
this.output.bindTo(this.input)
this.input.pair = this.output
this.input.update()
this.output.update()
}
public dispose(){
this.output.unbind(this.input)
this.input.pair = null
}
}

View file

@ -0,0 +1,81 @@
import { Singleton } from "@eix/utils";
import { Pin } from "../pin";
import { Wire } from "./wire";
import { svg } from "lit-html";
import { subscribe } from "lit-rx";
import { ComponentManager } from "../componentManager";
import { Observable, Subject } from "rxjs";
import { WireState, WireStateVal } from "./interface";
@Singleton
export class WireManager {
public start: Pin
public end: Pin
private wires: Wire[] = []
public update = new Subject<boolean>()
constructor() { }
public add(data: Pin) {
if (data.allowWrite) //output
this.start = data
else
this.end = data
this.tryResolving()
}
public dispose(){
for (let i of this.wires)
i.dispose()
this.wires = []
}
public tryResolving() {
if (this.start && this.end && this.start != this.end) {
if (this.canBind(this.start, this.end)) {
this.wires.push(new Wire(this.start, this.end))
this.start = null
this.end = null
this.update.next(true)
}
}
}
private canBind(start: Pin, end: Pin) {
if (this.wires.find(val => val.output === end))
return false
return true
}
get svg() {
return this.wires.map(val => {
const i = val.input.of
const o = val.output.of
return svg`
<line x1=${subscribe(i.pinx(false, 20))}
x2=${subscribe(o.pinx(true, 20))}
y1=${subscribe(i.piny(false,i.outputPins.indexOf(val.input)))}
y2=${subscribe(o.piny(true,o.inputPins.indexOf(val.output)))}
stroke=${subscribe(val.input.svgColor)}
>
</line>
`})
}
get state() {
return this.wires.map((val):WireStateVal => ({
from: {
owner: val.input.of.id,
index: val.input.of.outputPins.indexOf(val.input)
},
to: {
owner: val.output.of.id,
index: val.output.of.inputPins.indexOf(val.output)
}
}))
}
}

View file

@ -4,37 +4,59 @@ import { Screen } from "./common/screen.ts";
import { Component } from "./common/component"; import { Component } from "./common/component";
import { FunctionStore } from "./common/activation/activationStore"; import { FunctionStore } from "./common/activation/activationStore";
import { ComponentManager } from "./common/componentManager"; import { ComponentManager } from "./common/componentManager";
import { map } from "rxjs/operators";
const screen = new Screen() const screen = new Screen()
const test = new FunctionStore()
test.register("buffer",(data) => {
return true;
})
const manager = new ComponentManager() const manager = new ComponentManager()
manager.components.push(new Component("none",[200,100],[200,30])) manager.components.push(new Component("and",[200,100],[100,100]))
manager.components.push(new Component("none",[300,100],[200,30])) manager.components.push(new Component("not",[200,500],[100,100]))
manager.components.push(new Component("none",[400,100],[200,30])) manager.components.push(new Component("true",[200,500],[100,100]))
manager.components.push(new Component("none",[500,100],[200,30]))
manager.components.push(new Component("none",[600,100],[200,30]))
manager.update() manager.update()
console.log(manager.state)
const handleEvent = <T>(e:T,func:(e:T) => any) => {
if (manager.barAlpha.value == "0")
func(e)
else if (manager.barAlpha.value == "1"
&& (e as unknown as MouseEvent).type == "mousedown"
&& (e as unknown as MouseEvent).target != document.getElementById("nameInput"))
manager.barAlpha.next("0")
}
render(html` render(html`
<svg height=${ subscribe(screen.height) } width=${ subscribe(screen.width) } <div @mousemove=${(e:MouseEvent) => handleEvent(e,(e:MouseEvent) => {
@mousemove=${(e:MouseEvent) => {
manager.handleMouseMove(e) manager.handleMouseMove(e)
screen.updateMouse(e) screen.updateMouse(e)
}} })}
viewBox=${subscribe(screen.viewBox)} @mousedown=${(e:MouseEvent) => handleEvent(e,(e:MouseEvent) =>
@mousedown=${(e:MouseEvent) => manager.handleMouseDown(e)} manager.handleMouseDown(e)
@mouseup=${(e:MouseEvent) => manager.handleMouseUp(e)} )}
@wheel=${(e:WheelEvent) => screen.handleScroll(e)} @mouseup=${(e:MouseEvent) => handleEvent(e,(e:MouseEvent) =>
> manager.handleMouseUp(e)
)}
@wheel=${(e:MouseEvent) => handleEvent(e,(e:WheelEvent) =>
screen.handleScroll(e)
)}>
<div id=${subscribe(manager.barAlpha.pipe(map(val =>
(val == "1") ? "shown" : ""
)))}
class=createBar>
<div class="topContainer">
<div>
<input name="ComponentName" id="nameInput"
placeholder=${subscribe(manager.placeholder)}
></input>
</div>
</div>
</div>
<svg height=${ subscribe(screen.height) }
width=${ subscribe(screen.width) }
viewBox=${subscribe(screen.viewBox)}>
${ subscribe(manager.svgs) } ${ subscribe(manager.svgs) }
</svg> </svg>
</div>
`, document.body) `, document.body)
manager.update() manager.update()