Error handling + fixed pin connecting
This commit is contained in:
parent
d38ce7cd1b
commit
fbcfb76305
40
package-lock.json
generated
40
package-lock.json
generated
|
@ -2194,6 +2194,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"classnames": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
|
||||||
|
},
|
||||||
"clean-css": {
|
"clean-css": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
|
||||||
|
@ -3058,6 +3063,14 @@
|
||||||
"utila": "~0.4"
|
"utila": "~0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dom-helpers": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dom-serializer": {
|
"dom-serializer": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
|
||||||
|
@ -7715,6 +7728,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||||
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
||||||
},
|
},
|
||||||
|
"react-lifecycles-compat": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||||
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
|
||||||
|
@ -7761,6 +7779,28 @@
|
||||||
"tiny-warning": "^1.0.0"
|
"tiny-warning": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-toastify": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-YHTTey7JWqXVkkBIeJ34PAvQELmGfLEGCx9bu68aIZYd+kRU2u9k/nG3AydgbX/uevIb4QNpeeE98DjkooMs5w==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.2",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-transition-group": "^2.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-transition-group": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
|
||||||
|
"requires": {
|
||||||
|
"dom-helpers": "^3.4.0",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react-lifecycles-compat": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"read-pkg": {
|
"read-pkg": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-router-dom": "^5.0.1",
|
"react-router-dom": "^5.0.1",
|
||||||
|
"react-toastify": "^5.3.2",
|
||||||
"rxjs": "^6.5.2",
|
"rxjs": "^6.5.2",
|
||||||
"rxjs-hooks": "^0.5.1"
|
"rxjs-hooks": "^0.5.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,8 @@ import React from 'react'
|
||||||
import App from './modules/core/components/App'
|
import App from './modules/core/components/App'
|
||||||
|
|
||||||
import { render } from 'react-dom'
|
import { render } from 'react-dom'
|
||||||
|
import { handleErrors } from './modules/errors/helpers/handlErrors'
|
||||||
|
|
||||||
render(<App />, document.getElementById('app'))
|
render(<App />, document.getElementById('app'))
|
||||||
|
|
||||||
|
handleErrors()
|
||||||
|
|
|
@ -8,4 +8,5 @@ body {
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
background-color: #222222;
|
background-color: #222222;
|
||||||
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import '../styles/reset'
|
import '../styles/reset'
|
||||||
import './App.scss'
|
import './App.scss'
|
||||||
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
import Canvas from './Canvas'
|
import Canvas from './Canvas'
|
||||||
|
import { ToastContainer } from 'react-toastify'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return <Canvas />
|
return (
|
||||||
|
<>
|
||||||
|
<ToastContainer
|
||||||
|
position="top-left"
|
||||||
|
autoClose={5000}
|
||||||
|
hideProgressBar={false}
|
||||||
|
newestOnTop={false}
|
||||||
|
closeOnClick
|
||||||
|
rtl={false}
|
||||||
|
draggable
|
||||||
|
pauseOnHover
|
||||||
|
/>
|
||||||
|
<Canvas />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|
9
src/modules/errors/classes/SimulationError.ts
Normal file
9
src/modules/errors/classes/SimulationError.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export class SimulationError extends Error {
|
||||||
|
public constructor(public mesagge: string = '') {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return `SimulationError: ${this.mesagge}`
|
||||||
|
}
|
||||||
|
}
|
14
src/modules/errors/helpers/handlErrors.ts
Normal file
14
src/modules/errors/helpers/handlErrors.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { createToastArguments } from '../../toasts/helpers/createToastArguments'
|
||||||
|
|
||||||
|
export const handleErrors = () => {
|
||||||
|
window.onerror = (a, b, c, d, error) => {
|
||||||
|
if (error) {
|
||||||
|
const args = createToastArguments(error.toString())
|
||||||
|
|
||||||
|
toast.error(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(a)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,18 @@
|
||||||
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
|
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
|
||||||
import { Gate } from '../../simulation/classes/Gate'
|
import { Gate, PinWrapper } from '../../simulation/classes/Gate'
|
||||||
import {
|
import {
|
||||||
GateState,
|
GateState,
|
||||||
TransformState,
|
TransformState,
|
||||||
RendererState,
|
RendererState,
|
||||||
CameraState,
|
CameraState,
|
||||||
SimulationState
|
SimulationState,
|
||||||
|
WireState,
|
||||||
|
WireLimit
|
||||||
} from '../types/SimulationSave'
|
} from '../types/SimulationSave'
|
||||||
import { Transform } from '../../../common/math/classes/Transform'
|
import { Transform } from '../../../common/math/classes/Transform'
|
||||||
import { Camera } from '../../simulationRenderer/classes/Camera'
|
import { Camera } from '../../simulationRenderer/classes/Camera'
|
||||||
import { Simulation } from '../../simulation/classes/Simulation'
|
import { Simulation } from '../../simulation/classes/Simulation'
|
||||||
|
import { Wire } from '../../simulation/classes/Wire'
|
||||||
|
|
||||||
export const getTransformState = (transform: Transform): TransformState => {
|
export const getTransformState = (transform: Transform): TransformState => {
|
||||||
return {
|
return {
|
||||||
|
@ -25,9 +28,26 @@ export const getCameraState = (camera: Camera): CameraState => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getWireLimit = (pin: PinWrapper): WireLimit => {
|
||||||
|
return {
|
||||||
|
id: pin.value.gate.id,
|
||||||
|
index: pin.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getWireState = (wire: Wire): WireState => {
|
||||||
|
return {
|
||||||
|
from: getWireLimit(wire.start),
|
||||||
|
to: getWireLimit(wire.end),
|
||||||
|
id: wire.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getSimulationState = (simulation: Simulation): SimulationState => {
|
export const getSimulationState = (simulation: Simulation): SimulationState => {
|
||||||
return {
|
return {
|
||||||
gates: Array.from(simulation.gates).map(getGateState)
|
gates: Array.from(simulation.gates).map(getGateState),
|
||||||
|
wires: simulation.wires.map(getWireState),
|
||||||
|
mode: simulation.mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { vector2 } from '../../../common/math/classes/Transform'
|
import { vector2 } from '../../../common/math/classes/Transform'
|
||||||
|
|
||||||
|
export type simulationMode = 'ic' | 'project'
|
||||||
|
|
||||||
export interface TransformState {
|
export interface TransformState {
|
||||||
position: vector2
|
position: vector2
|
||||||
scale: vector2
|
scale: vector2
|
||||||
|
@ -16,8 +18,22 @@ export interface CameraState {
|
||||||
transform: TransformState
|
transform: TransformState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WireLimit {
|
||||||
|
id: number
|
||||||
|
index: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WireState {
|
||||||
|
from: WireLimit
|
||||||
|
to: WireLimit
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface SimulationState {
|
export interface SimulationState {
|
||||||
gates: GateState[]
|
gates: GateState[]
|
||||||
|
wires: WireState[]
|
||||||
|
|
||||||
|
mode: simulationMode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RendererState {
|
export interface RendererState {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Pin } from './Pin'
|
||||||
import merge from 'deepmerge'
|
import merge from 'deepmerge'
|
||||||
import { GateTemplate, PinCount } from '../types/GateTemplate'
|
import { GateTemplate, PinCount } from '../types/GateTemplate'
|
||||||
import { DefaultGateTemplate } from '../constants'
|
import { DefaultGateTemplate } from '../constants'
|
||||||
|
import { idStore } from '../stores/idStore'
|
||||||
|
|
||||||
export interface GatePins {
|
export interface GatePins {
|
||||||
inputs: Pin[]
|
inputs: Pin[]
|
||||||
|
@ -16,22 +17,30 @@ export interface PinWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Gate {
|
export class Gate {
|
||||||
public static lastId = 0
|
|
||||||
|
|
||||||
public transform = new Transform()
|
public transform = new Transform()
|
||||||
public id = Gate.lastId++
|
|
||||||
public _pins: GatePins = {
|
public _pins: GatePins = {
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: []
|
outputs: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public id: number
|
||||||
public template: GateTemplate
|
public template: GateTemplate
|
||||||
|
|
||||||
public constructor(template: DeepPartial<GateTemplate> = {}) {
|
public constructor(template: DeepPartial<GateTemplate> = {}, id?: number) {
|
||||||
this.template = merge(DefaultGateTemplate, template) as GateTemplate
|
this.template = merge(DefaultGateTemplate, template) as GateTemplate
|
||||||
|
|
||||||
this._pins.inputs = Gate.generatePins(this.template.pins.inputs, 1)
|
this._pins.inputs = Gate.generatePins(
|
||||||
this._pins.outputs = Gate.generatePins(this.template.pins.outputs, 2)
|
this.template.pins.inputs,
|
||||||
|
1,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
this._pins.outputs = Gate.generatePins(
|
||||||
|
this.template.pins.outputs,
|
||||||
|
2,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
|
this.id = id !== undefined ? id : idStore.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private wrapPins(pins: Pin[]) {
|
private wrapPins(pins: Pin[]) {
|
||||||
|
@ -58,7 +67,9 @@ export class Gate {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private static generatePins(options: PinCount, type: number) {
|
private static generatePins(options: PinCount, type: number, gate: Gate) {
|
||||||
return [...Array(options.count)].fill(true).map(() => new Pin(type))
|
return [...Array(options.count)]
|
||||||
|
.fill(true)
|
||||||
|
.map(() => new Pin(type, gate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SubscriptionData } from '../types/SubscriptionData'
|
import { SubscriptionData } from '../types/SubscriptionData'
|
||||||
import { BehaviorSubject } from 'rxjs'
|
import { BehaviorSubject } from 'rxjs'
|
||||||
|
import { Gate } from './Gate'
|
||||||
|
|
||||||
/* Types:
|
/* Types:
|
||||||
|
|
||||||
|
@ -9,24 +10,25 @@ Second bit = output
|
||||||
*/
|
*/
|
||||||
export class Pin {
|
export class Pin {
|
||||||
public state = new BehaviorSubject(false)
|
public state = new BehaviorSubject(false)
|
||||||
public connectedTo = new Set<Pin>()
|
public pairs = new Set<Pin>()
|
||||||
|
|
||||||
private pairs = new Set<Pin>()
|
|
||||||
private subscriptions: SubscriptionData<Pin>[] = []
|
private subscriptions: SubscriptionData<Pin>[] = []
|
||||||
|
|
||||||
public constructor(public type = 0b01) {}
|
public constructor(public type = 0b01, public gate: Gate) {}
|
||||||
|
|
||||||
public addPair(pin: Pin) {
|
public addPair(pin: Pin, subscribe = false) {
|
||||||
this.pairs.add(pin)
|
this.pairs.add(pin)
|
||||||
|
|
||||||
const rawSubscription = pin.state.subscribe(state => {
|
if (subscribe) {
|
||||||
this.state.next(state)
|
const rawSubscription = pin.state.subscribe(state => {
|
||||||
})
|
this.state.next(state)
|
||||||
|
})
|
||||||
|
|
||||||
this.subscriptions.push({
|
this.subscriptions.push({
|
||||||
data: pin,
|
data: pin,
|
||||||
subscription: rawSubscription
|
subscription: rawSubscription
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public removePair(pin: Pin) {
|
public removePair(pin: Pin) {
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { Gate } from './Gate'
|
import { Gate } from './Gate'
|
||||||
import { GateStorage } from './GateStorage'
|
import { GateStorage } from './GateStorage'
|
||||||
import { LruCacheNode } from '@eix-js/utils'
|
import { LruCacheNode } from '@eix-js/utils'
|
||||||
|
import { Wire } from './Wire'
|
||||||
|
import { simulationMode } from '../../saving/types/SimulationSave'
|
||||||
|
|
||||||
export class Simulation {
|
export class Simulation {
|
||||||
public gates = new GateStorage()
|
public gates = new GateStorage()
|
||||||
|
public wires: Wire[] = []
|
||||||
|
|
||||||
|
public constructor(public mode: simulationMode = 'project') {}
|
||||||
|
|
||||||
public push(...gates: Gate[]) {
|
public push(...gates: Gate[]) {
|
||||||
for (const gate of gates) {
|
for (const gate of gates) {
|
||||||
|
|
27
src/modules/simulation/classes/Wire.ts
Normal file
27
src/modules/simulation/classes/Wire.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { idStore } from '../stores/idStore'
|
||||||
|
import { PinWrapper } from './Gate'
|
||||||
|
import { SimulationError } from '../../errors/classes/SimulationError'
|
||||||
|
|
||||||
|
export class Wire {
|
||||||
|
public id: number
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public start: PinWrapper,
|
||||||
|
public end: PinWrapper,
|
||||||
|
id?: number
|
||||||
|
) {
|
||||||
|
if (end.value.pairs.size !== 0) {
|
||||||
|
throw new SimulationError('An input pin can only have 1 pair')
|
||||||
|
}
|
||||||
|
|
||||||
|
end.value.addPair(start.value, true)
|
||||||
|
start.value.addPair(end.value)
|
||||||
|
|
||||||
|
this.id = id !== undefined ? id : idStore.generate()
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.end.value.removePair(this.start.value)
|
||||||
|
this.start.value.removePair(this.end.value)
|
||||||
|
}
|
||||||
|
}
|
12
src/modules/simulation/stores/idStore.ts
Normal file
12
src/modules/simulation/stores/idStore.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { LocalStore } from '../../storage/classes/LocalStore'
|
||||||
|
|
||||||
|
const store = new LocalStore<number>('id')
|
||||||
|
|
||||||
|
export const idStore = {
|
||||||
|
generate() {
|
||||||
|
const current = store.get()
|
||||||
|
store.set(current + 1)
|
||||||
|
|
||||||
|
return current + 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import { getPinPosition } from '../helpers/pinPosition'
|
||||||
import { pointInCircle } from '../../../common/math/helpers/pointInCircle'
|
import { pointInCircle } from '../../../common/math/helpers/pointInCircle'
|
||||||
import { SelectedPins } from '../types/SelectedPins'
|
import { SelectedPins } from '../types/SelectedPins'
|
||||||
import { getRendererState } from '../../saving/helpers/getState'
|
import { getRendererState } from '../../saving/helpers/getState'
|
||||||
|
import { Wire } from '../../simulation/classes/Wire'
|
||||||
|
|
||||||
export class SimulationRenderer {
|
export class SimulationRenderer {
|
||||||
public mouseDownOutput = new Subject<MouseEventInfo>()
|
public mouseDownOutput = new Subject<MouseEventInfo>()
|
||||||
|
@ -104,11 +105,21 @@ export class SimulationRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedPins.start && this.selectedPins.end) {
|
if (
|
||||||
console.log('Connecting!')
|
this.selectedPins.start &&
|
||||||
console.log(getRendererState(this))
|
this.selectedPins.end &&
|
||||||
|
this.selectedPins.end.wrapper.value.pairs.size === 0
|
||||||
|
) {
|
||||||
|
this.simulation.wires.push(
|
||||||
|
new Wire(
|
||||||
|
this.selectedPins.start.wrapper,
|
||||||
|
this.selectedPins.end.wrapper
|
||||||
|
)
|
||||||
|
)
|
||||||
this.selectedPins.start = null
|
this.selectedPins.start = null
|
||||||
this.selectedPins.end = null
|
this.selectedPins.end = null
|
||||||
|
|
||||||
|
console.log(getRendererState(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,14 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
||||||
connectionLength: 30,
|
connectionLength: 30,
|
||||||
pinRadius: 10,
|
pinRadius: 10,
|
||||||
pinStrokeColor: '#888888',
|
pinStrokeColor: '#888888',
|
||||||
pinStrokeWidth: 3
|
pinStrokeWidth: 3,
|
||||||
|
pinFill: {
|
||||||
|
open: 'rgb(255,216,20)',
|
||||||
|
closed: 'rgb(90,90,90)'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
wires: {
|
wires: {
|
||||||
temporaryWireColor: `rgba(128,128,128,0.5)`
|
temporaryWireColor: `rgba(128,128,128,0.5)`,
|
||||||
|
curvePointOffset: 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Pin } from '../../simulation/classes/Pin'
|
import { Pin } from '../../simulation/classes/Pin'
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
|
||||||
export const pinFill = (pin: Pin) => {
|
export const pinFill = (renderer: SimulationRenderer, pin: Pin) => {
|
||||||
let color = 'rgba(0,0,0,0)'
|
let color = 'rgba(0,0,0,0)'
|
||||||
|
|
||||||
if (pin.connectedTo.size) {
|
if (pin.pairs.size) {
|
||||||
if (pin.state) {
|
if (pin.state.value) {
|
||||||
color = 'yellow'
|
color = renderer.options.gates.pinFill.open
|
||||||
} else {
|
} else {
|
||||||
color = 'grey'
|
color = renderer.options.gates.pinFill.closed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const renderPins = (
|
||||||
ctx.lineWidth = pinStrokeWidth
|
ctx.lineWidth = pinStrokeWidth
|
||||||
|
|
||||||
for (const pin of gate.pins) {
|
for (const pin of gate.pins) {
|
||||||
ctx.fillStyle = pinFill(pin.value)
|
ctx.fillStyle = pinFill(renderer, pin.value)
|
||||||
|
|
||||||
// render little connection
|
// render little connection
|
||||||
const start = calculatePinStart(
|
const start = calculatePinStart(
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { invert } from '../../vector2/helpers/basic'
|
||||||
import { renderGate } from './renderGate'
|
import { renderGate } from './renderGate'
|
||||||
import { clearCanvas } from '../../../common/canvas/helpers/clearCanvas'
|
import { clearCanvas } from '../../../common/canvas/helpers/clearCanvas'
|
||||||
import { renderClickedPins } from './renderClickedPins'
|
import { renderClickedPins } from './renderClickedPins'
|
||||||
|
import { renderWires } from './renderWires'
|
||||||
|
|
||||||
export const renderSimulation = (
|
export const renderSimulation = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
|
@ -12,7 +13,10 @@ export const renderSimulation = (
|
||||||
|
|
||||||
ctx.translate(...renderer.camera.transform.position)
|
ctx.translate(...renderer.camera.transform.position)
|
||||||
|
|
||||||
// render gates
|
for (const wire of renderer.simulation.wires) {
|
||||||
|
renderWires(ctx, renderer, wire)
|
||||||
|
}
|
||||||
|
|
||||||
for (const gate of renderer.simulation.gates) {
|
for (const gate of renderer.simulation.gates) {
|
||||||
renderGate(ctx, renderer, gate)
|
renderGate(ctx, renderer, gate)
|
||||||
}
|
}
|
||||||
|
|
94
src/modules/simulationRenderer/helpers/renderWires.ts
Normal file
94
src/modules/simulationRenderer/helpers/renderWires.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
import { pinFill } from './pinFill'
|
||||||
|
import { getPinPosition } from './pinPosition'
|
||||||
|
import { Wire } from '../../simulation/classes/Wire'
|
||||||
|
import { wireRadius } from './wireRadius'
|
||||||
|
import { clamp } from '../../simulation/helpers/clamp'
|
||||||
|
|
||||||
|
export const renderWires = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
renderer: SimulationRenderer,
|
||||||
|
wire: Wire
|
||||||
|
) => {
|
||||||
|
const { start, end } = wire
|
||||||
|
const startPosition = getPinPosition(
|
||||||
|
renderer,
|
||||||
|
start.value.gate.transform,
|
||||||
|
start
|
||||||
|
)
|
||||||
|
const endPosition = getPinPosition(renderer, end.value.gate.transform, end)
|
||||||
|
const length = renderer.options.wires.curvePointOffset
|
||||||
|
const centerY = (startPosition[1] + endPosition[1]) / 2
|
||||||
|
const controlPostions = [startPosition[0] + length, endPosition[0] - length]
|
||||||
|
|
||||||
|
ctx.strokeStyle = pinFill(renderer, start.value)
|
||||||
|
ctx.lineWidth = wireRadius(renderer)
|
||||||
|
ctx.lineCap = 'round'
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(...startPosition)
|
||||||
|
|
||||||
|
if (startPosition[0] < endPosition[0]) {
|
||||||
|
ctx.bezierCurveTo(
|
||||||
|
controlPostions[0],
|
||||||
|
startPosition[1],
|
||||||
|
controlPostions[1],
|
||||||
|
endPosition[1],
|
||||||
|
...endPosition
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const { abs, PI } = Math
|
||||||
|
|
||||||
|
const baseFactor = startPosition[1] < endPosition[1] ? 1 : -1
|
||||||
|
const factors = [baseFactor, baseFactor]
|
||||||
|
|
||||||
|
const radiuses = [...Array(2)].fill(
|
||||||
|
abs((centerY - startPosition[1]) / 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
const limit = 70
|
||||||
|
if (radiuses[0] < limit) {
|
||||||
|
factors[0] *= -1
|
||||||
|
radiuses[0] = limit
|
||||||
|
radiuses[1] = abs(
|
||||||
|
(startPosition[1] +
|
||||||
|
factors[0] * 2 * radiuses[0] -
|
||||||
|
endPosition[1]) /
|
||||||
|
2
|
||||||
|
)
|
||||||
|
// radiuses[0] =
|
||||||
|
}
|
||||||
|
|
||||||
|
const centerPosition = [
|
||||||
|
startPosition[1] + factors[0] * radiuses[0],
|
||||||
|
endPosition[1] - factors[1] * radiuses[1]
|
||||||
|
]
|
||||||
|
|
||||||
|
ctx.arc(
|
||||||
|
controlPostions[0],
|
||||||
|
centerPosition[0],
|
||||||
|
radiuses[0],
|
||||||
|
(-factors[0] * PI) / 2,
|
||||||
|
(factors[0] * PI) / 2,
|
||||||
|
factors[0] !== 1
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.lineTo(
|
||||||
|
controlPostions[1],
|
||||||
|
endPosition[1] - factors[1] * 2 * radiuses[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.arc(
|
||||||
|
controlPostions[1],
|
||||||
|
centerPosition[1],
|
||||||
|
radiuses[1],
|
||||||
|
(-factors[1] * PI) / 2,
|
||||||
|
(factors[1] * PI) / 2,
|
||||||
|
factors[1] === 1
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.lineTo(...endPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
9
src/modules/simulationRenderer/helpers/wireRadius.ts
Normal file
9
src/modules/simulationRenderer/helpers/wireRadius.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
|
||||||
|
export const wireRadius = (renderer: SimulationRenderer) => {
|
||||||
|
return (
|
||||||
|
2 *
|
||||||
|
(renderer.options.gates.pinRadius -
|
||||||
|
renderer.options.gates.pinStrokeWidth)
|
||||||
|
)
|
||||||
|
}
|
|
@ -7,8 +7,13 @@ export interface SimulationRendererOptions {
|
||||||
pinRadius: number
|
pinRadius: number
|
||||||
pinStrokeColor: string
|
pinStrokeColor: string
|
||||||
pinStrokeWidth: number
|
pinStrokeWidth: number
|
||||||
|
pinFill: {
|
||||||
|
open: string
|
||||||
|
closed: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wires: {
|
wires: {
|
||||||
temporaryWireColor: string
|
temporaryWireColor: string
|
||||||
|
curvePointOffset: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
56
src/modules/storage/classes/LocalStore.ts
Normal file
56
src/modules/storage/classes/LocalStore.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { CacheInstancesByKey } from '@eix-js/utils'
|
||||||
|
|
||||||
|
@CacheInstancesByKey(Infinity)
|
||||||
|
export class LocalStore<T> {
|
||||||
|
public constructor(public name: string) {
|
||||||
|
if (!localStorage.getItem(name)) {
|
||||||
|
localStorage.setItem(name, '{}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAll(): Record<string, T> {
|
||||||
|
const raw = localStorage.getItem(this.name)
|
||||||
|
|
||||||
|
if (!raw)
|
||||||
|
throw new Error(
|
||||||
|
`An error occured when accesing ${
|
||||||
|
this.name
|
||||||
|
} in the local storage!`
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
return JSON.parse(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ls(): string[] {
|
||||||
|
return Object.keys(this.getAll())
|
||||||
|
}
|
||||||
|
|
||||||
|
public *[Symbol.iterator](): Iterable<T> {
|
||||||
|
for (const item of this.ls()) {
|
||||||
|
return this.get(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key = 'index') {
|
||||||
|
return this.getAll()[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key: string | T = 'index', value?: T) {
|
||||||
|
if (typeof key !== 'string' || value === undefined) {
|
||||||
|
localStorage.setItem(
|
||||||
|
this.name,
|
||||||
|
JSON.stringify({
|
||||||
|
index: key
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(
|
||||||
|
this.name,
|
||||||
|
JSON.stringify({
|
||||||
|
[key]: value
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/modules/toasts/helpers/createToastArguments.ts
Normal file
15
src/modules/toasts/helpers/createToastArguments.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { ToastOptions } from 'react-toastify'
|
||||||
|
|
||||||
|
export const createToastArguments = (
|
||||||
|
message: string
|
||||||
|
): [string, ToastOptions] => [
|
||||||
|
message,
|
||||||
|
{
|
||||||
|
position: 'top-left',
|
||||||
|
autoClose: 5000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
]
|
|
@ -17,18 +17,27 @@ const babelRule = {
|
||||||
use: 'babel-loader'
|
use: 'babel-loader'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cssAndSass = [
|
||||||
|
isProduction
|
||||||
|
? MiniCssExtractPlugin.loader
|
||||||
|
: {
|
||||||
|
loader: 'style-loader',
|
||||||
|
options: {
|
||||||
|
singleton: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'css-loader'
|
||||||
|
]
|
||||||
|
|
||||||
|
const cssRule = {
|
||||||
|
test: /\.css$/,
|
||||||
|
use: cssAndSass
|
||||||
|
}
|
||||||
|
|
||||||
const sassRule = {
|
const sassRule = {
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: [
|
use: [
|
||||||
isProduction
|
...cssAndSass,
|
||||||
? MiniCssExtractPlugin.loader
|
|
||||||
: {
|
|
||||||
loader: 'style-loader',
|
|
||||||
options: {
|
|
||||||
singleton: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ loader: 'css-loader' },
|
|
||||||
{
|
{
|
||||||
loader: 'sass-loader',
|
loader: 'sass-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -47,7 +56,7 @@ const baseConfig = {
|
||||||
publicPath: '/'
|
publicPath: '/'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [babelRule, sassRule]
|
rules: [babelRule, sassRule, cssRule]
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.ts', '.tsx', '.scss']
|
extensions: ['.js', '.ts', '.tsx', '.scss']
|
||||||
|
|
Loading…
Reference in a new issue