logic gate button

This commit is contained in:
Matei Adriel 2019-07-22 19:58:26 +03:00
parent 0c76f3cdf6
commit e88244bc9d
29 changed files with 518 additions and 107 deletions

View file

@ -2,4 +2,5 @@ export interface Context {
memory: Record<string, unknown> memory: Record<string, unknown>
get: (index: number) => boolean get: (index: number) => boolean
set: (index: number, state: boolean) => void set: (index: number, state: boolean) => void
color: (color: string) => void
} }

View file

@ -12,6 +12,7 @@ import Theme from '@material-ui/styles/ThemeProvider'
import Sidebar from './Sidebar' import Sidebar from './Sidebar'
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 LogicGateModal from '../../logic-gates/components/LogicGateModal'
const App = () => { const App = () => {
return ( return (
@ -22,6 +23,7 @@ const App = () => {
<Sidebar /> <Sidebar />
<CreateSimulation /> <CreateSimulation />
<Input /> <Input />
<LogicGateModal />
</Theme> </Theme>
<CssBaseline /> <CssBaseline />
<ToastContainer <ToastContainer

View file

@ -0,0 +1,5 @@
@import '../styles/colors.scss';
#language-button {
border: 1px solid white;
}

View file

@ -0,0 +1,31 @@
import React from 'react'
import Icon from '@material-ui/core/Icon'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemIconText from '@material-ui/core/ListItemText'
import { useTranslation } from '../../internalisation/helpers/useLanguage'
import { nextLanguage } from '../../internalisation/helpers/nextLanguage'
import './Language.scss'
/**
* The language component from the sidebar
*/
const Language = () => {
const translation = useTranslation()
return (
<List>
<ListItem button onClick={nextLanguage} id="language-button">
<ListItemIcon>
<Icon>language</Icon>
</ListItemIcon>
<ListItemIconText>
{translation.sidebar.language}: {translation.language}
</ListItemIconText>
</ListItem>
</List>
)
}
export default Language

View file

@ -1,32 +1,11 @@
import React, { useState } from 'react' import React from 'react'
import ListItem from '@material-ui/core/ListItem' import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon' import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText' import ListItemText from '@material-ui/core/ListItemText'
import Menu from '@material-ui/core/Menu'
import MenuItem from '@material-ui/core/MenuItem'
import Icon from '@material-ui/core/Icon' import Icon from '@material-ui/core/Icon'
import Typography from '@material-ui/core/Typography'
import { BehaviorSubject } from 'rxjs'
import { useObservable } from 'rxjs-hooks'
import { switchTo } from '../../saving/helpers/switchTo'
import { SimulationError } from '../../errors/classes/SimulationError'
import { templateStore } from '../../saving/stores/templateStore'
import { useTranslation } from '../../internalisation/helpers/useLanguage' import { useTranslation } from '../../internalisation/helpers/useLanguage'
import { open } from '../../logic-gates/components/LogicGateModal'
/** import { updateLogicGateList } from '../../logic-gates/subjects/LogicGateList'
* Subject to make React update the dom when new gates are stored
*/
const allGatesSubject = new BehaviorSubject<string[]>([])
/**
* Triggers a dom update by pushing a new value to the
* useObservable hook inside the React component.
*
* It also has the side effect of sorting the template names.
*/
const updateTemplateList = () => {
allGatesSubject.next(templateStore.ls().sort())
}
/** /**
* Component wich contains the sidebar 'Open simulation' button * Component wich contains the sidebar 'Open simulation' button
@ -34,59 +13,21 @@ const updateTemplateList = () => {
* @throws SimulationError if the data about a simulation cant be found in localStorage * @throws SimulationError if the data about a simulation cant be found in localStorage
*/ */
const LogicGates = () => { const LogicGates = () => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const simulations = useObservable(() => allGatesSubject, [])
const translation = useTranslation() const translation = useTranslation()
const handleClose = () => {
setAnchorEl(null)
}
return ( return (
<> <ListItem
<ListItem button
button onClick={() => {
onClick={event => { updateLogicGateList()
updateTemplateList() open.next(true)
setAnchorEl(event.currentTarget) }}
}} >
> <ListItemIcon>
<ListItemIcon> <Icon>memory</Icon>
<Icon>memory</Icon> </ListItemIcon>
</ListItemIcon> <ListItemText>{translation.sidebar.logicGates}</ListItemText>
<ListItemText>{translation.sidebar.logicGates}</ListItemText> </ListItem>
</ListItem>
<Menu
keepMounted
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
>
{simulations.map((simulationName, index) => {
const simulationData = templateStore.get(simulationName)
if (!simulationData) {
throw new SimulationError(
`Cannot get data for simulation ${simulationName}`
)
}
return (
<MenuItem
key={index}
onClick={() => {
switchTo(simulationName)
handleClose()
}}
>
<Typography>{simulationName}</Typography>
</MenuItem>
)
})}
</Menu>
</>
) )
} }

View file

@ -5,6 +5,7 @@ import OpenSimulation from './OpenSimulation'
import CreateSimulationButton from './CreateSimulationButton' import CreateSimulationButton from './CreateSimulationButton'
import LogicGates from './LogicGates' import LogicGates from './LogicGates'
import { makeStyles, createStyles } from '@material-ui/core/styles' import { makeStyles, createStyles } from '@material-ui/core/styles'
import Language from './Language'
/** /**
* The width of the sidebar * The width of the sidebar
*/ */
@ -39,6 +40,11 @@ const useStyles = makeStyles(
padding: '4px', padding: '4px',
width: sidebarWidth, width: sidebarWidth,
zIndex: sidebarZIndex zIndex: sidebarZIndex
},
// This is the class for the main button list
list: {
flexGrow: 1
} }
}) })
) )
@ -60,11 +66,13 @@ const Sidebar = () => {
paper: classes.drawerPaper paper: classes.drawerPaper
}} }}
> >
<List component="nav"> <List component="nav" className={classes.list}>
<CreateSimulationButton /> <CreateSimulationButton />
<OpenSimulation /> <OpenSimulation />
<LogicGates /> <LogicGates />
</List> </List>
<Language />
</Drawer> </Drawer>
</div> </div>
) )

