fixed rgb light, new info page

This commit is contained in:
Matei Adriel 2019-08-01 16:09:14 +03:00
parent 8a51c5ad52
commit fddba4e55e
25 changed files with 453 additions and 38 deletions

10
src/assets/comparator.svg Normal file
View file

@ -0,0 +1,10 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="800" fill="#00B512"/>
<path d="M314 438H336.331M383.435 377L356.045 438H336.331M383.435 377H411M383.435 377H364.768L336.331 438" stroke="white" stroke-width="10"/>
<path d="M288 519.5V281L512 401L288 519.5Z" stroke="white" stroke-width="10"/>
<path d="M512.5 403H584.5" stroke="white" stroke-width="10"/>
<path d="M429 356H501" stroke="white" stroke-width="10"/>
<path d="M429 446H501" stroke="white" stroke-width="10"/>
<path d="M216 316H288" stroke="white" stroke-width="10"/>
<path d="M216 482H288" stroke="white" stroke-width="10"/>
</svg>

After

Width:  |  Height:  |  Size: 670 B

7
src/assets/merger.svg Normal file
View file

@ -0,0 +1,7 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="800" fill="#DB00FF"/>
<path d="M352.11 400L283 492H312L386.5 400L312 308H283L352.11 400Z" fill="white"/>
<path d="M312 308H283L352.11 400L283 492H312M312 308H413.682C413.682 308 497.243 308 495.986 400C494.729 492 413.682 492 413.682 492H312M312 308L386.5 400L312 492" stroke="white" stroke-width="10"/>
<path d="M228 333H306.5M228 467.5H306.5" stroke="white" stroke-width="10"/>
<path d="M493.5 396H572M228 333H306.5M228 467.5H306.5" stroke="white" stroke-width="10"/>
</svg>

After

Width:  |  Height:  |  Size: 603 B

7
src/assets/splitter.svg Normal file
View file

@ -0,0 +1,7 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="800" transform="translate(800 800) rotate(-180)" fill="#DB00FF"/>
<path d="M447.89 400L517 308H488L413.5 400L488 492H517L447.89 400Z" fill="white"/>
<path d="M488 492H517L447.89 400L517 308H488M488 492H386.318C386.318 492 302.757 492 304.014 400C305.271 308 386.318 308 386.318 308H488M488 492L413.5 400L488 308" stroke="white" stroke-width="10"/>
<path d="M572 467H493.5M572 332.5H493.5" stroke="white" stroke-width="10"/>
<path d="M306.5 404H228M572 467H493.5M572 332.5H493.5" stroke="white" stroke-width="10"/>
</svg>

After

Width:  |  Height:  |  Size: 647 B

View file

