diff --git a/src/assets/comparator.svg b/src/assets/comparator.svg new file mode 100644 index 0000000..14c9523 --- /dev/null +++ b/src/assets/comparator.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/merger.svg b/src/assets/merger.svg new file mode 100644 index 0000000..dd29bef --- /dev/null +++ b/src/assets/merger.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/splitter.svg b/src/assets/splitter.svg new file mode 100644 index 0000000..fb09d23 --- /dev/null +++ b/src/assets/splitter.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/common/lang/strings/firstCharUpperCase.ts b/src/common/lang/strings/firstCharUpperCase.ts new file mode 100644 index 0000000..df02cb7 --- /dev/null +++ b/src/common/lang/strings/firstCharUpperCase.ts @@ -0,0 +1,7 @@ +/** + * Makes the first char of a string uppercase + * + * @param name The string to convert + */ +export const firstCharUpperCase = (name: string) => + `${name[0].toUpperCase()}${name.substr(1)}` diff --git a/src/modules/core/components/App.scss b/src/modules/core/components/App.scss index 9b0475f..250d683 100644 --- a/src/modules/core/components/App.scss +++ b/src/modules/core/components/App.scss @@ -16,4 +16,5 @@ body { background-color: $bg; overflow-y: auto; overflow-x: hidden; + color: white; } diff --git a/src/modules/core/components/App.tsx b/src/modules/core/components/App.tsx index 000ee42..3453c73 100644 --- a/src/modules/core/components/App.tsx +++ b/src/modules/core/components/App.tsx @@ -16,6 +16,7 @@ import Root from './Root' import LogicGatePage from '../../logic-gates/components/LogicGatesPage' import { loadSubject } from '../subjects/loadedSubject' import { CustomRouter } from './CustomRouter' +import LogicGateInfoPage from '../../logic-gate-info/components/LogicGateInfoPage' const App = () => { useEffect(() => { @@ -33,6 +34,7 @@ const App = () => { + diff --git a/src/modules/core/components/CreateSimulationButton.tsx b/src/modules/core/components/CreateSimulationButton.tsx index 7ce8441..234838a 100644 --- a/src/modules/core/components/CreateSimulationButton.tsx +++ b/src/modules/core/components/CreateSimulationButton.tsx @@ -16,7 +16,11 @@ const CreateSimulationButton = () => { const translation = useTranslation() return ( - + note_add diff --git a/src/modules/create-simulation/components/CreateSimulation.tsx b/src/modules/create-simulation/components/CreateSimulation.tsx index 72784ba..21635d2 100644 --- a/src/modules/create-simulation/components/CreateSimulation.tsx +++ b/src/modules/create-simulation/components/CreateSimulation.tsx @@ -9,16 +9,19 @@ import { useTranslation } from '../../internalisation/helpers/useLanguage' export interface CreateSimulationOption { mode: simulationMode icon: string + id: string } export const createSimulationOptions: CreateSimulationOption[] = [ { mode: 'project', - icon: 'gamepad' + icon: 'gamepad', + id: 'create-project' }, { icon: 'memory', - mode: 'ic' + mode: 'ic', + id: 'create-ic' } ] @@ -52,7 +55,10 @@ const CreateSimulation = () => { CreateSimulationStore.actions.next('submit') }} > -
+
{option.icon}
diff --git a/src/modules/logic-gate-info/components/LogicGateInfoPage.scss b/src/modules/logic-gate-info/components/LogicGateInfoPage.scss new file mode 100644 index 0000000..10db724 --- /dev/null +++ b/src/modules/logic-gate-info/components/LogicGateInfoPage.scss @@ -0,0 +1,33 @@ +@import '../../core/styles/mixins/flex.scss'; + +#logic-gate-info-page { + display: grid; + grid-template-columns: 12rem auto; + grid-template-rows: 15rem auto 1fr; +} + +#logic-gate-info-page > * { + padding: 3rem; +} + +#gate-info-top-right > #gate-info-title { + font-size: 3rem; +} + +#gate-info-io-table { + @include flex(); + width: 100%; +} + +#gate-info-io-table, +#gate-info-read-more { + grid-column: 1 / 3; +} + +#gate-info-io-table-paper { + width: 100%; +} + +#gate-info-read-more > a { + color: #8888ff; +} diff --git a/src/modules/logic-gate-info/components/LogicGateInfoPage.tsx b/src/modules/logic-gate-info/components/LogicGateInfoPage.tsx new file mode 100644 index 0000000..2619bf9 --- /dev/null +++ b/src/modules/logic-gate-info/components/LogicGateInfoPage.tsx @@ -0,0 +1,47 @@ +import React, { Fragment } from 'react' +import '../../logic-gates/components/GatePreview.scss' +import './LogicGateInfoPage.scss' +import { withRouter, Redirect } from 'react-router' +import GatePreview from '../../logic-gates/components/GatePreview' +import { getTemplateSafely } from '../../logic-gates/helpers/getTemplateSafely' +import { firstCharUpperCase } from '../../../common/lang/strings/firstCharUpperCase' +import { descriptions } from '../data/descriptions' +import LogicGateIoTable from './LogicGateIoTable' + +export default withRouter(props => { + try { + const name = props.match.params.name.toLowerCase() + const template = getTemplateSafely(name) + const description = descriptions[name] || '' + + return ( +
+
+ +
+
+
{firstCharUpperCase(name)}
+
{description}
+
+
+ +
+
+ Read more:
+ {template.info.map((url, index) => { + return ( + + + {url} + +
+
+ ) + })} +
+
+ ) + } catch { + return + } +}) diff --git a/src/modules/logic-gate-info/components/LogicGateIoTable.tsx b/src/modules/logic-gate-info/components/LogicGateIoTable.tsx new file mode 100644 index 0000000..12e1e33 --- /dev/null +++ b/src/modules/logic-gate-info/components/LogicGateIoTable.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { ioTables } from '../data/tables' +import Table from '@material-ui/core/Table' +import TableBody from '@material-ui/core/TableBody' +import TableCell from '@material-ui/core/TableCell' +import TableHead from '@material-ui/core/TableHead' +import TableRow from '@material-ui/core/TableRow' +import Paper from '@material-ui/core/Paper' + +export default ({ name }: { name: string }) => { + const ioTable = ioTables[name] || {} + + return ( + + + + + {ioTable.columns.map((heading, index) => { + return {heading} + })} + + + + {ioTable.data.map((row, index) => { + return ( + + {row.map((data, index) => { + return ( + + {data} + + ) + })} + + ) + })} + +
+
+ ) +} diff --git a/src/modules/logic-gate-info/data/descriptions.ts b/src/modules/logic-gate-info/data/descriptions.ts new file mode 100644 index 0000000..9d7544c --- /dev/null +++ b/src/modules/logic-gate-info/data/descriptions.ts @@ -0,0 +1,51 @@ +const carryExplanation = (half = true) => ` + The carry out is necessary to preserve the bit count + (the maximum sum of 2 n-bit integers ${ + half ? '' : 'and a carry' + } beeing 1 << (n + 1) - ${half ? 2 : 1}) +` + +export const descriptions: Record = { + not: ` + The not gate is one of the most basic logic gates. + It outputs the inverse of the input + `, + and: ` + The and gate outputs true only if both inputs are true. + The and gate is the logic equivalent of f(x, y) = x * y + `, + or: ` + The or gate outputs true if any of the inputs is true. + The or gate is the logic equivalent of f(x, y) = x + y - x * y + `, + nand: ` + The nand gate only outputs true if none or one of the inputs is true. + The nand gate is the logic equivalent of f(x, y) = 1 - x * y + `, + nor: ` + The nor gate only outputs true if none of the inputs is true. + The nor gate is the logic equivalent of f(x, y) = 1 + x * y - (x + y) + `, + xor: ` + The xor gate (also known as the 'exclusive or' gate) only outputs true if one and only one of the inputs is true. + The xor gate is the logic equivalent of f(x, y) = x + y - 2 * x * y + `, + xnor: ` + The xnor gate (also known as the 'not exlusive or' gate) only outputs true if none or both the inputs are true. + The xnor gate is the logic equivalent of f(x, y) = 1 - |x - y| + `, + 'half adder': ` + The half adder is used to add 2 numbers. It outputs a result and a carry. + ${carryExplanation()} + The half adder is the logic equivalent of f(x, y) = { x + y - 2 * x * y, x * y } + `, + 'full adder': ` + The full adder is the building block for almos all math related circuit. + The full adder is used to add 2 number and a carry. It outputs a result and a carry. + ${carryExplanation(false)} + The full adder is the logic equivalent of f(x, y, z) = { + x + y + z + 4 * x * y * z - 2 * (x * y + y * z + z * x), + x * y + y * z + z * x - 2 * x * y * z + } + ` +} diff --git a/src/modules/logic-gate-info/data/tables.ts b/src/modules/logic-gate-info/data/tables.ts new file mode 100644 index 0000000..9b8b355 --- /dev/null +++ b/src/modules/logic-gate-info/data/tables.ts @@ -0,0 +1,59 @@ +import { + allCombinations, + recursiveCombinations +} from '../../simulation/helpers/allCombinations' + +const _2i1oColumns = ['Input A', 'Input B', 'Output'] + +const adderData = (half = true) => { + return recursiveCombinations([0, 1], half ? 2 : 3).map(combination => { + const a = combination[0] + combination[1] + (half ? 0 : combination[2]) + + return [...combination, Number(a % 2 === 1), Number(a >= 2)] + }) +} + +export const ioTables: Record< + string, + { + columns: string[] + data: number[][] + } +> = { + not: { + columns: ['Input', 'Output'], + data: [[0, 1], [1, 0]] + }, + and: { + columns: _2i1oColumns, + data: [[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 1]] + }, + or: { + columns: _2i1oColumns, + data: [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]] + }, + nor: { + columns: _2i1oColumns, + data: [[0, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 0]] + }, + nand: { + columns: _2i1oColumns, + data: [[0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 0]] + }, + xor: { + columns: _2i1oColumns, + data: [[0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0]] + }, + xnor: { + columns: _2i1oColumns, + data: [[0, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 1]] + }, + 'half adder': { + columns: ['x', 'y', 'sum', 'carry out'], + data: adderData() + }, + 'full adder': { + columns: ['carry in', 'x', 'y', 'sum', 'carry out'], + data: adderData(false) + } +} diff --git a/src/modules/logic-gates/components/GateInfo.tsx b/src/modules/logic-gates/components/GateInfo.tsx index 24033fd..45982a3 100644 --- a/src/modules/logic-gates/components/GateInfo.tsx +++ b/src/modules/logic-gates/components/GateInfo.tsx @@ -3,23 +3,18 @@ import Icon from '@material-ui/core/Icon' import IconButton from '@material-ui/core/IconButton' import { LogicGateProps } from '../types/LogicGateProps' import { randomItem } from '../../internalisation/helpers/randomItem' +import { Link } from 'react-router-dom' -const GateInfo = ({ template }: LogicGateProps) => { - const info = template.info - - if (info.length === 0) { - return <> - } else { - return ( - - ) - } +const GateInfo = ({ name }: { name: string }) => { + return ( +
+ + + info + + +
+ ) } export default GateInfo diff --git a/src/modules/logic-gates/components/GatePreview.scss b/src/modules/logic-gates/components/GatePreview.scss new file mode 100644 index 0000000..4884703 --- /dev/null +++ b/src/modules/logic-gates/components/GatePreview.scss @@ -0,0 +1,8 @@ +.gate-preview > * { + display: block; + height: 10em; + width: 10em; + border-radius: 1em; + + margin: 1em; +} diff --git a/src/modules/logic-gates/components/LogicGate.scss b/src/modules/logic-gates/components/LogicGate.scss index 51d1da9..a145ab4 100644 --- a/src/modules/logic-gates/components/LogicGate.scss +++ b/src/modules/logic-gates/components/LogicGate.scss @@ -1,16 +1,8 @@ @import '../../core/styles/mixins/flex.scss'; +@import './GatePreview.scss'; $gate-margin: 1em; -.gate > section > .gate-preview > * { - display: block; - height: 10em; - width: 10em; - border-radius: 1em; - - margin: $gate-margin; -} - .gate:hover { border: 2px solid white !important; } diff --git a/src/modules/logic-gates/components/LogicGate.tsx b/src/modules/logic-gates/components/LogicGate.tsx index 4de0ba3..587b00f 100644 --- a/src/modules/logic-gates/components/LogicGate.tsx +++ b/src/modules/logic-gates/components/LogicGate.tsx @@ -1,16 +1,16 @@ import './LogicGate.scss' import React from 'react' import GateInfo from './GateInfo' -import GateSettings from './GateSettings' import AddGate from './AddGate' import { addGateFromTemplate } from '../helpers/addGateFromTemplate' import DeleteGateIcon from './DeleteGate' import GatePreview from './GatePreview' import { LogicGateProps } from '../types/LogicGateProps' +import { firstCharUpperCase } from '../../../common/lang/strings/firstCharUpperCase' const LogicGate = ({ template }: LogicGateProps) => { const rawName = template.metadata.name - const name = `${rawName[0].toUpperCase()}${rawName.substr(1)}` + const name = firstCharUpperCase(rawName) return (
@@ -27,8 +27,9 @@ const LogicGate = ({ template }: LogicGateProps) => {
- - {/* */} + {!template.tags.includes('integrated') && ( + + )}
diff --git a/src/modules/saving/constants.ts b/src/modules/saving/constants.ts index 1c006a0..e5cbc27 100644 --- a/src/modules/saving/constants.ts +++ b/src/modules/saving/constants.ts @@ -16,6 +16,9 @@ import halfAdderTemplate from './templates/halfAdder' import fullAdderTemplate from './templates/fullAdder' import _4bitEncoderTemplate from './templates/4bitEncoder' import _4bitDecoderTemplate from './templates/4bitDecoder' +import comparatorTemplate from './templates/comparator' +import bitMergerTemplate from './templates/bitMerger' +import bitSplitterTemplate from './templates/bitSplitter' export const defaultSimulationName = 'default' export const baseTemplates: DeepPartial[] = [ @@ -34,7 +37,10 @@ export const baseTemplates: DeepPartial[] = [ halfAdderTemplate, fullAdderTemplate, _4bitEncoderTemplate, - _4bitDecoderTemplate + _4bitDecoderTemplate, + comparatorTemplate, + bitMergerTemplate, + bitSplitterTemplate // commentTemplate ] diff --git a/src/modules/saving/templates/bitMerger.ts b/src/modules/saving/templates/bitMerger.ts new file mode 100644 index 0000000..83e5c0f --- /dev/null +++ b/src/modules/saving/templates/bitMerger.ts @@ -0,0 +1,31 @@ +import { PartialTemplate } from '../types/PartialTemplate' +import { categories } from '../data/categories' + +/** + * The template of the bitMerger gate + */ +const bitMergerTemplate: PartialTemplate = { + metadata: { + name: 'bit merger' + }, + pins: { + inputs: { + count: 2 + } + }, + code: { + activation: ` + const a = context.get(0) + const b = context.get(1) + + context.set(0, a + b) + ` + }, + category: categories.compressing, + material: { + type: 'image', + fill: require('../../../assets/merger') + } +} + +export default bitMergerTemplate diff --git a/src/modules/saving/templates/bitSplitter.ts b/src/modules/saving/templates/bitSplitter.ts new file mode 100644 index 0000000..a1b981d --- /dev/null +++ b/src/modules/saving/templates/bitSplitter.ts @@ -0,0 +1,40 @@ +import { PartialTemplate } from '../types/PartialTemplate' +import { categories } from '../data/categories' + +/** + * The template of the bitSplitter gate + */ +const bitSplitterTemplate: PartialTemplate = { + metadata: { + name: 'bit splitter' + }, + code: { + activation: ` + const a = context.get(0) + const l = a.length + (a.length % 2 === 1) + const b = context.toLength(a, l) + const half = b.length / 2 + + const chunks = [ + b.substr(0, half), + b.substr(half) + ] + + for (let index = 0; index < 2; index++ ) { + context.set(index, chunks[index]) + } + ` + }, + material: { + type: 'image', + fill: require('../../../assets/splitter') + }, + category: categories.compressing, + pins: { + outputs: { + count: 2 + } + } +} + +export default bitSplitterTemplate diff --git a/src/modules/saving/templates/comparator.ts b/src/modules/saving/templates/comparator.ts new file mode 100644 index 0000000..3c9f3c4 --- /dev/null +++ b/src/modules/saving/templates/comparator.ts @@ -0,0 +1,40 @@ +import { PartialTemplate } from '../types/PartialTemplate' +import { categories } from '../data/categories' + +/** + * The template of the comparator gate + */ +const comparatorTemplate: PartialTemplate = { + metadata: { + name: 'comparator' + }, + info: ['https://www.technobyte.org/comparator/'], + category: categories.math, + code: { + activation: ` + const a = context.getBinary(0) + const b = context.getBinary(1) + + context.setBinary(0, Number(a > b)) + context.setBinary(1, Number(a === b)) + context.setBinary(2, Number(a < b)) + ` + }, + pins: { + inputs: { + count: 2 + }, + outputs: { + count: 3 + } + }, + material: { + type: 'image', + fill: require('../../../assets/comparator.svg') + }, + shape: { + scale: [125, 125] + } +} + +export default comparatorTemplate diff --git a/src/modules/saving/templates/fullAdder.ts b/src/modules/saving/templates/fullAdder.ts index d642d17..959f6ef 100644 --- a/src/modules/saving/templates/fullAdder.ts +++ b/src/modules/saving/templates/fullAdder.ts @@ -24,7 +24,12 @@ const fullAdderTemplate: PartialTemplate = { count: 2 } }, - category: categories.math + category: categories.math, + info: [ + 'https://www.elprocus.com/half-adder-and-full-adder/', + 'https://en.wikipedia.org/wiki/Adder_(electronics)', + 'https://www.geeksforgeeks.org/full-adder-digital-electronics/' + ] } export default fullAdderTemplate diff --git a/src/modules/saving/templates/halfAdder.ts b/src/modules/saving/templates/halfAdder.ts index f8dbd9d..653aa9a 100644 --- a/src/modules/saving/templates/halfAdder.ts +++ b/src/modules/saving/templates/halfAdder.ts @@ -24,7 +24,12 @@ const halfAdderTemplate: PartialTemplate = { count: 2 } }, - category: categories.math + category: categories.math, + info: [ + 'https://www.elprocus.com/half-adder-and-full-adder/', + 'https://en.wikipedia.org/wiki/Adder_(electronics)', + 'http://isweb.redwoods.edu/instruct/calderwoodd/diglogic/half-add.htm' + ] } export default halfAdderTemplate diff --git a/src/modules/saving/templates/rgb.ts b/src/modules/saving/templates/rgb.ts index 2b06e3d..a55c4a6 100644 --- a/src/modules/saving/templates/rgb.ts +++ b/src/modules/saving/templates/rgb.ts @@ -22,13 +22,14 @@ const rgbLightTemplate: PartialTemplate = { }, code: { activation: ` - const color = (context.get(0) << 2) + (context.get(1) << 1) + context.get(2) + const get = (index) => context.getBinary(index) & 1 + const color = (get(0) << 2) + (get(1) << 1) + get(2) if (color === 0){ context.color(context.colors.main) } - else{ + else { context.color(context.colors[color]) } ` diff --git a/src/modules/simulation/helpers/allCombinations.ts b/src/modules/simulation/helpers/allCombinations.ts index eabdc4b..c7f2c6d 100644 --- a/src/modules/simulation/helpers/allCombinations.ts +++ b/src/modules/simulation/helpers/allCombinations.ts @@ -6,3 +6,19 @@ export function* allCombinations(first: T[], second: T[]): Iterable<[T, T]> { } } } + +export const recursiveCombinations = (values: T[], depth = 2) => { + if (depth === 0) { + return [[]] + } + + const combinations: T[][] = [] + + for (const value of values) { + for (const combination of recursiveCombinations(values, depth - 1)) { + combinations.push([value, ...combination]) + } + } + + return combinations +}