View file

@ -1,2 +1,3 @@
$modal-bg-color: rgba(0, 0, 0, 0.7); $modal-bg-color: rgba(0, 0, 0, 0.7);
$primary: #673ab7; $primary: #673ab7;
$grey: #444444;

View file

@ -0,0 +1,16 @@
@keyframes glow {
from {
text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #e60073,
0 0 40px #e60073, 0 0 50px #e60073, 0 0 60px #e60073,
0 0 70px #e60073;
}
to {
text-shadow: 0 0 20px #fff, 0 0 30px #ff4da6, 0 0 40px #ff4da6,
0 0 50px #ff4da6, 0 0 60px #ff4da6, 0 0 70px #ff4da6,
0 0 80px #ff4da6;
}
}
@mixin glow {
animation: glow 1s ease-in-out infinite alternate;
}

View file

@ -444,3 +444,8 @@ textarea {
color: #000; color: #000;
padding: 0.2em 0; padding: 0.2em 0;
} }
a {
color: white;
text-decoration: none;
}

View file

@ -1,3 +1,5 @@
@import '../../core/styles/colors.scss';
#create-options { #create-options {
width: 100%; width: 100%;
display: flex; display: flex;
@ -7,7 +9,7 @@
.create-option { .create-option {
@include flex(); @include flex();
background-color: #444444; background-color: $grey;
height: 17em; height: 17em;
width: 17em; width: 17em;
border-radius: 1em; border-radius: 1em;

View file

@ -1,12 +1,20 @@
import { supportedLanguages } from './types/supportedLanguages' import { supportedLanguage } from './types/supportedLanguages'
import { Translation } from './types/TranslationInterface' import { Translation } from './types/TranslationInterface'
import { EnglishTranslation } from './translations/english' import { EnglishTranslation } from './translations/english'
import { RomanianTranslation } from './translations/romanian' import { RomanianTranslation } from './translations/romanian'
import { DutchTranslation } from './translations/nederlands'
/** /**
* Object with all translations * Object with all translations
*/ */
export const translations: Record<supportedLanguages, Translation> = { export const translations: Record<supportedLanguage, Translation> = {
english: EnglishTranslation, english: EnglishTranslation,
['română']: RomanianTranslation ['română']: RomanianTranslation,
dutch: DutchTranslation
} }
export const allSupportedLanguages: supportedLanguage[] = [
'english',
'română',
'dutch'
]

View file

@ -0,0 +1,17 @@
import {
CurrentLanguage,
CurrentLanguageLocalStore
} from '../stores/currentLanguage'
import { allSupportedLanguages } from '../constants'
/**
* Changes the language to the next one avabile
*/
export const nextLanguage = () => {
const current = CurrentLanguage.get()
const index = allSupportedLanguages.indexOf(current)
CurrentLanguage.set(
allSupportedLanguages[(index + 1) % allSupportedLanguages.length]
)
}

View file

@ -0,0 +1,7 @@
/**
* Returns a random element of the aray
*
* @param arr - the array to choose from
*/
export const randomItem = <T>(arr: T[]): T =>
arr[Math.floor(Math.random() * arr.length)]

View file

@ -1,4 +1,4 @@
import { supportedLanguages } from '../types/supportedLanguages' import { supportedLanguage } from '../types/supportedLanguages'
import { LocalStore } from '../../storage/classes/LocalStore' import { LocalStore } from '../../storage/classes/LocalStore'
import currentLanguageSubject from './../subjects/currentLanguageSubject' import currentLanguageSubject from './../subjects/currentLanguageSubject'
import { SimulationError } from '../../errors/classes/SimulationError' import { SimulationError } from '../../errors/classes/SimulationError'
@ -7,7 +7,7 @@ import { translations } from '../constants'
/** /**
* Local store containing the current selected language * Local store containing the current selected language
*/ */
export const CurrentLanguageLocalStore = new LocalStore<supportedLanguages>( export const CurrentLanguageLocalStore = new LocalStore<supportedLanguage>(
'language' 'language'
) )
@ -22,7 +22,7 @@ currentLanguageSubject.next(CurrentLanguageLocalStore.get() || 'english')
* The preffered interface for interacting with CurrentLanguageLocalStore * The preffered interface for interacting with CurrentLanguageLocalStore
*/ */
const CurrentLanguage = { const CurrentLanguage = {
set(name: supportedLanguages) { set(name: supportedLanguage) {
CurrentLanguageLocalStore.set(name) CurrentLanguageLocalStore.set(name)
currentLanguageSubject.next(name) currentLanguageSubject.next(name)
}, },

View file

@ -1,7 +1,7 @@
import { BehaviorSubject } from 'rxjs' import { BehaviorSubject } from 'rxjs'
import { supportedLanguages } from '../types/supportedLanguages' import { supportedLanguage } from '../types/supportedLanguages'
/** /**
* Subject with the current language * Subject with the current language
*/ */
export default new BehaviorSubject<supportedLanguages>('english') export default new BehaviorSubject<supportedLanguage>('english')

View file

@ -8,7 +8,8 @@ export const EnglishTranslation: Translation = {
sidebar: { sidebar: {
createSimulation: 'Create simulation', createSimulation: 'Create simulation',
logicGates: 'Logic gates', logicGates: 'Logic gates',
openSimulation: 'Open simulations' openSimulation: 'Open simulations',
language: 'Language'
}, },
createSimulation: { createSimulation: {
mode: { mode: {

View file

@ -0,0 +1,33 @@
import { Translation } from '../types/TranslationInterface'
/**
* The dutch translation
*/
export const DutchTranslation: Translation = {
language: 'dutch',
sidebar: {
createSimulation: 'Maak simulatie',
logicGates: 'Logische poorten',
openSimulation: 'Open simulatie',
language: 'Taal'
},
createSimulation: {
mode: {
question: 'Wat voor simulatie wil je maken?',
options: {
// ic: 'Geïntegreerde schakeling',
ic: 'IC',
project: 'Project'
}
},
name: {
question: 'Hoe wil je je simulatie noemen?'
}
},
messages: {
createdSimulation: name => `Simulatie '${name}' succesvol gecreerd`,
switchedToSimulation: name =>
`Succesvol veranderd naar simulatie '${name}'`,
savedSimulation: name => `Simulatie succesvol opgeslagen '${name}'`
}
}

View file

@ -8,11 +8,12 @@ export const RomanianTranslation: Translation = {
sidebar: { sidebar: {
createSimulation: 'Creează o simulație', createSimulation: 'Creează o simulație',
openSimulation: 'Deschide o simulație', openSimulation: 'Deschide o simulație',
logicGates: 'Porți logice' logicGates: 'Porți logice',
language: 'Limba'
}, },
createSimulation: { createSimulation: {
mode: { mode: {
question: 'Ce fel de simulație vrei să creiezi?', question: 'Ce fel de simulație vrei să creezi?',
options: { options: {
ic: 'Circuit integrat', ic: 'Circuit integrat',
project: 'Proiect' project: 'Proiect'
@ -24,7 +25,7 @@ export const RomanianTranslation: Translation = {
}, },
messages: { messages: {
createdSimulation: name => createdSimulation: name =>
`Simulația '${name}' a fost creiată cu succes`, `Simulația '${name}' a fost creeată cu succes`,
switchedToSimulation: name => switchedToSimulation: name =>
`Simulația '${name}' a fost deschisă cu succes`, `Simulația '${name}' a fost deschisă cu succes`,
savedSimulation: name => `Simulația '${name}' a fost salvată cu succes` savedSimulation: name => `Simulația '${name}' a fost salvată cu succes`

View file

@ -1,4 +1,4 @@
import { supportedLanguages } from './supportedLanguages' import { supportedLanguage } from './supportedLanguages'
import { simulationMode } from '../../saving/types/SimulationSave' import { simulationMode } from '../../saving/types/SimulationSave'
export type SentenceFactory<T extends string[]> = (...names: T) => string export type SentenceFactory<T extends string[]> = (...names: T) => string
@ -8,11 +8,12 @@ export type NameSentence = SentenceFactory<[string]>
* The interface all translations need to follow * The interface all translations need to follow
*/ */
export interface Translation { export interface Translation {
language: supportedLanguages language: supportedLanguage
sidebar: { sidebar: {
createSimulation: string createSimulation: string
openSimulation: string openSimulation: string
logicGates: string logicGates: string
language: string
} }
createSimulation: { createSimulation: {
mode: { mode: {

View file

@ -1,4 +1,4 @@
/** /**
* Type containing the names of all supported languages * Type containing the names of all supported languages
*/ */
export type supportedLanguages = 'română' | 'english' export type supportedLanguage = 'română' | 'english' | 'dutch'

View file

@ -0,0 +1,62 @@
@import '../../core/styles/mixins/flex.scss';
@import '../../core/styles/mixins/visibility.scss';
@import '../../modals/styles/mixins/modal-container.scss';
@import '../../modals/styles/mixins/modal-title.scss';
@import '../../core/styles/colors.scss';
@import '../../core/styles/mixins/glow.scss';
// Opacities
$normal-opacity: 0.6;
$hover-opacity: 0.9;
// Colors
$item-color: $grey;
#logic-gate-modal-container {
@include modal-container();
justify-content: center;
}
.visible#logic-gate-modal-container {
@include visible();
}
#logic-gate-modal-container > .logic-gate-item {
@include flex();
justify-content: left;
flex-direction: row;
width: 80%;
height: 4em;
border: 3px solid white;
margin: 0.5em;
background-color: rgba($item-color, $normal-opacity);
}
#logic-gate-modal-container > .logic-gate-item:hover {
background-color: rgba($item-color, $hover-opacity);
}
#logic-gate-modal-container > .logic-gate-item > * {
font-size: 3em;
}
#logic-gate-modal-container > .logic-gate-item > .logic-gate-item-type {
margin: 0.5em;
}
#logic-gate-modal-container > .logic-gate-item > *:last-child {
margin-right: 0.5em;
}
.lgi-icon:hover:not(.logic-gate-item-type) {
border: 1px solid white;
}
#logic-gate-modal-container > .logic-gate-item > .logic-gate-item-name {
flex-grow: 1;
}

View file

@ -0,0 +1,90 @@
import './LogicGateModal.scss'
import React from 'react'
import { BehaviorSubject } from 'rxjs'
import { useObservable } from 'rxjs-hooks'
import { LogicGateList } from '../subjects/LogicGateList'
import Icon from '@material-ui/core/Icon'
import Typography from '@material-ui/core/Typography'
import { addGate } from '../../simulation/helpers/addGate'
import { rendererSubject } from '../../core/subjects/rendererSubject'
import { SimulationError } from '../../errors/classes/SimulationError'
import { templateStore } from '../../saving/stores/templateStore'
import { randomItem } from '../../internalisation/helpers/randomItem'
/**
* Subject containing the open state of the modal
*/
export const open = new BehaviorSubject(false)
/**
* helper to close the modal
*/
export const handleClose = () => {
open.next(false)
}
/**
* The component containing the info / actions about all logic gates
*/
const LogicGateModal = () => {
const openSnapshot = useObservable(() => open, false)
const gates = useObservable(() => LogicGateList, [])
return (
<div
className={openSnapshot ? 'visible' : ''}
id="logic-gate-modal-container"
onClick={handleClose}
>
{gates.map((gate, index) => {
const renderer = rendererSubject.value
if (!renderer) {
throw new SimulationError(`Renderer not found`)
}
const template =
gate.source === 'base' ? templateStore.get(gate.name) : ''
if (gate.source === 'base' && !template) {
throw new SimulationError(
`Template ${gate.name} cannot be found`
)
}
return (
<div
key={index}
className="logic-gate-item"
onClick={e => {
addGate(renderer.simulation, gate.name)
}}
>
<Icon className="lgi-icon logic-gate-item-type">
{gate.source === 'base' ? 'sd_storage' : 'memory'}
</Icon>
<Typography className="logic-gate-item-name">
{gate.name}
</Typography>
{template && template.info && template.info.length && (
<a
target="_blank"
className="logic-gate-item-info"
href={randomItem(template.info)}
>
<Icon className="lgi-icon">info</Icon>
</a>
)}
{gate.source === 'ic' && (
<Icon className="lgi-icon logic-gate-item-delete">
delete
</Icon>
)}
</div>
)
})}
</div>
)
}
export default LogicGateModal

View file

@ -0,0 +1,26 @@
import { saveStore } from '../../saving/stores/saveStore'
import { SimulationError } from '../../errors/classes/SimulationError'
/**
* Helper to get the names of all integrated circuits
*
* @throws SimulationError if a save cannot be found in localsStorage
*/
export const getAllics = () => {
const saves = saveStore.ls()
const result: string[] = []
for (const save of saves) {
const saveState = saveStore.get(save)
if (saveState) {
if (saveState.simulation.mode === 'ic') {
result.push(saveState.simulation.name)
}
} else {
throw new SimulationError(`Cannot find save ${save}`)
}
}
return result
}

View file

@ -0,0 +1,37 @@
import { BehaviorSubject } from 'rxjs'
import { templateStore } from '../../saving/stores/templateStore'
import { getAllics } from '../helpers/getAllIcs'
/**
* The interface for the items in the list
*/
export interface LogicGateNameWrapper {
source: 'base' | 'ic'
name: string
}
/**
* Subject containing a list with the names of all logic gate templates
*/
export const LogicGateList = new BehaviorSubject<LogicGateNameWrapper[]>([])
/**
* Helper method to update the list of logic gate templates.
*/
export const updateLogicGateList = () => {
const ics = getAllics().map(
(name): LogicGateNameWrapper => ({
source: 'ic',
name
})
)
const templates = templateStore.ls().map(
(name): LogicGateNameWrapper => ({
source: 'base',
name
})
)
LogicGateList.next([...ics, ...templates])
}

View file

@ -3,25 +3,118 @@ import { RendererState } from './types/SimulationSave'
export const defaultSimulationName = 'default' export const defaultSimulationName = 'default'
export const baseTemplates: DeepPartial<GateTemplate>[] = [ export const baseTemplates: DeepPartial<GateTemplate>[] = [
{
metadata: {
name: 'and'
},
material: {
value: 'green'
},
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: 'or'
},
material: {
value: 'yellow'
},
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: 'xor'
},
material: {
value: 'white'
},
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: { metadata: {
name: 'not' name: 'not'
}, },
material: { material: {
value: 'red', value: 'red'
type: 'color'
}, },
code: { code: {
activation: `context.set(0, !context.get(0))` activation: `context.set(0, !context.get(0))`
}, },
info: ['https://en.wikipedia.org/wiki/Inverter_(logic_gate)']
},
{
metadata: {
name: 'button'
},
material: {
value: 'red'
},
code: {
onClick: `
const old = context.memory.state
const state = !old
context.set(0, state)
context.color(old ? 'red' : '#550000')
context.memory.state = state
`
},
pins: { pins: {
inputs: { inputs: {
count: 1, count: 0
variable: false }
}, },
info: ['https://en.wikipedia.org/wiki/Push-button']
},
{
metadata: {
name: 'light bulb'
},
shape: {
radius: 50
},
material: {
value: 'white'
},
code: {
activation: `
context.color(context.get(0) ? 'yellow' : 'white')
`
},
info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
pins: {
outputs: { outputs: {
count: 1, count: 0
variable: false
} }
} }
} }

View file

@ -22,8 +22,11 @@ export interface PinWrapper {
value: Pin value: Pin
} }
export type GateFunction = null | ((ctx: Context) => void)
export interface GateFunctions { export interface GateFunctions {
activation: null | ((ctx: Context) => void) activation: GateFunction
onClick: GateFunction
} }
export class Gate { export class Gate {
@ -37,7 +40,8 @@ export class Gate {
public template: GateTemplate public template: GateTemplate
private functions: GateFunctions = { private functions: GateFunctions = {
activation: null activation: null,
onClick: null
} }
private subscriptions: Subscription[] = [] private subscriptions: Subscription[] = []
@ -53,6 +57,11 @@ export class Gate {
'context' 'context'
) )
this.functions.onClick = toFunction(
this.template.code.onClick,
'context'
)
this._pins.inputs = Gate.generatePins( this._pins.inputs = Gate.generatePins(
this.template.pins.inputs, this.template.pins.inputs,
1, 1,
@ -77,6 +86,12 @@ export class Gate {
} }
} }
public onClick() {
if (this.functions.onClick) {
this.functions.onClick(this.getContext())
}
}
public dispose() { public dispose() {
for (const pin of this.pins) { for (const pin of this.pins) {
pin.value.dispose() pin.value.dispose()
@ -104,7 +119,12 @@ 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 memory: this.memory,
color: (color: string) => {
if (this.template.material.type === 'color') {
this.template.material.value = color
}
}
} }
} }

View file

@ -10,7 +10,7 @@ export const DefaultGateTemplate: GateTemplate = {
}, },
pins: { pins: {
inputs: { inputs: {
count: 2, count: 1,
variable: false variable: false
}, },
outputs: { outputs: {
@ -25,8 +25,7 @@ export const DefaultGateTemplate: GateTemplate = {
}, },
code: { code: {
activation: 'context.set(0,true)', activation: 'context.set(0,true)',
start: '', onClick: ''
stop: ''
}, },
simulation: { simulation: {
debounce: { debounce: {
@ -36,5 +35,6 @@ export const DefaultGateTemplate: GateTemplate = {
throttle: { throttle: {
enabled: false enabled: false
} }
} },
info: []
} }

View file

@ -39,12 +39,12 @@ export interface GateTemplate {
name: string name: string
} }
code: { code: {
start: string
activation: string activation: string
stop: string onClick: string
} }
simulation: { simulation: {
throttle: TimePipe throttle: TimePipe
debounce: TimePipe debounce: TimePipe
} }
info: string[]
} }

View file

@ -77,6 +77,9 @@ export class SimulationRenderer {
const { transform, id, pins } = gates[index] const { transform, id, pins } = gates[index]
if (pointInSquare(worldPosition, transform)) { if (pointInSquare(worldPosition, transform)) {
// run function
gates[index].onClick()
this.mouseManager.clear(worldPosition[0]) this.mouseManager.clear(worldPosition[0])
this.mouseState |= 1 this.mouseState |= 1