From e88244bc9dd92d891ecee8db532c480a716c25e4 Mon Sep 17 00:00:00 2001 From: Matei Adriel Date: Mon, 22 Jul 2019 19:58:26 +0300 Subject: [PATCH] logic gate button --- src/modules/activation/types/Context.ts | 1 + src/modules/core/components/App.tsx | 2 + src/modules/core/components/Language.scss | 5 + src/modules/core/components/Language.tsx | 31 +++++ src/modules/core/components/LogicGates.tsx | 89 +++------------ src/modules/core/components/Sidebar.tsx | 10 +- src/modules/core/styles/colors.scss | 1 + src/modules/core/styles/mixins/glow.scss | 16 +++ src/modules/core/styles/reset.scss | 5 + .../components/CreateOption.scss | 4 +- src/modules/internalisation/constants.ts | 14 ++- .../internalisation/helpers/nextLanguage.ts | 17 +++ .../internalisation/helpers/randomItem.ts | 7 ++ .../internalisation/stores/currentLanguage.ts | 6 +- .../subjects/currentLanguageSubject.ts | 4 +- .../internalisation/translations/english.ts | 3 +- .../translations/nederlands.ts | 33 ++++++ .../internalisation/translations/romanian.ts | 7 +- .../types/TranslationInterface.ts | 5 +- .../types/supportedLanguages.ts | 2 +- .../components/LogicGateModal.scss | 62 ++++++++++ .../logic-gates/components/LogicGateModal.tsx | 90 +++++++++++++++ src/modules/logic-gates/helpers/getAllIcs.ts | 26 +++++ .../logic-gates/subjects/LogicGateList.ts | 37 ++++++ src/modules/saving/constants.ts | 107 ++++++++++++++++-- src/modules/simulation/classes/Gate.ts | 26 ++++- src/modules/simulation/constants.ts | 8 +- src/modules/simulation/types/GateTemplate.ts | 4 +- .../classes/SimulationRenderer.ts | 3 + 29 files changed, 518 insertions(+), 107 deletions(-) create mode 100644 src/modules/core/components/Language.scss create mode 100644 src/modules/core/components/Language.tsx create mode 100644 src/modules/core/styles/mixins/glow.scss create mode 100644 src/modules/internalisation/helpers/nextLanguage.ts create mode 100644 src/modules/internalisation/helpers/randomItem.ts create mode 100644 src/modules/internalisation/translations/nederlands.ts create mode 100644 src/modules/logic-gates/components/LogicGateModal.scss create mode 100644 src/modules/logic-gates/components/LogicGateModal.tsx create mode 100644 src/modules/logic-gates/helpers/getAllIcs.ts create mode 100644 src/modules/logic-gates/subjects/LogicGateList.ts diff --git a/src/modules/activation/types/Context.ts b/src/modules/activation/types/Context.ts index f418d27..973583c 100644 --- a/src/modules/activation/types/Context.ts +++ b/src/modules/activation/types/Context.ts @@ -2,4 +2,5 @@ export interface Context { memory: Record get: (index: number) => boolean set: (index: number, state: boolean) => void + color: (color: string) => void } diff --git a/src/modules/core/components/App.tsx b/src/modules/core/components/App.tsx index 91c3fe8..31707f6 100644 --- a/src/modules/core/components/App.tsx +++ b/src/modules/core/components/App.tsx @@ -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 = () => { + { + const translation = useTranslation() + + return ( + + + + language + + + {translation.sidebar.language}: {translation.language} + + + + ) +} + +export default Language diff --git a/src/modules/core/components/LogicGates.tsx b/src/modules/core/components/LogicGates.tsx index dbf5f0f..7af8256 100644 --- a/src/modules/core/components/LogicGates.tsx +++ b/src/modules/core/components/LogicGates.tsx @@ -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([]) - -/** - * 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) - const simulations = useObservable(() => allGatesSubject, []) - const translation = useTranslation() - const handleClose = () => { - setAnchorEl(null) - } - return ( - <> - { - updateTemplateList() - setAnchorEl(event.currentTarget) - }} - > - - memory - - {translation.sidebar.logicGates} - - - - {simulations.map((simulationName, index) => { - const simulationData = templateStore.get(simulationName) - - if (!simulationData) { - throw new SimulationError( - `Cannot get data for simulation ${simulationName}` - ) - } - - return ( - { - switchTo(simulationName) - handleClose() - }} - > - {simulationName} - - ) - })} - - + { + updateLogicGateList() + open.next(true) + }} + > + + memory + + {translation.sidebar.logicGates} + ) } diff --git a/src/modules/core/components/Sidebar.tsx b/src/modules/core/components/Sidebar.tsx index 32c356c..0880958 100644 --- a/src/modules/core/components/Sidebar.tsx +++ b/src/modules/core/components/Sidebar.tsx @@ -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 }} > - + + + ) diff --git a/src/modules/core/styles/colors.scss b/src/modules/core/styles/colors.scss index cf790d9..aef8a79 100644 --- a/src/modules/core/styles/colors.scss +++ b/src/modules/core/styles/colors.scss @@ -1,2 +1,3 @@ $modal-bg-color: rgba(0, 0, 0, 0.7); $primary: #673ab7; +$grey: #444444; diff --git a/src/modules/core/styles/mixins/glow.scss b/src/modules/core/styles/mixins/glow.scss new file mode 100644 index 0000000..17d7c04 --- /dev/null +++ b/src/modules/core/styles/mixins/glow.scss @@ -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; +} diff --git a/src/modules/core/styles/reset.scss b/src/modules/core/styles/reset.scss index d38bf8c..942b44d 100644 --- a/src/modules/core/styles/reset.scss +++ b/src/modules/core/styles/reset.scss @@ -444,3 +444,8 @@ textarea { color: #000; padding: 0.2em 0; } + +a { + color: white; + text-decoration: none; +} diff --git a/src/modules/create-simulation/components/CreateOption.scss b/src/modules/create-simulation/components/CreateOption.scss index b962742..062b207 100644 --- a/src/modules/create-simulation/components/CreateOption.scss +++ b/src/modules/create-simulation/components/CreateOption.scss @@ -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; diff --git a/src/modules/internalisation/constants.ts b/src/modules/internalisation/constants.ts index c7f743a..42e19b6 100644 --- a/src/modules/internalisation/constants.ts +++ b/src/modules/internalisation/constants.ts @@ -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 = { +export const translations: Record = { english: EnglishTranslation, - ['română']: RomanianTranslation + ['română']: RomanianTranslation, + dutch: DutchTranslation } + +export const allSupportedLanguages: supportedLanguage[] = [ + 'english', + 'română', + 'dutch' +] diff --git a/src/modules/internalisation/helpers/nextLanguage.ts b/src/modules/internalisation/helpers/nextLanguage.ts new file mode 100644 index 0000000..5802902 --- /dev/null +++ b/src/modules/internalisation/helpers/nextLanguage.ts @@ -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] + ) +} diff --git a/src/modules/internalisation/helpers/randomItem.ts b/src/modules/internalisation/helpers/randomItem.ts new file mode 100644 index 0000000..7b78d03 --- /dev/null +++ b/src/modules/internalisation/helpers/randomItem.ts @@ -0,0 +1,7 @@ +/** + * Returns a random element of the aray + * + * @param arr - the array to choose from + */ +export const randomItem = (arr: T[]): T => + arr[Math.floor(Math.random() * arr.length)] diff --git a/src/modules/internalisation/stores/currentLanguage.ts b/src/modules/internalisation/stores/currentLanguage.ts index c2cada7..1e4b8d5 100644 --- a/src/modules/internalisation/stores/currentLanguage.ts +++ b/src/modules/internalisation/stores/currentLanguage.ts @@ -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( +export const CurrentLanguageLocalStore = new LocalStore( '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) }, diff --git a/src/modules/internalisation/subjects/currentLanguageSubject.ts b/src/modules/internalisation/subjects/currentLanguageSubject.ts index 219fde6..3dba6e6 100644 --- a/src/modules/internalisation/subjects/currentLanguageSubject.ts +++ b/src/modules/internalisation/subjects/currentLanguageSubject.ts @@ -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('english') +export default new BehaviorSubject('english') diff --git a/src/modules/internalisation/translations/english.ts b/src/modules/internalisation/translations/english.ts index 1c784fd..e6881ed 100644 --- a/src/modules/internalisation/translations/english.ts +++ b/src/modules/internalisation/translations/english.ts @@ -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: { diff --git a/src/modules/internalisation/translations/nederlands.ts b/src/modules/internalisation/translations/nederlands.ts new file mode 100644 index 0000000..67e862e --- /dev/null +++ b/src/modules/internalisation/translations/nederlands.ts @@ -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}'` + } +} diff --git a/src/modules/internalisation/translations/romanian.ts b/src/modules/internalisation/translations/romanian.ts index 333eca4..b2fa222 100644 --- a/src/modules/internalisation/translations/romanian.ts +++ b/src/modules/internalisation/translations/romanian.ts @@ -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` diff --git a/src/modules/internalisation/types/TranslationInterface.ts b/src/modules/internalisation/types/TranslationInterface.ts index 4995db1..c84de13 100644 --- a/src/modules/internalisation/types/TranslationInterface.ts +++ b/src/modules/internalisation/types/TranslationInterface.ts @@ -1,4 +1,4 @@ -import { supportedLanguages } from './supportedLanguages' +import { supportedLanguage } from './supportedLanguages' import { simulationMode } from '../../saving/types/SimulationSave' export type SentenceFactory = (...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: { diff --git a/src/modules/internalisation/types/supportedLanguages.ts b/src/modules/internalisation/types/supportedLanguages.ts index fadf838..2e348c5 100644 --- a/src/modules/internalisation/types/supportedLanguages.ts +++ b/src/modules/internalisation/types/supportedLanguages.ts @@ -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' diff --git a/src/modules/logic-gates/components/LogicGateModal.scss b/src/modules/logic-gates/components/LogicGateModal.scss new file mode 100644 index 0000000..7ef63cd --- /dev/null +++ b/src/modules/logic-gates/components/LogicGateModal.scss @@ -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; +} diff --git a/src/modules/logic-gates/components/LogicGateModal.tsx b/src/modules/logic-gates/components/LogicGateModal.tsx new file mode 100644 index 0000000..6bad1fc --- /dev/null +++ b/src/modules/logic-gates/components/LogicGateModal.tsx @@ -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 ( +
+ {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 ( +
{ + addGate(renderer.simulation, gate.name) + }} + > + + {gate.source === 'base' ? 'sd_storage' : 'memory'} + + + {gate.name} + + {template && template.info && template.info.length && ( + + info + + )} + {gate.source === 'ic' && ( + + delete + + )} +
+ ) + })} +
+ ) +} + +export default LogicGateModal diff --git a/src/modules/logic-gates/helpers/getAllIcs.ts b/src/modules/logic-gates/helpers/getAllIcs.ts new file mode 100644 index 0000000..bb1a74a --- /dev/null +++ b/src/modules/logic-gates/helpers/getAllIcs.ts @@ -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 +} diff --git a/src/modules/logic-gates/subjects/LogicGateList.ts b/src/modules/logic-gates/subjects/LogicGateList.ts new file mode 100644 index 0000000..d141c1f --- /dev/null +++ b/src/modules/logic-gates/subjects/LogicGateList.ts @@ -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([]) + +/** + * 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]) +} diff --git a/src/modules/saving/constants.ts b/src/modules/saving/constants.ts index 78ce138..ab956a8 100644 --- a/src/modules/saving/constants.ts +++ b/src/modules/saving/constants.ts @@ -3,25 +3,118 @@ import { RendererState } from './types/SimulationSave' export const defaultSimulationName = 'default' export const baseTemplates: DeepPartial[] = [ + { + 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 } } } diff --git a/src/modules/simulation/classes/Gate.ts b/src/modules/simulation/classes/Gate.ts index 4049238..5bbbd11 100644 --- a/src/modules/simulation/classes/Gate.ts +++ b/src/modules/simulation/classes/Gate.ts @@ -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 + } + } } } diff --git a/src/modules/simulation/constants.ts b/src/modules/simulation/constants.ts index 3b7d92b..bb2c815 100644 --- a/src/modules/simulation/constants.ts +++ b/src/modules/simulation/constants.ts @@ -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: [] } diff --git a/src/modules/simulation/types/GateTemplate.ts b/src/modules/simulation/types/GateTemplate.ts index ed2b0cc..17af852 100644 --- a/src/modules/simulation/types/GateTemplate.ts +++ b/src/modules/simulation/types/GateTemplate.ts @@ -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[] } diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts index 0acadab..b4d99f4 100644 --- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts +++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts @@ -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