logic gate button
This commit is contained in:
parent
0c76f3cdf6
commit
e88244bc9d
|
@ -2,4 +2,5 @@ export interface Context {
|
|||
memory: Record<string, unknown>
|
||||
get: (index: number) => boolean
|
||||
set: (index: number, state: boolean) => void
|
||||
color: (color: string) => void
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import Theme from '@material-ui/styles/ThemeProvider'
|
|||
import Sidebar from './Sidebar'
|
||||
import CreateSimulation from '../../create-simulation/components/CreateSimulation'
|
||||
import Input from '../../input/components/Input'
|
||||
import LogicGateModal from '../../logic-gates/components/LogicGateModal'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
|
@ -22,6 +23,7 @@ const App = () => {
|
|||
<Sidebar />
|
||||
<CreateSimulation />
|
||||
<Input />
|
||||
<LogicGateModal />
|
||||
</Theme>
|
||||
<CssBaseline />
|
||||
<ToastContainer
|
||||
|
|
5
src/modules/core/components/Language.scss
Normal file
5
src/modules/core/components/Language.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
@import '../styles/colors.scss';
|
||||
|
||||
#language-button {
|
||||
border: 1px solid white;
|
||||
}
|
31
src/modules/core/components/Language.tsx
Normal file
31
src/modules/core/components/Language.tsx
Normal 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
|
|
@ -1,32 +1,11 @@
|
|||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import ListItem from '@material-ui/core/ListItem'
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||
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 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'
|
||||
|
||||
/**
|
||||
* 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())
|
||||
}
|
||||
import { open } from '../../logic-gates/components/LogicGateModal'
|
||||
import { updateLogicGateList } from '../../logic-gates/subjects/LogicGateList'
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
const LogicGates = () => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
||||
const simulations = useObservable(() => allGatesSubject, [])
|
||||
|
||||
const translation = useTranslation()
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem
|
||||
button
|
||||
onClick={event => {
|
||||
updateTemplateList()
|
||||
setAnchorEl(event.currentTarget)
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Icon>memory</Icon>
|
||||
</ListItemIcon>
|
||||
<ListItemText>{translation.sidebar.logicGates}</ListItemText>
|
||||
</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>
|
||||
</>
|
||||
<ListItem
|
||||
button
|
||||
onClick={() => {
|
||||
updateLogicGateList()
|
||||
open.next(true)
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Icon>memory</Icon>
|
||||
</ListItemIcon>
|
||||
<ListItemText>{translation.sidebar.logicGates}</ListItemText>
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import OpenSimulation from './OpenSimulation'
|
|||
import CreateSimulationButton from './CreateSimulationButton'
|
||||
import LogicGates from './LogicGates'
|
||||
import { makeStyles, createStyles } from '@material-ui/core/styles'
|
||||
import Language from './Language'
|
||||
/**
|
||||
* The width of the sidebar
|
||||
*/
|
||||
|
@ -39,6 +40,11 @@ const useStyles = makeStyles(
|
|||
padding: '4px',
|
||||
width: sidebarWidth,
|
||||
zIndex: sidebarZIndex
|
||||
},
|
||||
|
||||
// This is the class for the main button list
|
||||
list: {
|
||||
flexGrow: 1
|
||||
}
|
||||
})
|
||||
)
|
||||
|
@ -60,11 +66,13 @@ const Sidebar = () => {
|
|||
paper: classes.drawerPaper
|
||||
}}
|
||||
>
|
||||
<List component="nav">
|
||||
<List component="nav" className={classes.list}>
|
||||
<CreateSimulationButton />
|
||||
<OpenSimulation />
|
||||
<LogicGates />
|
||||
</List>
|
||||
|
||||
<Language />
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
$modal-bg-color: rgba(0, 0, 0, 0.7);
|
||||
$primary: #673ab7;
|
||||
$grey: #444444;
|
||||
|
|
16
src/modules/core/styles/mixins/glow.scss
Normal file
16
src/modules/core/styles/mixins/glow.scss
Normal 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;
|
||||
}
|
|
@ -444,3 +444,8 @@ textarea {
|
|||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import '../../core/styles/colors.scss';
|
||||
|
||||
#create-options {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -7,7 +9,7 @@
|
|||
.create-option {
|
||||
@include flex();
|
||||
|
||||
background-color: #444444;
|
||||
background-color: $grey;
|
||||
height: 17em;
|
||||
width: 17em;
|
||||
border-radius: 1em;
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import { supportedLanguages } from './types/supportedLanguages'
|
||||
import { supportedLanguage } from './types/supportedLanguages'
|
||||
import { Translation } from './types/TranslationInterface'
|
||||
import { EnglishTranslation } from './translations/english'
|
||||
import { RomanianTranslation } from './translations/romanian'
|
||||
import { DutchTranslation } from './translations/nederlands'
|
||||
|
||||
/**
|
||||
* Object with all translations
|
||||
*/
|
||||
export const translations: Record<supportedLanguages, Translation> = {
|
||||
export const translations: Record<supportedLanguage, Translation> = {
|
||||
english: EnglishTranslation,
|
||||
['română']: RomanianTranslation
|
||||
['română']: RomanianTranslation,
|
||||
dutch: DutchTranslation
|
||||
}
|
||||
|
||||
export const allSupportedLanguages: supportedLanguage[] = [
|
||||
'english',
|
||||
'română',
|
||||
'dutch'
|
||||
]
|
||||
|
|
17
src/modules/internalisation/helpers/nextLanguage.ts
Normal file
17
src/modules/internalisation/helpers/nextLanguage.ts
Normal 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]
|
||||
)
|
||||
}
|
7
src/modules/internalisation/helpers/randomItem.ts
Normal file
7
src/modules/internalisation/helpers/randomItem.ts
Normal 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)]
|
|
@ -1,4 +1,4 @@
|
|||
import { supportedLanguages } from '../types/supportedLanguages'
|
||||
import { supportedLanguage } from '../types/supportedLanguages'
|
||||
import { LocalStore } from '../../storage/classes/LocalStore'
|
||||
import currentLanguageSubject from './../subjects/currentLanguageSubject'
|
||||
import { SimulationError } from '../../errors/classes/SimulationError'
|
||||
|
@ -7,7 +7,7 @@ import { translations } from '../constants'
|
|||
/**
|
||||
* Local store containing the current selected language
|
||||
*/
|
||||
export const CurrentLanguageLocalStore = new LocalStore<supportedLanguages>(
|
||||
export const CurrentLanguageLocalStore = new LocalStore<supportedLanguage>(
|
||||
'language'
|
||||
)
|
||||
|
||||
|
@ -22,7 +22,7 @@ currentLanguageSubject.next(CurrentLanguageLocalStore.get() || 'english')
|
|||
* The preffered interface for interacting with CurrentLanguageLocalStore
|
||||
*/
|
||||
const CurrentLanguage = {
|
||||
set(name: supportedLanguages) {
|
||||
set(name: supportedLanguage) {
|
||||
CurrentLanguageLocalStore.set(name)
|
||||
currentLanguageSubject.next(name)
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BehaviorSubject } from 'rxjs'
|
||||
import { supportedLanguages } from '../types/supportedLanguages'
|
||||
import { supportedLanguage } from '../types/supportedLanguages'
|
||||
|
||||
/**
|
||||
* Subject with the current language
|
||||
*/
|
||||
export default new BehaviorSubject<supportedLanguages>('english')
|
||||
export default new BehaviorSubject<supportedLanguage>('english')
|
||||
|
|
|
@ -8,7 +8,8 @@ export const EnglishTranslation: Translation = {
|
|||
sidebar: {
|
||||
createSimulation: 'Create simulation',
|
||||
logicGates: 'Logic gates',
|
||||
openSimulation: 'Open simulations'
|
||||
openSimulation: 'Open simulations',
|
||||
language: 'Language'
|
||||
},
|
||||
createSimulation: {
|
||||
mode: {
|
||||
|
|
33
src/modules/internalisation/translations/nederlands.ts
Normal file
33
src/modules/internalisation/translations/nederlands.ts
Normal 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}'`
|
||||
}
|
||||
}
|
|
@ -8,11 +8,12 @@ export const RomanianTranslation: Translation = {
|
|||
sidebar: {
|
||||
createSimulation: 'Creează o simulație',
|
||||
openSimulation: 'Deschide o simulație',
|
||||
logicGates: 'Porți logice'
|
||||
logicGates: 'Porți logice',
|
||||
language: 'Limba'
|
||||
},
|
||||
createSimulation: {
|
||||
mode: {
|
||||
question: 'Ce fel de simulație vrei să creiezi?',
|
||||
question: 'Ce fel de simulație vrei să creezi?',
|
||||
options: {
|
||||
ic: 'Circuit integrat',
|
||||
project: 'Proiect'
|
||||
|
@ -24,7 +25,7 @@ export const RomanianTranslation: Translation = {
|
|||
},
|
||||
messages: {
|
||||
createdSimulation: name =>
|
||||
`Simulația '${name}' a fost creiată cu succes`,
|
||||
`Simulația '${name}' a fost creeată cu succes`,
|
||||
switchedToSimulation: name =>
|
||||
`Simulația '${name}' a fost deschisă cu succes`,
|
||||
savedSimulation: name => `Simulația '${name}' a fost salvată cu succes`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { supportedLanguages } from './supportedLanguages'
|
||||
import { supportedLanguage } from './supportedLanguages'
|
||||
import { simulationMode } from '../../saving/types/SimulationSave'
|
||||
|
||||
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
|
||||
*/
|
||||
export interface Translation {
|
||||
language: supportedLanguages
|
||||
language: supportedLanguage
|
||||
sidebar: {
|
||||
createSimulation: string
|
||||
openSimulation: string
|
||||
logicGates: string
|
||||
language: string
|
||||
}
|
||||
createSimulation: {
|
||||
mode: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
* Type containing the names of all supported languages
|
||||
*/
|
||||
export type supportedLanguages = 'română' | 'english'
|
||||
export type supportedLanguage = 'română' | 'english' | 'dutch'
|
||||
|
|
62
src/modules/logic-gates/components/LogicGateModal.scss
Normal file
62
src/modules/logic-gates/components/LogicGateModal.scss
Normal 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;
|
||||
}
|
90
src/modules/logic-gates/components/LogicGateModal.tsx
Normal file
90
src/modules/logic-gates/components/LogicGateModal.tsx
Normal 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
|
26
src/modules/logic-gates/helpers/getAllIcs.ts
Normal file
26
src/modules/logic-gates/helpers/getAllIcs.ts
Normal 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
|
||||
}
|
37
src/modules/logic-gates/subjects/LogicGateList.ts
Normal file
37
src/modules/logic-gates/subjects/LogicGateList.ts
Normal 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])
|
||||
}
|
|
@ -3,25 +3,118 @@ import { RendererState } from './types/SimulationSave'
|
|||
|
||||
export const defaultSimulationName = 'default'
|
||||
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: {
|
||||
name: 'not'
|
||||
},
|
||||
material: {
|
||||
value: 'red',
|
||||
type: 'color'
|
||||
value: 'red'
|
||||
},
|
||||
code: {
|
||||
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: {
|
||||
inputs: {
|
||||
count: 1,
|
||||
variable: false
|
||||
},
|
||||
count: 0
|
||||
}
|
||||
},
|
||||
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: {
|
||||
count: 1,
|
||||
variable: false
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,11 @@ export interface PinWrapper {
|
|||
value: Pin
|
||||
}
|
||||
|
||||
export type GateFunction = null | ((ctx: Context) => void)
|
||||
|
||||
export interface GateFunctions {
|
||||
activation: null | ((ctx: Context) => void)
|
||||
activation: GateFunction
|
||||
onClick: GateFunction
|
||||
}
|
||||
|
||||
export class Gate {
|
||||
|
@ -37,7 +40,8 @@ export class Gate {
|
|||
public template: GateTemplate
|
||||
|
||||
private functions: GateFunctions = {
|
||||
activation: null
|
||||
activation: null,
|
||||
onClick: null
|
||||
}
|
||||
|
||||
private subscriptions: Subscription[] = []
|
||||
|
@ -53,6 +57,11 @@ export class Gate {
|
|||
'context'
|
||||
)
|
||||
|
||||
this.functions.onClick = toFunction(
|
||||
this.template.code.onClick,
|
||||
'context'
|
||||
)
|
||||
|
||||
this._pins.inputs = Gate.generatePins(
|
||||
this.template.pins.inputs,
|
||||
1,
|
||||
|
@ -77,6 +86,12 @@ export class Gate {
|
|||
}
|
||||
}
|
||||
|
||||
public onClick() {
|
||||
if (this.functions.onClick) {
|
||||
this.functions.onClick(this.getContext())
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
for (const pin of this.pins) {
|
||||
pin.value.dispose()
|
||||
|
@ -104,7 +119,12 @@ export class Gate {
|
|||
set: (index: number, state: boolean = false) => {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export const DefaultGateTemplate: GateTemplate = {
|
|||
},
|
||||
pins: {
|
||||
inputs: {
|
||||
count: 2,
|
||||
count: 1,
|
||||
variable: false
|
||||
},
|
||||
outputs: {
|
||||
|
@ -25,8 +25,7 @@ export const DefaultGateTemplate: GateTemplate = {
|
|||
},
|
||||
code: {
|
||||
activation: 'context.set(0,true)',
|
||||
start: '',
|
||||
stop: ''
|
||||
onClick: ''
|
||||
},
|
||||
simulation: {
|
||||
debounce: {
|
||||
|
@ -36,5 +35,6 @@ export const DefaultGateTemplate: GateTemplate = {
|
|||
throttle: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
info: []
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ export interface GateTemplate {
|
|||
name: string
|
||||
}
|
||||
code: {
|
||||
start: string
|
||||
activation: string
|
||||
stop: string
|
||||
onClick: string
|
||||
}
|
||||
simulation: {
|
||||
throttle: TimePipe
|
||||
debounce: TimePipe
|
||||
}
|
||||
info: string[]
|
||||
}
|
||||
|
|
|
@ -77,6 +77,9 @@ export class SimulationRenderer {
|
|||
const { transform, id, pins } = gates[index]
|
||||
|
||||
if (pointInSquare(worldPosition, transform)) {
|
||||
// run function
|
||||
gates[index].onClick()
|
||||
|
||||
this.mouseManager.clear(worldPosition[0])
|
||||
|
||||
this.mouseState |= 1
|
||||
|
|
Loading…
Reference in a new issue