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 {
|
||||
memory: Record<string, unknown>
|
||||
getProperty: (name: string) => unknown
|
||||
setProperty: (name: string, value: unknown) => void
|
||||
get: (index: number) => boolean
|
||||
set: (index: number, state: boolean) => void
|
||||
color: (color: string) => void
|
||||
update: () => void
|
||||
enviroment: SimulationEnv
|
||||
colors: Record<string, string>
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'react-toastify/dist/ReactToastify.css'
|
|||
|
||||
import { ToastContainer } from 'react-toastify'
|
||||
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 CssBaseline from '@material-ui/core/CssBaseline'
|
||||
|
@ -15,6 +15,7 @@ import Head from './Head'
|
|||
import Root from './Root'
|
||||
import LogicGatePage from '../../logic-gates/components/LogicGatesPage'
|
||||
import { loadSubject } from '../subjects/loadedSubject'
|
||||
import { CustomRouter } from './CustomRouter'
|
||||
|
||||
const App = () => {
|
||||
useEffect(() => {
|
||||
|
@ -27,12 +28,12 @@ const App = () => {
|
|||
<CssBaseline />
|
||||
|
||||
<Theme theme={muiTheme}>
|
||||
<Router>
|
||||
<CustomRouter>
|
||||
<Sidebar />
|
||||
|
||||
<Route path="/" component={Root} exact />
|
||||
<Route path="/gates" component={LogicGatePage} />
|
||||
</Router>
|
||||
</CustomRouter>
|
||||
</Theme>
|
||||
|
||||
<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 CreateSimulation from '../../create-simulation/components/CreateSimulation'
|
||||
import Input from '../../input/components/Input'
|
||||
import GateProperties from '../../logic-gates/components/GatePropertiesModal'
|
||||
|
||||
const Root = () => {
|
||||
return (
|
||||
<>
|
||||
<Canvas />
|
||||
<CreateSimulation />
|
||||
<GateProperties />
|
||||
<Input />
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createMuiTheme } from '@material-ui/core/styles'
|
||||
import { red, deepPurple } from '@material-ui/core/colors'
|
||||
import { simulationMode } from '../saving/types/SimulationSave'
|
||||
import { createBrowserHistory } from 'history'
|
||||
|
||||
export const theme = createMuiTheme({
|
||||
palette: {
|
||||
|
@ -31,3 +32,8 @@ export const icons: IconInterface = {
|
|||
* The width of the sidebar
|
||||
*/
|
||||
export const sidebarWidth = 240
|
||||
|
||||
/**
|
||||
* The history to be used by the router
|
||||
*/
|
||||
export const history = createBrowserHistory()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import '../colors.scss';
|
||||
|
||||
.MuiListItem-root.contained {
|
||||
.contained {
|
||||
// i spent hours trying to find a better solution
|
||||
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 { 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 baseTemplates: DeepPartial<GateTemplate>[] = [
|
||||
{
|
||||
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']
|
||||
},
|
||||
{
|
||||
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)
|
||||
})
|
||||
`
|
||||
}
|
||||
}
|
||||
andTemplate,
|
||||
buttonTemplate,
|
||||
lightTemplate,
|
||||
nandTemplate,
|
||||
norTemplate,
|
||||
notTemplate,
|
||||
orTemplate,
|
||||
parallelDelayerTemplate,
|
||||
rgbLightTemplate,
|
||||
sequentialDelayerTemplate,
|
||||
xnorTemplate,
|
||||
xorTemplate
|
||||
]
|
||||
|
||||
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) {
|
||||
const gate = new Gate(
|
||||
templateStore.get(gateState.template),
|
||||
gateState.id
|
||||
gateState.id,
|
||||
gateState.props
|
||||
)
|
||||
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
|
||||
id: number
|
||||
template: string
|
||||
props: Record<string, unknown>
|
||||
props: Record<string, string | number | boolean>
|
||||
}
|
||||
|
||||
export interface CameraState {
|
||||
|
|
|
@ -118,7 +118,7 @@ export class Gate {
|
|||
/**
|
||||
* 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
|
||||
|
@ -129,7 +129,7 @@ export class Gate {
|
|||
public constructor(
|
||||
template: DeepPartial<GateTemplate> = {},
|
||||
id?: number,
|
||||
props: Record<string, unknown> = {}
|
||||
props: Record<string, string> = {}
|
||||
) {
|
||||
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
|
||||
*/
|
||||
private assignProps(props: Record<string, unknown>) {
|
||||
private assignProps(props: Record<string, string>) {
|
||||
let shouldUpdate = false
|
||||
|
||||
if (this.template.properties.enabled) {
|
||||
for (const prop in this.template.properties) {
|
||||
if (prop !== 'enabled') {
|
||||
this.props[prop] =
|
||||
props[prop] !== undefined
|
||||
? props[prop]
|
||||
: this.template.properties[prop].base
|
||||
for (const { base, name, needsUpdate } of this.template.properties
|
||||
.data) {
|
||||
this.props[name] = props.hasOwnProperty(name)
|
||||
? props[name]
|
||||
: base
|
||||
|
||||
if (!shouldUpdate && needsUpdate) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
this.update()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -335,13 +343,22 @@ export class Gate {
|
|||
set: (index: number, state: boolean = false) => {
|
||||
return this._pins.outputs[index].state.next(state)
|
||||
},
|
||||
memory: this.memory,
|
||||
color: (color: string) => {
|
||||
if (this.template.material.type === '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,
|
||||
memory: this.memory,
|
||||
colors: {
|
||||
...this.template.material.colors
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export class Wire {
|
|||
public dispose() {
|
||||
this.end.value.removePair(this.start.value)
|
||||
this.start.value.removePair(this.end.value)
|
||||
this.end.value.state.next(false)
|
||||
|
||||
this.active = false
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ export const DefaultGateTemplate: GateTemplate = {
|
|||
info: [],
|
||||
tags: ['base'],
|
||||
properties: {
|
||||
enabled: false
|
||||
enabled: false,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { vector2 } from '../../../common/math/classes/Transform'
|
||||
import { InputHTMLAttributes } from 'react'
|
||||
|
||||
export interface PinCount {
|
||||
variable: boolean
|
||||
count: number
|
||||
}
|
||||
|
||||
export interface Property<T> {
|
||||
type: HTMLInputElement['type']
|
||||
encode: (value: string) => T
|
||||
export interface Property<
|
||||
T extends boolean | number | string = boolean | number | string
|
||||
> {
|
||||
type: 'number' | 'string' | 'text' | 'boolean'
|
||||
base: T
|
||||
name: string
|
||||
needsUpdate?: boolean
|
||||
}
|
||||
|
||||
export interface Material {
|
||||
|
@ -69,5 +71,8 @@ export interface GateTemplate {
|
|||
}
|
||||
info: string[]
|
||||
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 { addIdToSelection, idIsSelected } from '../helpers/idIsSelected'
|
||||
import { GateInitter } from '../types/GateInitter'
|
||||
import {
|
||||
open as propsAreOpen,
|
||||
id as propsModalId
|
||||
} from '../../logic-gates/subjects/LogicGatePropsSubjects'
|
||||
|
||||
export class SimulationRenderer {
|
||||
public mouseDownOutput = new Subject<MouseEventInfo>()
|
||||
|
@ -89,29 +93,34 @@ export class SimulationRenderer {
|
|||
// because if we have 2 overlapping gates,
|
||||
// we want to select the one on top
|
||||
for (let index = gates.length - 1; index >= 0; index--) {
|
||||
const { transform, id, pins } = gates[index]
|
||||
const { transform, id, pins, template } = gates[index]
|
||||
|
||||
if (
|
||||
event.button === mouseButtons.drag &&
|
||||
pointInSquare(worldPosition, transform)
|
||||
) {
|
||||
gates[index].onClick()
|
||||
if (pointInSquare(worldPosition, transform)) {
|
||||
if (event.button === mouseButtons.drag) {
|
||||
gates[index].onClick()
|
||||
|
||||
this.mouseState |= 1
|
||||
this.mouseState |= 1
|
||||
|
||||
if (!idIsSelected(this, id)) {
|
||||
this.clearSelection()
|
||||
addIdToSelection(this, 'temporary', id)
|
||||
}
|
||||
if (!idIsSelected(this, id)) {
|
||||
this.clearSelection()
|
||||
addIdToSelection(this, 'temporary', id)
|
||||
}
|
||||
|
||||
const gateNode = this.simulation.gates.get(id)
|
||||
const gateNode = this.simulation.gates.get(id)
|
||||
|
||||
if (gateNode) {
|
||||
return this.simulation.gates.moveOnTop(gateNode)
|
||||
} else {
|
||||
throw new SimulationError(
|
||||
`Cannot find gate with id ${id}`
|
||||
)
|
||||
if (gateNode) {
|
||||
return this.simulation.gates.moveOnTop(gateNode)
|
||||
} else {
|
||||
throw new SimulationError(
|
||||
`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() {
|
||||
try {
|
||||
dumpSimulation(this)
|
||||
dumpSimulation(this)
|
||||
|
||||
const current = currentStore.get()
|
||||
const save = saveStore.get(current)
|
||||
const current = currentStore.get()
|
||||
const save = saveStore.get(current)
|
||||
|
||||
if (!save) return
|
||||
if (!(save.simulation || save.camera)) return
|
||||
if (!save) return
|
||||
if (!(save.simulation || save.camera)) return
|
||||
|
||||
this.loadSave(save)
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`An error occured while loading the save: ${e as Error}`
|
||||
)
|
||||
}
|
||||
this.loadSave(save)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,15 +37,13 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
|||
|
||||
export const imageQuality: vector2 = [100, 100]
|
||||
|
||||
export const mouseButtons: Record<
|
||||
'zoom' | 'pan' | 'drag' | 'select' | 'unselect',
|
||||
mouseButton
|
||||
> = {
|
||||
export const mouseButtons = {
|
||||
zoom: 1,
|
||||
drag: 0,
|
||||
pan: 2,
|
||||
select: 0,
|
||||
unselect: 0
|
||||
unselect: 0,
|
||||
properties: 2
|
||||
}
|
||||
|
||||
export const shiftInput = new KeyboardInput('shift')
|
||||
|
|
Loading…
Reference in a new issue