@ -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)}`

View file

@ -16,4 +16,5 @@ body {
background-color: $bg; background-color: $bg;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
color: white;
} }

View file

@ -16,6 +16,7 @@ import Root from './Root'
import LogicGatePage from '../../logic-gates/components/LogicGatesPage' import LogicGatePage from '../../logic-gates/components/LogicGatesPage'
import { loadSubject } from '../subjects/loadedSubject' import { loadSubject } from '../subjects/loadedSubject'
import { CustomRouter } from './CustomRouter' import { CustomRouter } from './CustomRouter'
import LogicGateInfoPage from '../../logic-gate-info/components/LogicGateInfoPage'
const App = () => { const App = () => {
useEffect(() => { useEffect(() => {
@ -33,6 +34,7 @@ const App = () => {
<Route path="/" component={Root} exact /> <Route path="/" component={Root} exact />
<Route path="/gates" component={LogicGatePage} /> <Route path="/gates" component={LogicGatePage} />
<Route path="/info/:name" component={LogicGateInfoPage} />
</CustomRouter> </CustomRouter>
</Theme> </Theme>

View file

@ -16,7 +16,11 @@ const CreateSimulationButton = () => {
const translation = useTranslation() const translation = useTranslation()
return ( return (
<ListItem button className="contained" onClick={handleCreating}> <ListItem
button
className="contained create-simulation"
onClick={handleCreating}
>
<ListItemIcon> <ListItemIcon>
<Icon>note_add</Icon> <Icon>note_add</Icon>
</ListItemIcon> </ListItemIcon>

View file

@ -9,16 +9,19 @@ import { useTranslation } from '../../internalisation/helpers/useLanguage'
export interface CreateSimulationOption { export interface CreateSimulationOption {
mode: simulationMode mode: simulationMode
icon: string icon: string
id: string
} }
export const createSimulationOptions: CreateSimulationOption[] = [ export const createSimulationOptions: CreateSimulationOption[] = [
{ {
mode: 'project', mode: 'project',
icon: 'gamepad' icon: 'gamepad',
id: 'create-project'
}, },
{ {
icon: 'memory', icon: 'memory',
mode: 'ic' mode: 'ic',
id: 'create-ic'
} }
] ]
@ -52,7 +55,10 @@ const CreateSimulation = () => {
CreateSimulationStore.actions.next('submit') CreateSimulationStore.actions.next('submit')
}} }}
> >
<div className="create-option-icon"> <div
className={`create-option-icon ${option.id}`}
id={option.id}
>
<Icon>{option.icon}</Icon> <Icon>{option.icon}</Icon>
</div> </div>
<div className="create-option-name" id={option.mode}> <div className="create-option-name" id={option.mode}>

View file

@ -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;
}

View file

@ -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 (
<div className="page" id="logic-gate-info-page">
<div className="gate-preview">
<GatePreview template={template} />
</div>
<div id="gate-info-top-right">
<div id="gate-info-title">{firstCharUpperCase(name)}</div>
<div className="gate-info-description">{description}</div>
</div>
<div id="gate-info-io-table">
<LogicGateIoTable name={name} />
</div>
<div id="gate-info-read-more">
Read more: <br />
{template.info.map((url, index) => {
return (
<Fragment key={index}>
<a target="_blank" href={url}>
{url}
</a>
<br />
</Fragment>
)
})}
</div>
</div>
)
} catch {
return <Redirect to="/gates" />
}
})

View file

@ -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 (
<Paper id="gate-info-io-table-paper">
<Table>
<TableHead>
<TableRow>
{ioTable.columns.map((heading, index) => {
return <TableCell key={index}>{heading}</TableCell>
})}
</TableRow>
</TableHead>
<TableBody>
{ioTable.data.map((row, index) => {
return (
<TableRow key={index}>
{row.map((data, index) => {
return (
<TableCell key={index}>
{data}
</TableCell>
)
})}
</TableRow>
)
})}
</TableBody>
</Table>
</Paper>
)
}

View file

@ -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<string, string> = {
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
}
`
}

View file

@ -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)
}
}

View file

@ -3,23 +3,18 @@ import Icon from '@material-ui/core/Icon'
import IconButton from '@material-ui/core/IconButton' import IconButton from '@material-ui/core/IconButton'
import { LogicGateProps } from '../types/LogicGateProps' import { LogicGateProps } from '../types/LogicGateProps'
import { randomItem } from '../../internalisation/helpers/randomItem' import { randomItem } from '../../internalisation/helpers/randomItem'
import { Link } from 'react-router-dom'
const GateInfo = ({ template }: LogicGateProps) => { const GateInfo = ({ name }: { name: string }) => {
const info = template.info return (
<div className="gate-info-icon">
if (info.length === 0) { <Link to={`/info/${name}`}>
return <></> <IconButton aria-label="info">
} else { <Icon>info</Icon>
return ( </IconButton>
<div className="gate-info-icon"> </Link>
<a href={randomItem(info)} target="blank"> </div>
<IconButton aria-label="info"> )
<Icon>info</Icon>
</IconButton>
</a>
</div>
)
}
} }
export default GateInfo export default GateInfo

View file

@ -0,0 +1,8 @@
.gate-preview > * {
display: block;
height: 10em;
width: 10em;
border-radius: 1em;
margin: 1em;
}

View file

