gate props and fixed the pins not beenig set to false bug
This commit is contained in:
parent
f06fe88df5
commit
285248435a
15
.vscode/template.code-snippets
vendored
Normal file
15
.vscode/template.code-snippets
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"Gate template": {
|
||||||
|
"prefix": "template",
|
||||||
|
"body": [
|
||||||
|
"import { PartialTemplate } from '../types/PartialTemplate'",
|
||||||
|
"",
|
||||||
|
"/**",
|
||||||
|
" * The template of the ${1:and} gate",
|
||||||
|
" */",
|
||||||
|
"const ${1}Template: PartialTemplate = ${2:[]}",
|
||||||
|
"",
|
||||||
|
"export default ${1}Template"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,12 @@ import { fromSimulationState } from '../../saving/helpers/fromState'
|
||||||
|
|
||||||
export interface Context {
|
export interface Context {
|
||||||
memory: Record<string, unknown>
|
memory: Record<string, unknown>
|
||||||
|
getProperty: (name: string) => unknown
|
||||||
|
setProperty: (name: string, value: unknown) => void
|
||||||
get: (index: number) => boolean
|
get: (index: number) => boolean
|
||||||
set: (index: number, state: boolean) => void
|
set: (index: number, state: boolean) => void
|
||||||
color: (color: string) => void
|
color: (color: string) => void
|
||||||
|
update: () => void
|
||||||
enviroment: SimulationEnv
|
enviroment: SimulationEnv
|
||||||
colors: Record<string, string>
|
colors: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'react-toastify/dist/ReactToastify.css'
|
||||||
|
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
import { theme as muiTheme } from '../constants'
|
import { theme as muiTheme } from '../constants'
|
||||||
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
|
import { Route } from 'react-router-dom'
|
||||||
|
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
|
@ -15,6 +15,7 @@ import Head from './Head'
|
||||||
import Root from './Root'
|
import Root from './Root'
|
||||||
import LogicGatePage from '../../logic-gates/components/LogicGatesPage'
|
import LogicGatePage from '../../logic-gates/components/LogicGatesPage'
|
||||||
import { loadSubject } from '../subjects/loadedSubject'
|
import { loadSubject } from '../subjects/loadedSubject'
|
||||||
|
import { CustomRouter } from './CustomRouter'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -27,12 +28,12 @@ const App = () => {
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
||||||
<Theme theme={muiTheme}>
|
<Theme theme={muiTheme}>
|
||||||
<Router>
|
<CustomRouter>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|
||||||
<Route path="/" component={Root} exact />
|
<Route path="/" component={Root} exact />
|
||||||
<Route path="/gates" component={LogicGatePage} />
|
<Route path="/gates" component={LogicGatePage} />
|
||||||
</Router>
|
</CustomRouter>
|
||||||
</Theme>
|
</Theme>
|
||||||
|
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
|
|
6
src/modules/core/components/CustomRouter.tsx
Normal file
6
src/modules/core/components/CustomRouter.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
|
import { history } from '../constants'
|
||||||
|
|
||||||
|
export class CustomRouter extends BrowserRouter {
|
||||||
|
public history = history
|
||||||
|
}
|
|
@ -2,12 +2,14 @@ import React from 'react'
|
||||||
import Canvas from './Canvas'
|
import Canvas from './Canvas'
|
||||||
import CreateSimulation from '../../create-simulation/components/CreateSimulation'
|
import CreateSimulation from '../../create-simulation/components/CreateSimulation'
|
||||||
import Input from '../../input/components/Input'
|
import Input from '../../input/components/Input'
|
||||||
|
import GateProperties from '../../logic-gates/components/GatePropertiesModal'
|
||||||
|
|
||||||
const Root = () => {
|
const Root = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Canvas />
|
<Canvas />
|
||||||
<CreateSimulation />
|
<CreateSimulation />
|
||||||
|
<GateProperties />
|
||||||
<Input />
|
<Input />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createMuiTheme } from '@material-ui/core/styles'
|
import { createMuiTheme } from '@material-ui/core/styles'
|
||||||
import { red, deepPurple } from '@material-ui/core/colors'
|
import { red, deepPurple } from '@material-ui/core/colors'
|
||||||
import { simulationMode } from '../saving/types/SimulationSave'
|
import { simulationMode } from '../saving/types/SimulationSave'
|
||||||
|
import { createBrowserHistory } from 'history'
|
||||||
|
|
||||||
export const theme = createMuiTheme({
|
export const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
|
@ -31,3 +32,8 @@ export const icons: IconInterface = {
|
||||||
* The width of the sidebar
|
* The width of the sidebar
|
||||||
*/
|
*/
|
||||||
export const sidebarWidth = 240
|
export const sidebarWidth = 240
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The history to be used by the router
|
||||||
|
*/
|
||||||
|
export const history = createBrowserHistory()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@import '../colors.scss';
|
@import '../colors.scss';
|
||||||
|
|
||||||
.MuiListItem-root.contained {
|
.contained {
|
||||||
// i spent hours trying to find a better solution
|
// i spent hours trying to find a better solution
|
||||||
background-color: $primary !important;
|
background-color: $primary !important;
|
||||||
}
|
}
|
||||||
|
|
53
src/modules/logic-gates/components/GateProperties.scss
Normal file
53
src/modules/logic-gates/components/GateProperties.scss
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
@import '../../core/styles/mixins/flex.scss';
|
||||||
|
@import '../../core/styles/colors.scss';
|
||||||
|
@import '../../core/styles/mixins/visibility.scss';
|
||||||
|
@import '../../modals/styles/mixins/modal-container.scss';
|
||||||
|
@import '../../modals/styles/mixins/modal-title.scss';
|
||||||
|
|
||||||
|
$gate-props-margin: 1rem;
|
||||||
|
|
||||||
|
#gate-properties-modal {
|
||||||
|
@include modal-container();
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible#gate-properties-modal {
|
||||||
|
@include visible();
|
||||||
|
}
|
||||||
|
|
||||||
|
div #gate-properties-container {
|
||||||
|
@include flex;
|
||||||
|
|
||||||
|
background-color: $grey;
|
||||||
|
padding: $gate-props-margin * 4;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gate-props-divider {
|
||||||
|
margin-top: $gate-props-margin;
|
||||||
|
margin-bottom: $gate-props-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
div #gate-props-title {
|
||||||
|
color: white;
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .gate-prop-container {
|
||||||
|
@include flex();
|
||||||
|
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
div #save-props {
|
||||||
|
width: 50%;
|
||||||
|
margin: $gate-props-margin * 2;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
138
src/modules/logic-gates/components/GatePropertiesModal.tsx
Normal file
138
src/modules/logic-gates/components/GatePropertiesModal.tsx
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import './GateProperties.scss'
|
||||||
|
import React, { ChangeEvent } from 'react'
|
||||||
|
import { getRendererSafely } from '../helpers/getRendererSafely'
|
||||||
|
import { Property } from '../../simulation/types/GateTemplate'
|
||||||
|
import { BehaviorSubject } from 'rxjs'
|
||||||
|
import { useObservable } from 'rxjs-hooks'
|
||||||
|
import Divider from '@material-ui/core/Divider'
|
||||||
|
import TextField from '@material-ui/core/TextField'
|
||||||
|
import CheckBox from '@material-ui/core/Checkbox'
|
||||||
|
import { open, id } from '../subjects/LogicGatePropsSubjects'
|
||||||
|
import { Gate } from '../../simulation/classes/Gate'
|
||||||
|
|
||||||
|
export interface GatePropertyProps {
|
||||||
|
raw: Property
|
||||||
|
name: string
|
||||||
|
output: BehaviorSubject<string | number | boolean>
|
||||||
|
gate: Gate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a single props of the gate
|
||||||
|
*
|
||||||
|
* @param param0 The props passed to the component
|
||||||
|
*/
|
||||||
|
export const GatePropery = ({ name, raw, output, gate }: GatePropertyProps) => {
|
||||||
|
const outputSnapshot = useObservable(() => output, '')
|
||||||
|
|
||||||
|
const displayableName = `${name[0].toUpperCase()}${name.slice(1)}:`
|
||||||
|
|
||||||
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
let value: boolean | string | number = target.value
|
||||||
|
|
||||||
|
if (raw.type === 'boolean') {
|
||||||
|
value = target.checked
|
||||||
|
} else if (raw.type === 'number') {
|
||||||
|
value = Number(target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
gate.props[name] = value
|
||||||
|
|
||||||
|
if (raw.type !== 'boolean') {
|
||||||
|
output.next(target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw.needsUpdate === true) {
|
||||||
|
gate.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let input = <></>
|
||||||
|
if (raw.type === 'number' || raw.type === 'text') {
|
||||||
|
input = (
|
||||||
|
<TextField
|
||||||
|
onChange={handleChange}
|
||||||
|
label={displayableName}
|
||||||
|
value={outputSnapshot}
|
||||||
|
type={raw.type}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (raw.type === 'boolean') {
|
||||||
|
input = (
|
||||||
|
<>
|
||||||
|
<span className="checkbox-label">{displayableName}</span>
|
||||||
|
<CheckBox
|
||||||
|
onClick={() => {
|
||||||
|
output.next(!outputSnapshot)
|
||||||
|
}}
|
||||||
|
onChange={handleChange}
|
||||||
|
checked={!!outputSnapshot}
|
||||||
|
/>{' '}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="gate-prop-container">{input}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The props page of any gate wich has props enabled
|
||||||
|
*
|
||||||
|
* @param props The react props of the component
|
||||||
|
*/
|
||||||
|
const GateProperties = () => {
|
||||||
|
const openSnapshot = useObservable(() => open, false)
|
||||||
|
const renderer = getRendererSafely()
|
||||||
|
|
||||||
|
const node = renderer.simulation.gates.get(id.value)
|
||||||
|
|
||||||
|
if (!(node && node.data && node.data.template.properties.enabled)) {
|
||||||
|
open.next(false)
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const gate = node.data
|
||||||
|
const gateProps = gate.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="gate-properties-modal"
|
||||||
|
className={openSnapshot ? 'visible' : ''}
|
||||||
|
onClick={() => {
|
||||||
|
open.next(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="gate-properties-container"
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div id="gate-props-title">Gate properties</div>
|
||||||
|
<Divider id="gate-props-divider" />
|
||||||
|
|
||||||
|
{gate.template.properties.data.map((raw, index) => {
|
||||||
|
const { name, base } = raw
|
||||||
|
|
||||||
|
const prop = gateProps[name]
|
||||||
|
const output = new BehaviorSubject(
|
||||||
|
gateProps.hasOwnProperty(name) ? prop : base
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GatePropery
|
||||||
|
output={output}
|
||||||
|
raw={raw}
|
||||||
|
name={name}
|
||||||
|
gate={gate}
|
||||||
|
key={`${index}-${id.value}-${output.value}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GateProperties
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { BehaviorSubject, Subject } from 'rxjs'
|
||||||
|
|
||||||
|
export const open = new Subject<boolean>()
|
||||||
|
export const id = new BehaviorSubject<number>(0)
|
|
@ -1,292 +1,32 @@
|
||||||
import { GateTemplate } from '../simulation/types/GateTemplate'
|
import { GateTemplate } from '../simulation/types/GateTemplate'
|
||||||
import { RendererState } from './types/SimulationSave'
|
import { RendererState } from './types/SimulationSave'
|
||||||
|
import andTemplate from './templates/and'
|
||||||
|
import buttonTemplate from './templates/button'
|
||||||
|
import lightTemplate from './templates/light'
|
||||||
|
import nandTemplate from './templates/nand'
|
||||||
|
import norTemplate from './templates/nor'
|
||||||
|
import notTemplate from './templates/not'
|
||||||
|
import orTemplate from './templates/or'
|
||||||
|
import parallelDelayerTemplate from './templates/parallelDelayer'
|
||||||
|
import rgbLightTemplate from './templates/rgb'
|
||||||
|
import sequentialDelayerTemplate from './templates/sequentialDelayer'
|
||||||
|
import xnorTemplate from './templates/xnor'
|
||||||
|
import xorTemplate from './templates/xor'
|
||||||
|
|
||||||
export const defaultSimulationName = 'default'
|
export const defaultSimulationName = 'default'
|
||||||
export const baseTemplates: DeepPartial<GateTemplate>[] = [
|
export const baseTemplates: DeepPartial<GateTemplate>[] = [
|
||||||
{
|
andTemplate,
|
||||||
metadata: {
|
buttonTemplate,
|
||||||
name: 'and'
|
lightTemplate,
|
||||||
},
|
nandTemplate,
|
||||||
material: {
|
norTemplate,
|
||||||
type: 'image',
|
notTemplate,
|
||||||
fill: require('../../assets/and')
|
orTemplate,
|
||||||
},
|
parallelDelayerTemplate,
|
||||||
code: {
|
rgbLightTemplate,
|
||||||
activation: `context.set(0, context.get(0) && context.get(1))`
|
sequentialDelayerTemplate,
|
||||||
},
|
xnorTemplate,
|
||||||
pins: {
|
xorTemplate
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/AND_gate']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'nand'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/nand')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `context.set(0, !context.get(0) || !context.get(1))`
|
|
||||||
},
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/NAND_gate']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'or'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/or')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `context.set(0, context.get(0) || context.get(1))`
|
|
||||||
},
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/OR_gate']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'nor'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/nor')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `context.set(0, !(context.get(0) || context.get(1)))`
|
|
||||||
},
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/NOR_gate']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'xor'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/xor')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const a = context.get(0)
|
|
||||||
const b = context.get(1)
|
|
||||||
const c = (a || b) && (!a || !b)
|
|
||||||
|
|
||||||
context.set(0, c)`
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/XOR_gate'],
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'xnor'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/xnor')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const a = context.get(0)
|
|
||||||
const b = context.get(1)
|
|
||||||
const c = (a && b) || !(a || b)
|
|
||||||
|
|
||||||
context.set(0, c)`
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/XNOR_gate'],
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'not'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/not')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `context.set(0, !context.get(0))`
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/Inverter_(logic_gate)']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'button'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
fill: '#D32F2E',
|
|
||||||
stroke: {
|
|
||||||
normal: '#AB8C31',
|
|
||||||
active: '#7EF813'
|
|
||||||
},
|
|
||||||
colors: {
|
|
||||||
pressed: '#7D1313'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
onClick: `
|
|
||||||
const old = context.memory.state
|
|
||||||
const state = !old
|
|
||||||
|
|
||||||
context.set(0, state)
|
|
||||||
context.color(old ? context.colors.main : context.colors.pressed)
|
|
||||||
|
|
||||||
context.memory.state = state
|
|
||||||
`
|
|
||||||
},
|
|
||||||
pins: {
|
|
||||||
inputs: {
|
|
||||||
count: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
integration: {
|
|
||||||
input: true
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/Push-button']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'light bulb'
|
|
||||||
},
|
|
||||||
shape: {
|
|
||||||
radius: 50
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
fill: '#1C1C1C',
|
|
||||||
stroke: {
|
|
||||||
normal: '#3C3C3C'
|
|
||||||
},
|
|
||||||
colors: {
|
|
||||||
active: '#C6FF00'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const { main, active } = context.colors
|
|
||||||
|
|
||||||
context.color(context.get(0) ? active : main)
|
|
||||||
`
|
|
||||||
},
|
|
||||||
integration: {
|
|
||||||
output: true
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
|
|
||||||
pins: {
|
|
||||||
outputs: {
|
|
||||||
count: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'rgb light'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
fill: '#1C1C1C',
|
|
||||||
colors: {
|
|
||||||
1: '#00f',
|
|
||||||
2: `#0f0`,
|
|
||||||
3: `#0ff`,
|
|
||||||
4: `#f00`,
|
|
||||||
5: `#f0f`,
|
|
||||||
6: `#ff0`,
|
|
||||||
7: `#fff`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const color = (context.get(0) << 2) + (context.get(1) << 1) + context.get(2)
|
|
||||||
|
|
||||||
if (color === 0){
|
|
||||||
context.color(context.colors.main)
|
|
||||||
}
|
|
||||||
|
|
||||||
else{
|
|
||||||
context.color(context.colors[color])
|
|
||||||
}
|
|
||||||
`
|
|
||||||
},
|
|
||||||
integration: {
|
|
||||||
output: true
|
|
||||||
},
|
|
||||||
info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
|
|
||||||
pins: {
|
|
||||||
outputs: {
|
|
||||||
count: 0
|
|
||||||
},
|
|
||||||
inputs: {
|
|
||||||
count: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'Sequential delayer'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/sequential')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const i = context.get(0)
|
|
||||||
return new Promise((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res()
|
|
||||||
},1000)
|
|
||||||
}).then(() => {
|
|
||||||
context.set(0,i)
|
|
||||||
})
|
|
||||||
`,
|
|
||||||
async: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadata: {
|
|
||||||
name: 'Parallel delayer'
|
|
||||||
},
|
|
||||||
material: {
|
|
||||||
type: 'image',
|
|
||||||
fill: require('../../assets/parallel')
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
activation: `
|
|
||||||
const i = context.get(0)
|
|
||||||
return new Promise((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res()
|
|
||||||
},1000)
|
|
||||||
}).then(() => {
|
|
||||||
context.set(0,i)
|
|
||||||
})
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export const baseSave: RendererState = {
|
export const baseSave: RendererState = {
|
||||||
|
|
12
src/modules/saving/data/delayProperties.ts
Normal file
12
src/modules/saving/data/delayProperties.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { GateTemplate } from '../../simulation/types/GateTemplate'
|
||||||
|
|
||||||
|
export const delayProperties: GateTemplate['properties'] = {
|
||||||
|
enabled: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
base: 1000,
|
||||||
|
type: 'number',
|
||||||
|
name: 'delay'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -35,7 +35,8 @@ export const fromSimulationState = (
|
||||||
for (const gateState of state.gates) {
|
for (const gateState of state.gates) {
|
||||||
const gate = new Gate(
|
const gate = new Gate(
|
||||||
templateStore.get(gateState.template),
|
templateStore.get(gateState.template),
|
||||||
gateState.id
|
gateState.id,
|
||||||
|
gateState.props
|
||||||
)
|
)
|
||||||
gate.transform = fromTransformState(gateState.transform)
|
gate.transform = fromTransformState(gateState.transform)
|
||||||
|
|
||||||
|
|
25
src/modules/saving/templates/and.ts
Normal file
25
src/modules/saving/templates/and.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the and gate
|
||||||
|
*/
|
||||||
|
const andTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'and'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/and')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `context.set(0, context.get(0) && context.get(1))`
|
||||||
|
},
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/AND_gate']
|
||||||
|
}
|
||||||
|
|
||||||
|
export default andTemplate
|
56
src/modules/saving/templates/button.ts
Normal file
56
src/modules/saving/templates/button.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the button gate
|
||||||
|
*/
|
||||||
|
const buttonTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'button'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
fill: '#D32F2E',
|
||||||
|
stroke: {
|
||||||
|
normal: '#AB8C31',
|
||||||
|
active: '#7EF813'
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
pressed: '#7D1313'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
onClick: `
|
||||||
|
const active = context.getProperty('active')
|
||||||
|
context.setProperty('active',!active)
|
||||||
|
|
||||||
|
context.update()
|
||||||
|
`,
|
||||||
|
activation: `
|
||||||
|
const state = context.getProperty('active')
|
||||||
|
|
||||||
|
context.set(0, state)
|
||||||
|
context.color(!state ? context.colors.main : context.colors.pressed)
|
||||||
|
`
|
||||||
|
},
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
integration: {
|
||||||
|
input: true
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/Push-button'],
|
||||||
|
properties: {
|
||||||
|
enabled: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
base: false,
|
||||||
|
name: 'active',
|
||||||
|
type: 'boolean',
|
||||||
|
needsUpdate: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default buttonTemplate
|
40
src/modules/saving/templates/light.ts
Normal file
40
src/modules/saving/templates/light.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the light gate
|
||||||
|
*/
|
||||||
|
const lightTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'light bulb'
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
radius: 50
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
fill: '#1C1C1C',
|
||||||
|
stroke: {
|
||||||
|
normal: '#3C3C3C'
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
active: '#C6FF00'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const { main, active } = context.colors
|
||||||
|
|
||||||
|
context.color(context.get(0) ? active : main)
|
||||||
|
`
|
||||||
|
},
|
||||||
|
integration: {
|
||||||
|
output: true
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
|
||||||
|
pins: {
|
||||||
|
outputs: {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default lightTemplate
|
25
src/modules/saving/templates/nand.ts
Normal file
25
src/modules/saving/templates/nand.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the nand gate
|
||||||
|
*/
|
||||||
|
const nandTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'nand'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/nand')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `context.set(0, !context.get(0) || !context.get(1))`
|
||||||
|
},
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/NAND_gate']
|
||||||
|
}
|
||||||
|
|
||||||
|
export default nandTemplate
|
25
src/modules/saving/templates/nor.ts
Normal file
25
src/modules/saving/templates/nor.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the nor gate
|
||||||
|
*/
|
||||||
|
const norTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'nor'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/nor')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `context.set(0, !(context.get(0) || context.get(1)))`
|
||||||
|
},
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/NOR_gate']
|
||||||
|
}
|
||||||
|
|
||||||
|
export default norTemplate
|
20
src/modules/saving/templates/not.ts
Normal file
20
src/modules/saving/templates/not.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the not gate
|
||||||
|
*/
|
||||||
|
const notTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'not'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/not')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `context.set(0, !context.get(0))`
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/Inverter_(logic_gate)']
|
||||||
|
}
|
||||||
|
|
||||||
|
export default notTemplate
|
25
src/modules/saving/templates/or.ts
Normal file
25
src/modules/saving/templates/or.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the or gate
|
||||||
|
*/
|
||||||
|
const orTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'or'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/or')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `context.set(0, context.get(0) || context.get(1))`
|
||||||
|
},
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/OR_gate']
|
||||||
|
}
|
||||||
|
|
||||||
|
export default orTemplate
|
30
src/modules/saving/templates/parallelDelayer.ts
Normal file
30
src/modules/saving/templates/parallelDelayer.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
import { delayProperties } from '../data/delayProperties'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the parallelDelayer gate
|
||||||
|
*/
|
||||||
|
const parallelDelayerTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'Parallel delayer'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/parallel')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const i = context.get(0)
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res()
|
||||||
|
}, context.getProperty('delay'))
|
||||||
|
}).then(() => {
|
||||||
|
context.set(0,i)
|
||||||
|
})
|
||||||
|
`
|
||||||
|
},
|
||||||
|
properties: delayProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export default parallelDelayerTemplate
|
49
src/modules/saving/templates/rgb.ts
Normal file
49
src/modules/saving/templates/rgb.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the rgbLight gate
|
||||||
|
*/
|
||||||
|
const rgbLightTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'rgb light'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
fill: '#1C1C1C',
|
||||||
|
colors: {
|
||||||
|
1: '#00f',
|
||||||
|
2: `#0f0`,
|
||||||
|
3: `#0ff`,
|
||||||
|
4: `#f00`,
|
||||||
|
5: `#f0f`,
|
||||||
|
6: `#ff0`,
|
||||||
|
7: `#fff`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const color = (context.get(0) << 2) + (context.get(1) << 1) + context.get(2)
|
||||||
|
|
||||||
|
if (color === 0){
|
||||||
|
context.color(context.colors.main)
|
||||||
|
}
|
||||||
|
|
||||||
|
else{
|
||||||
|
context.color(context.colors[color])
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
integration: {
|
||||||
|
output: true
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
|
||||||
|
pins: {
|
||||||
|
outputs: {
|
||||||
|
count: 0
|
||||||
|
},
|
||||||
|
inputs: {
|
||||||
|
count: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default rgbLightTemplate
|
31
src/modules/saving/templates/sequentialDelayer.ts
Normal file
31
src/modules/saving/templates/sequentialDelayer.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
import { delayProperties } from '../data/delayProperties'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the sequentialDelayer gate
|
||||||
|
*/
|
||||||
|
const sequentialDelayerTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'Sequential delayer'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/sequential')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const i = context.get(0)
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res()
|
||||||
|
},context.getProperty('delay'))
|
||||||
|
}).then(() => {
|
||||||
|
context.set(0,i)
|
||||||
|
})
|
||||||
|
`,
|
||||||
|
async: true
|
||||||
|
},
|
||||||
|
properties: delayProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export default sequentialDelayerTemplate
|
30
src/modules/saving/templates/xnor.ts
Normal file
30
src/modules/saving/templates/xnor.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the xnor gate
|
||||||
|
*/
|
||||||
|
const xnorTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'xnor'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/xnor')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const a = context.get(0)
|
||||||
|
const b = context.get(1)
|
||||||
|
const c = (a && b) || !(a || b)
|
||||||
|
|
||||||
|
context.set(0, c)`
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/XNOR_gate'],
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default xnorTemplate
|
30
src/modules/saving/templates/xor.ts
Normal file
30
src/modules/saving/templates/xor.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { PartialTemplate } from '../types/PartialTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The template of the xor gate
|
||||||
|
*/
|
||||||
|
const xorTemplate: PartialTemplate = {
|
||||||
|
metadata: {
|
||||||
|
name: 'xor'
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: 'image',
|
||||||
|
fill: require('../../../assets/xor')
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
activation: `
|
||||||
|
const a = context.get(0)
|
||||||
|
const b = context.get(1)
|
||||||
|
const c = (a || b) && (!a || !b)
|
||||||
|
|
||||||
|
context.set(0, c)`
|
||||||
|
},
|
||||||
|
info: ['https://en.wikipedia.org/wiki/XOR_gate'],
|
||||||
|
pins: {
|
||||||
|
inputs: {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default xorTemplate
|
6
src/modules/saving/types/PartialTemplate.ts
Normal file
6
src/modules/saving/types/PartialTemplate.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { GateTemplate } from '../../simulation/types/GateTemplate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep partial of a gate template
|
||||||
|
*/
|
||||||
|
export type PartialTemplate = DeepPartial<GateTemplate>
|
|
@ -12,7 +12,7 @@ export interface GateState {
|
||||||
transform: TransformState
|
transform: TransformState
|
||||||
id: number
|
id: number
|
||||||
template: string
|
template: string
|
||||||
props: Record<string, unknown>
|
props: Record<string, string | number | boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CameraState {
|
export interface CameraState {
|
||||||
|
|
|
@ -118,7 +118,7 @@ export class Gate {
|
||||||
/**
|
/**
|
||||||
* The props used by the activation function (the same as memory but presists)
|
* The props used by the activation function (the same as memory but presists)
|
||||||
*/
|
*/
|
||||||
public props: Record<string, unknown> = {}
|
public props: Record<string, string | number | boolean> = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main logic gate class
|
* The main logic gate class
|
||||||
|
@ -129,7 +129,7 @@ export class Gate {
|
||||||
public constructor(
|
public constructor(
|
||||||
template: DeepPartial<GateTemplate> = {},
|
template: DeepPartial<GateTemplate> = {},
|
||||||
id?: number,
|
id?: number,
|
||||||
props: Record<string, unknown> = {}
|
props: Record<string, string> = {}
|
||||||
) {
|
) {
|
||||||
this.template = completeTemplate(template)
|
this.template = completeTemplate(template)
|
||||||
|
|
||||||
|
@ -259,17 +259,25 @@ export class Gate {
|
||||||
/**
|
/**
|
||||||
* Assign the props passed to the gate and mere them with the base ones
|
* Assign the props passed to the gate and mere them with the base ones
|
||||||
*/
|
*/
|
||||||
private assignProps(props: Record<string, unknown>) {
|
private assignProps(props: Record<string, string>) {
|
||||||
|
let shouldUpdate = false
|
||||||
|
|
||||||
if (this.template.properties.enabled) {
|
if (this.template.properties.enabled) {
|
||||||
for (const prop in this.template.properties) {
|
for (const { base, name, needsUpdate } of this.template.properties
|
||||||
if (prop !== 'enabled') {
|
.data) {
|
||||||
this.props[prop] =
|
this.props[name] = props.hasOwnProperty(name)
|
||||||
props[prop] !== undefined
|
? props[name]
|
||||||
? props[prop]
|
: base
|
||||||
: this.template.properties[prop].base
|
|
||||||
|
if (!shouldUpdate && needsUpdate) {
|
||||||
|
shouldUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldUpdate) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -335,13 +343,22 @@ export class Gate {
|
||||||
set: (index: number, state: boolean = false) => {
|
set: (index: number, state: boolean = false) => {
|
||||||
return this._pins.outputs[index].state.next(state)
|
return this._pins.outputs[index].state.next(state)
|
||||||
},
|
},
|
||||||
memory: this.memory,
|
|
||||||
color: (color: string) => {
|
color: (color: string) => {
|
||||||
if (this.template.material.type === 'color') {
|
if (this.template.material.type === 'color') {
|
||||||
this.template.material.fill = color
|
this.template.material.fill = color
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getProperty: (name: string) => {
|
||||||
|
return this.props[name]
|
||||||
|
},
|
||||||
|
setProperty: (name: string, value: string | number | boolean) => {
|
||||||
|
this.props[name] = value
|
||||||
|
},
|
||||||
|
update: () => {
|
||||||
|
this.update()
|
||||||
|
},
|
||||||
enviroment: this.env,
|
enviroment: this.env,
|
||||||
|
memory: this.memory,
|
||||||
colors: {
|
colors: {
|
||||||
...this.template.material.colors
|
...this.template.material.colors
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ export class Wire {
|
||||||
public dispose() {
|
public dispose() {
|
||||||
this.end.value.removePair(this.start.value)
|
this.end.value.removePair(this.start.value)
|
||||||
this.start.value.removePair(this.end.value)
|
this.start.value.removePair(this.end.value)
|
||||||
|
this.end.value.state.next(false)
|
||||||
|
|
||||||
this.active = false
|
this.active = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ export const DefaultGateTemplate: GateTemplate = {
|
||||||
info: [],
|
info: [],
|
||||||
tags: ['base'],
|
tags: ['base'],
|
||||||
properties: {
|
properties: {
|
||||||
enabled: false
|
enabled: false,
|
||||||
|
data: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { vector2 } from '../../../common/math/classes/Transform'
|
import { vector2 } from '../../../common/math/classes/Transform'
|
||||||
import { InputHTMLAttributes } from 'react'
|
|
||||||
|
|
||||||
export interface PinCount {
|
export interface PinCount {
|
||||||
variable: boolean
|
variable: boolean
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Property<T> {
|
export interface Property<
|
||||||
type: HTMLInputElement['type']
|
T extends boolean | number | string = boolean | number | string
|
||||||
encode: (value: string) => T
|
> {
|
||||||
|
type: 'number' | 'string' | 'text' | 'boolean'
|
||||||
base: T
|
base: T
|
||||||
|
name: string
|
||||||
|
needsUpdate?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Material {
|
export interface Material {
|
||||||
|
@ -69,5 +71,8 @@ export interface GateTemplate {
|
||||||
}
|
}
|
||||||
info: string[]
|
info: string[]
|
||||||
tags: GateTag[]
|
tags: GateTag[]
|
||||||
properties: Enabled<Record<Exclude<string, 'enabled'>, Property<unknown>>>
|
properties: {
|
||||||
|
enabled: boolean
|
||||||
|
data: Property[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ import { gatesInSelection } from '../helpers/gatesInSelection'
|
||||||
import { selectionType } from '../types/selectionType'
|
import { selectionType } from '../types/selectionType'
|
||||||
import { addIdToSelection, idIsSelected } from '../helpers/idIsSelected'
|
import { addIdToSelection, idIsSelected } from '../helpers/idIsSelected'
|
||||||
import { GateInitter } from '../types/GateInitter'
|
import { GateInitter } from '../types/GateInitter'
|
||||||
|
import {
|
||||||
|
open as propsAreOpen,
|
||||||
|
id as propsModalId
|
||||||
|
} from '../../logic-gates/subjects/LogicGatePropsSubjects'
|
||||||
|
|
||||||
export class SimulationRenderer {
|
export class SimulationRenderer {
|
||||||
public mouseDownOutput = new Subject<MouseEventInfo>()
|
public mouseDownOutput = new Subject<MouseEventInfo>()
|
||||||
|
@ -89,29 +93,34 @@ export class SimulationRenderer {
|
||||||
// because if we have 2 overlapping gates,
|
// because if we have 2 overlapping gates,
|
||||||
// we want to select the one on top
|
// we want to select the one on top
|
||||||
for (let index = gates.length - 1; index >= 0; index--) {
|
for (let index = gates.length - 1; index >= 0; index--) {
|
||||||
const { transform, id, pins } = gates[index]
|
const { transform, id, pins, template } = gates[index]
|
||||||
|
|
||||||
if (
|
if (pointInSquare(worldPosition, transform)) {
|
||||||
event.button === mouseButtons.drag &&
|
if (event.button === mouseButtons.drag) {
|
||||||
pointInSquare(worldPosition, transform)
|
gates[index].onClick()
|
||||||
) {
|
|
||||||
gates[index].onClick()
|
|
||||||
|
|
||||||
this.mouseState |= 1
|
this.mouseState |= 1
|
||||||
|
|
||||||
if (!idIsSelected(this, id)) {
|
if (!idIsSelected(this, id)) {
|
||||||
this.clearSelection()
|
this.clearSelection()
|
||||||
addIdToSelection(this, 'temporary', id)
|
addIdToSelection(this, 'temporary', id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const gateNode = this.simulation.gates.get(id)
|
const gateNode = this.simulation.gates.get(id)
|
||||||
|
|
||||||
if (gateNode) {
|
if (gateNode) {
|
||||||
return this.simulation.gates.moveOnTop(gateNode)
|
return this.simulation.gates.moveOnTop(gateNode)
|
||||||
} else {
|
} else {
|
||||||
throw new SimulationError(
|
throw new SimulationError(
|
||||||
`Cannot find gate with id ${id}`
|
`Cannot find gate with id ${id}`
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
event.button === mouseButtons.properties &&
|
||||||
|
template.properties.enabled
|
||||||
|
) {
|
||||||
|
propsModalId.next(id)
|
||||||
|
return propsAreOpen.next(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,21 +322,15 @@ export class SimulationRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public reloadSave() {
|
public reloadSave() {
|
||||||
try {
|
dumpSimulation(this)
|
||||||
dumpSimulation(this)
|
|
||||||
|
|
||||||
const current = currentStore.get()
|
const current = currentStore.get()
|
||||||
const save = saveStore.get(current)
|
const save = saveStore.get(current)
|
||||||
|
|
||||||
if (!save) return
|
if (!save) return
|
||||||
if (!(save.simulation || save.camera)) return
|
if (!(save.simulation || save.camera)) return
|
||||||
|
|
||||||
this.loadSave(save)
|
this.loadSave(save)
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`An error occured while loading the save: ${e as Error}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,15 +37,13 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
||||||
|
|
||||||
export const imageQuality: vector2 = [100, 100]
|
export const imageQuality: vector2 = [100, 100]
|
||||||
|
|
||||||
export const mouseButtons: Record<
|
export const mouseButtons = {
|
||||||
'zoom' | 'pan' | 'drag' | 'select' | 'unselect',
|
|
||||||
mouseButton
|
|
||||||
> = {
|
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
drag: 0,
|
drag: 0,
|
||||||
pan: 2,
|
pan: 2,
|
||||||
select: 0,
|
select: 0,
|
||||||
unselect: 0
|
unselect: 0,
|
||||||
|
properties: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shiftInput = new KeyboardInput('shift')
|
export const shiftInput = new KeyboardInput('shift')
|
||||||
|
|
Loading…
Reference in a new issue