@ -1,16 +1,8 @@
@import '../../core/styles/mixins/flex.scss'; @import '../../core/styles/mixins/flex.scss';
@import './GatePreview.scss';
$gate-margin: 1em; $gate-margin: 1em;
.gate > section > .gate-preview > * {
display: block;
height: 10em;
width: 10em;
border-radius: 1em;
margin: $gate-margin;
}
.gate:hover { .gate:hover {
border: 2px solid white !important; border: 2px solid white !important;
} }

View file

@ -1,16 +1,16 @@
import './LogicGate.scss' import './LogicGate.scss'
import React from 'react' import React from 'react'
import GateInfo from './GateInfo' import GateInfo from './GateInfo'
import GateSettings from './GateSettings'
import AddGate from './AddGate' import AddGate from './AddGate'
import { addGateFromTemplate } from '../helpers/addGateFromTemplate' import { addGateFromTemplate } from '../helpers/addGateFromTemplate'
import DeleteGateIcon from './DeleteGate' import DeleteGateIcon from './DeleteGate'
import GatePreview from './GatePreview' import GatePreview from './GatePreview'
import { LogicGateProps } from '../types/LogicGateProps' import { LogicGateProps } from '../types/LogicGateProps'
import { firstCharUpperCase } from '../../../common/lang/strings/firstCharUpperCase'
const LogicGate = ({ template }: LogicGateProps) => { const LogicGate = ({ template }: LogicGateProps) => {
const rawName = template.metadata.name const rawName = template.metadata.name
const name = `${rawName[0].toUpperCase()}${rawName.substr(1)}` const name = firstCharUpperCase(rawName)
return ( return (
<div className="gate"> <div className="gate">
@ -27,8 +27,9 @@ const LogicGate = ({ template }: LogicGateProps) => {
</section> </section>
<section> <section>
<div className="gate-icons"> <div className="gate-icons">
<GateInfo template={template} /> {!template.tags.includes('integrated') && (
{/* <GateSettings template={template} /> */} <GateInfo name={name} />
)}
<AddGate template={template} /> <AddGate template={template} />
<DeleteGateIcon template={template} /> <DeleteGateIcon template={template} />
</div> </div>

View file

@ -16,6 +16,9 @@ import halfAdderTemplate from './templates/halfAdder'
import fullAdderTemplate from './templates/fullAdder' import fullAdderTemplate from './templates/fullAdder'
import _4bitEncoderTemplate from './templates/4bitEncoder' import _4bitEncoderTemplate from './templates/4bitEncoder'
import _4bitDecoderTemplate from './templates/4bitDecoder' 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 defaultSimulationName = 'default'
export const baseTemplates: DeepPartial<GateTemplate>[] = [ export const baseTemplates: DeepPartial<GateTemplate>[] = [
@ -34,7 +37,10 @@ export const baseTemplates: DeepPartial<GateTemplate>[] = [
halfAdderTemplate, halfAdderTemplate,
fullAdderTemplate, fullAdderTemplate,
_4bitEncoderTemplate, _4bitEncoderTemplate,
_4bitDecoderTemplate _4bitDecoderTemplate,
comparatorTemplate,
bitMergerTemplate,
bitSplitterTemplate
// commentTemplate // commentTemplate
] ]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -24,7 +24,12 @@ const fullAdderTemplate: PartialTemplate = {
count: 2 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 export default fullAdderTemplate

View file

@ -24,7 +24,12 @@ const halfAdderTemplate: PartialTemplate = {
count: 2 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 export default halfAdderTemplate

View file

@ -22,13 +22,14 @@ const rgbLightTemplate: PartialTemplate = {
}, },
code: { code: {
activation: ` 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){ if (color === 0){
context.color(context.colors.main) context.color(context.colors.main)
} }
else{ else {
context.color(context.colors[color]) context.color(context.colors[color])
} }
` `

View file

@ -6,3 +6,19 @@ export function* allCombinations<T>(first: T[], second: T[]): Iterable<[T, T]> {
} }
} }
} }
export const recursiveCombinations = <T>(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
}