├── .gitignore ├── dist ├── archives │ ├── hradla-1.7.7.zip │ └── hradla-1.7.7.tar.gz ├── docs │ ├── gen │ │ ├── img │ │ │ └── toast-ui.png │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── scripts │ │ │ ├── prettify │ │ │ │ └── lang-css.js │ │ │ ├── linenumber.js │ │ │ └── tui-doc.js │ │ └── styles │ │ │ ├── prettify-jsdoc.css │ │ │ └── prettify-tomorrow.css │ └── user.html ├── fonts │ ├── forkawesome-webfont.eot │ ├── forkawesome-webfont.ttf │ ├── forkawesome-webfont.woff │ └── forkawesome-webfont.woff2 ├── img │ ├── gui │ │ ├── help.svg │ │ ├── import.svg │ │ ├── library.svg │ │ ├── tutorial.svg │ │ ├── export.svg │ │ └── github.svg │ └── svg │ │ ├── other │ │ ├── output-off.svg │ │ ├── output-on.svg │ │ ├── output.svg │ │ ├── output-osc.svg │ │ ├── input-on.svg │ │ └── input.svg │ │ └── gate │ │ ├── repeater.svg │ │ ├── or.svg │ │ ├── not.svg │ │ ├── xor.svg │ │ ├── and.svg │ │ ├── nor.svg │ │ ├── xnor.svg │ │ └── nand.svg ├── library │ ├── adder-blackbox.json │ ├── subtractor-blackbox.json │ ├── networkList.json │ ├── rs-nand-latch.json │ ├── half-adder.json │ ├── half-subtractor.json │ ├── xor-and-or-adder.json │ └── xor-and-or-subtractor.json ├── index.html └── css │ ├── jsdoc.min.css │ ├── lib │ └── lity.min.css │ └── docs.min.css ├── src ├── fonts │ ├── forkawesome-webfont.eot │ ├── forkawesome-webfont.ttf │ ├── forkawesome-webfont.woff │ └── forkawesome-webfont.woff2 ├── scss │ ├── lib │ │ ├── _normalize.scss │ │ ├── forkAwesome │ │ │ ├── _fixed-width.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _core.scss │ │ │ ├── _stacked.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _path.scss │ │ │ ├── _animated.scss │ │ │ └── _mixins.scss │ │ ├── normalize │ │ │ ├── _import-now.scss │ │ │ ├── _variables.scss │ │ │ └── _vertical-rhythm.scss │ │ └── _forkAwesome.scss │ ├── style.scss │ ├── _colors.scss │ ├── jsdoc.scss │ ├── _svg.scss │ ├── docs.scss │ └── _tutorial.scss ├── es6 │ ├── main.js │ ├── modules │ │ ├── editorElements │ │ │ ├── stateClasses.js │ │ │ ├── OutputConnector.js │ │ │ ├── HelperWire.js │ │ │ ├── NetworkElement.js │ │ │ ├── InputConnector.js │ │ │ ├── OutputBox.js │ │ │ ├── InputBox.js │ │ │ ├── Gate.js │ │ │ └── Connector.js │ │ ├── svgObjects │ │ │ ├── SvgImage.js │ │ │ ├── Group.js │ │ │ ├── SvgElement.js │ │ │ ├── Rectangle.js │ │ │ ├── Text.js │ │ │ ├── PolyLine.js │ │ │ ├── Pattern.js │ │ │ ├── PolyLinePoint.js │ │ │ ├── MultiLineText.js │ │ │ ├── Tag.js │ │ │ └── PolyLinePoints.js │ │ ├── svgObjects.js │ │ ├── editorElements.js │ │ ├── other │ │ │ ├── id.js │ │ │ ├── mapWithDefaultValue.js │ │ │ └── helperFunctions.js │ │ └── ui │ │ │ ├── networkLibrary.js │ │ │ └── ViewBox.js │ └── routeWorker.js ├── html │ ├── help.html │ └── index.html ├── help │ └── user.md └── img │ ├── gui │ ├── import.svg │ ├── help.svg │ ├── tutorial.svg │ └── library.svg │ └── svg │ └── other │ ├── output.svg │ ├── output-off.svg │ ├── output-on.svg │ ├── output-osc.svg │ ├── input.svg │ └── input-on.svg ├── .eslintrc ├── library ├── adder-blackbox.json ├── subtractor-blackbox.json ├── rs-nand-latch.json ├── half-adder.json └── half-subtractor.json ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | gh-pages/ 3 | .sass-cache/ 4 | .idea/ 5 | -------------------------------------------------------------------------------- /dist/archives/hradla-1.7.7.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/archives/hradla-1.7.7.zip -------------------------------------------------------------------------------- /dist/docs/gen/img/toast-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/docs/gen/img/toast-ui.png -------------------------------------------------------------------------------- /dist/archives/hradla-1.7.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/archives/hradla-1.7.7.tar.gz -------------------------------------------------------------------------------- /dist/fonts/forkawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/fonts/forkawesome-webfont.eot -------------------------------------------------------------------------------- /dist/fonts/forkawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/fonts/forkawesome-webfont.ttf -------------------------------------------------------------------------------- /dist/fonts/forkawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/fonts/forkawesome-webfont.woff -------------------------------------------------------------------------------- /src/fonts/forkawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/src/fonts/forkawesome-webfont.eot -------------------------------------------------------------------------------- /src/fonts/forkawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/src/fonts/forkawesome-webfont.ttf -------------------------------------------------------------------------------- /src/fonts/forkawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/src/fonts/forkawesome-webfont.woff -------------------------------------------------------------------------------- /src/fonts/forkawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/src/fonts/forkawesome-webfont.woff2 -------------------------------------------------------------------------------- /dist/fonts/forkawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/fonts/forkawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/scss/lib/_normalize.scss: -------------------------------------------------------------------------------- 1 | @import 'normalize/variables'; 2 | @import 'normalize/vertical-rhythm'; 3 | @import 'normalize/normalize-mixin'; 4 | -------------------------------------------------------------------------------- /dist/docs/gen/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/docs/gen/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /dist/docs/gen/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/docs/gen/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /dist/docs/gen/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/docs/gen/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /dist/docs/gen/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janjaromirhorak/hradla/HEAD/dist/docs/gen/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/scss/lib/forkAwesome/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/scss/lib/forkAwesome/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /src/es6/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import App from './modules/App'; 4 | 5 | /** 6 | * When the document is ready, initialize the application 7 | */ 8 | $(() => { 9 | new App('#canvas', 10); 10 | }); 11 | -------------------------------------------------------------------------------- /src/scss/lib/normalize/_import-now.scss: -------------------------------------------------------------------------------- 1 | // Import Now 2 | // 3 | // If you import this module directly, it will immediately output all the CSS 4 | // needed to normalize default HTML elements across all browsers. 5 | // 6 | // ``` 7 | // @import "normalize/import-now"; 8 | // ``` 9 | 10 | @import '../normalize'; 11 | @include normalize(); 12 | -------------------------------------------------------------------------------- /dist/img/gui/help.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6 5 | }, 6 | "rules": { 7 | "no-console": 0 8 | }, 9 | "globals": { 10 | "lity": true 11 | }, 12 | "env": { 13 | "browser": true, 14 | "jquery": true, 15 | "commonjs": true, 16 | "es6": true 17 | }, 18 | "parser": "babel-eslint" 19 | } 20 | -------------------------------------------------------------------------------- /src/html/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |Logic network simulator
| action | controls |
|---|---|
| move the canvas | drag with middle mouse or ctrl + left mouse |
| zoom the canvas | ctrl + middle wheel or ctrl + + / ctrl + - |
| open the context menu (add elements) | right click |
| open the context menu for an element (add/remove elements) | right click on the element |
| rotate an element clockwise | middle click |
| rotate an element counterclockwise | ctrl + middle click |
| set element state (enable / disable input on an input box) | left click on the element |
| add a wire | left click on the first connector, than left click on the second connector |
| state | color |
|---|---|
| unknown | grey |
| 1 | green |
| 0 | red |
| oscillating | blue |
For technical documentation please visit the docs.
-------------------------------------------------------------------------------- /dist/img/svg/gate/xnor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/help/user.md: -------------------------------------------------------------------------------- 1 | # Hradla 2 | 3 | Logic network simulator 4 | 5 | ## Controls 6 | 7 | | action | controls | 8 | | ---------------------------------------------------------- | ------------------------------------------------------------------------------ | 9 | | move the canvas | drag with `middle mouse` or `ctrl + left mouse` | 10 | | zoom the canvas | `ctrl + middle wheel` or `ctrl + + / ctrl + -` | 11 | | open the context menu (add elements) | `right click` | 12 | | open the context menu for an element (add/remove elements) | `right click` on the element | 13 | | rotate an element clockwise | `middle click` | 14 | | rotate an element counterclockwise | `ctrl + middle click` | 15 | | set element state (enable / disable input on an input box) | `left click` on the element | 16 | | add a wire | `left click` on the first connector, than `left click` on the second connector | 17 | 18 | ## Connector states 19 | 20 | | state | color | 21 | | ----------- | ------------------------------- | 22 | | unknown | grey | 23 | | 1 | green | 24 | | 0 | red | 25 | | oscillating | blue | 26 | -------------------------------------------------------------------------------- /src/es6/modules/editorElements/InputConnector.js: -------------------------------------------------------------------------------- 1 | import Connector from './Connector'; 2 | import Logic from '../Logic'; 3 | 4 | /** @module editorElements.InputConnector */ 5 | 6 | /** 7 | * Connector that gets its state from a connected value and passes it through to the {@link Box} this connector belongs to. 8 | * @extends Connector 9 | */ 10 | export default class InputConnector extends Connector { 11 | /** 12 | * Call the constructor from the parent {@link Connector} class and set isInputConnector to true. 13 | * @param {App} appInstance link to the [App](./module-App.html) instance that this connector will belong to 14 | * @param {number} left horizontal position defined in grid units (SVG pixels divided by the grid size) 15 | * @param {number} top vertical position defined in grid units (SVG pixels divided by the grid size) 16 | */ 17 | constructor(appInstance, left, top) { 18 | super(appInstance, left, top); 19 | 20 | this.isInputConnector = true; 21 | } 22 | 23 | /** 24 | * Call the setState method of {@link Connector} and than refresh the state of the connected {@link Box} 25 | * @param {Logic.state} state new {@link Logic.state} of the connector 26 | */ 27 | setState(state) { 28 | super.setState(state); 29 | // console.log("SET STATE ON IC", this.id, ":", state) 30 | 31 | let box = this.appInstance.getBoxByConnectorId(this.svgObj.id); 32 | box.refreshState(); 33 | } 34 | 35 | /** 36 | * remove the wire (by calling the removeWireIdAndUpdate of {@link Connector}) 37 | * and update state of this connector by setting it to undefined using the setState method 38 | * @param {string} wireId ID of the {@link Wire} 39 | */ 40 | removeWireIdAndUpdate(wireId) { 41 | super.removeWireIdAndUpdate(wireId); 42 | this.setState(Logic.state.unknown); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/scss/lib/forkAwesome/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} #{$fa-font-family}; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/es6/modules/svgObjects/PolyLinePoint.js: -------------------------------------------------------------------------------- 1 | /** @module svgObjects.PolyLinePoint */ 2 | 3 | /** 4 | * one point of {@link PolyLinePoints}, used in the {@link PolyLine} object 5 | */ 6 | export default class PolyLinePoint { 7 | /** 8 | * @param {number} x horizontal coordinate of the PolyLine point 9 | * @param {number} y vertical coordinate of the PolyLine point 10 | */ 11 | constructor(x, y) { 12 | this.x = 0; 13 | this.y = 0; 14 | if (x !== undefined && y !== undefined) { 15 | this.x = x; 16 | this.y = y; 17 | } 18 | } 19 | 20 | /** 21 | * change the coordinates of this point 22 | * @param {number} x horizontal coordinate of the PolyLine point 23 | * @param {number} y vertical coordinate of the PolyLine point 24 | */ 25 | set(x, y) { 26 | this.x = x; 27 | this.y = y; 28 | } 29 | 30 | /** 31 | * create PolyLine from a comma separated string (e.g. from a string formatted like this: "x,y", for example "15,8") 32 | * @param {string} string string in the format "x,y" representing a point in the SVG PolyLine 33 | * @return {PolyLinePoint} newly created instance of {@link PolyLinePoint} 34 | */ 35 | static parseFromString(string) { 36 | let arr = string.split(','); 37 | return new PolyLinePoint(arr[0], arr[1]); 38 | } 39 | 40 | /** 41 | * return a string representation of this PolyLine point 42 | * @return {string} string in the format "x,y" 43 | */ 44 | get string() { 45 | return this.x + ',' + this.y; 46 | } 47 | 48 | /** 49 | * compare PolyLine points, return `true` if they are equal, else return `false` 50 | * @param {PolyLinePoint} a 51 | * @param {PolyLinePoint} b 52 | * @return {boolean} 53 | */ 54 | static equals(a, b) { 55 | return a.x === b.x && a.y === b.y; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /dist/img/svg/gate/nand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/es6/modules/editorElements/OutputBox.js: -------------------------------------------------------------------------------- 1 | import Logic from '../Logic'; 2 | 3 | import Box from './Box'; 4 | 5 | /** @module editorElements.OutputBox */ 6 | 7 | /** 8 | * OutputBox has only input connectors and is used to visualize the output states of the logic network. 9 | * @extends Box 10 | */ 11 | export default class OutputBox extends Box { 12 | /** 13 | * @param {App} appInstance instance of [App](./module-App.html) 14 | */ 15 | constructor(appInstance) { 16 | const gridHeight = 4; 17 | const gridWidth = 5; 18 | 19 | super(appInstance, 'output', 'other', gridWidth, gridHeight); 20 | 21 | this.addConnector(0, gridHeight / 2, true); 22 | 23 | this.generateBlockNodes(); 24 | } 25 | 26 | /** 27 | * set state of this output box to match the state of its input connector 28 | */ 29 | refreshState() { 30 | this.setState(this.connectors[0].state); 31 | } 32 | 33 | /** 34 | * Reflect the input connector state in the appearance of the element - set 35 | * the element image to represent the corresponding state 36 | * @param {Logic.state} state new state of this outputBox 37 | */ 38 | setState(state) { 39 | if (state === Logic.state.on) { 40 | if (this.appInstance.tutorial) { 41 | this.appInstance.tutorial.onOutputBoxTrue(); 42 | } 43 | } 44 | 45 | let stateMap = {}; 46 | stateMap[Logic.state.on] = 'on'; 47 | stateMap[Logic.state.off] = 'off'; 48 | stateMap[Logic.state.unknown] = ''; 49 | stateMap[Logic.state.oscillating] = 'osc'; 50 | 51 | this.changeImage(stateMap[state]); 52 | } 53 | 54 | generateBlockNodes() { 55 | // block the input connector node 56 | const specialNode = { 57 | x: 0, 58 | y: this.gridHeight / 2 59 | }; 60 | super.generateBlockNodes(0, 0, 0, 1, specialNode); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hradla", 3 | "version": "1.7.7", 4 | "repository": "github:janjaromirhorak/hradla", 5 | "browser": "dist/index.html", 6 | "license": "GPL-3.0", 7 | "author": { 8 | "name": "Jan Horák" 9 | }, 10 | "devDependencies": { 11 | "@babel/core": "^7.11.6", 12 | "@babel/preset-env": "^7.11.5", 13 | "babel-eslint": "^10.1.0", 14 | "babelify": "^10.0.0", 15 | "bl": "^4.0.3", 16 | "browserify": "^16.5.2", 17 | "del": "^6.0.0", 18 | "event-stream": "^4.0.1", 19 | "gulp": "^4.0.2", 20 | "gulp-autoprefixer": "^7.0.1", 21 | "gulp-changed": "^4.0.2", 22 | "gulp-eslint": "^6.0.0", 23 | "gulp-file": "^0.4.0", 24 | "gulp-filter": "^6.0.0", 25 | "gulp-gzip": "^1.4.2", 26 | "gulp-if": "^3.0.0", 27 | "gulp-imagemin": "^7.1.0", 28 | "gulp-insert": "^0.5.0", 29 | "gulp-jsdoc3": "^3.0.0", 30 | "gulp-json-editor": "^2.5.4", 31 | "gulp-markdown": "^5.1.0", 32 | "gulp-minifier": "^3.5.0", 33 | "gulp-rename": "^2.0.0", 34 | "gulp-replace": "^1.0.0", 35 | "gulp-sass": "^4.1.0", 36 | "gulp-sourcemaps": "^2.6.5", 37 | "gulp-tap": "^2.0.0", 38 | "gulp-tar": "^3.1.0", 39 | "gulp-template-html": "^0.2.2", 40 | "gulp-uglify": "^3.0.2", 41 | "gulp-watch": "^5.0.1", 42 | "gulp-zip": "^5.0.2", 43 | "jquery": "^3.5.1", 44 | "jsdoc": "^3.6.6", 45 | "json-stringify-pretty-compact": "^2.0.0", 46 | "libstl": "^0.1.22", 47 | "lity": "^2.4.1", 48 | "tui-jsdoc-template": "^1.2.2", 49 | "vinyl-buffer": "^1.0.1", 50 | "vinyl-source-stream": "^2.0.0", 51 | "yargs-parser": "^20.2.0" 52 | }, 53 | "scripts": { 54 | "gulp": "./node_modules/gulp/bin/gulp.js" 55 | }, 56 | "dependencies": {}, 57 | "config": { 58 | "title": "Hradla — Logic Network Simulator" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dist/docs/gen/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/scss/lib/normalize/_vertical-rhythm.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Vertical Rhythm 3 | // 4 | // This is the minimal amount of code needed to create vertical rhythm in our 5 | // CSS. If you are looking for a robust solution, look at the excellent Typey 6 | // library. @see https://github.com/jptaranto/typey 7 | 8 | @function normalize-rhythm($value, $relative-to: $base-font-size, $unit: $base-unit) { 9 | @if unit($value) != px { 10 | @error "The normalize vertical-rhythm module only supports px inputs. The typey library is better."; 11 | } 12 | @if $unit == rem { 13 | @return ($value / $base-font-size) * 1rem; 14 | } 15 | @else if $unit == em { 16 | @return ($value / $relative-to) * 1em; 17 | } 18 | @else { // $unit == px 19 | @return $value; 20 | } 21 | } 22 | 23 | @mixin normalize-font-size($value, $relative-to: $base-font-size) { 24 | @if unit($value) != 'px' { 25 | @error "normalize-font-size() only supports px inputs. The typey library is better."; 26 | } 27 | font-size: normalize-rhythm($value, $relative-to); 28 | } 29 | 30 | @mixin normalize-rhythm($property, $values, $relative-to: $base-font-size) { 31 | $value-list: $values; 32 | $sep: space; 33 | @if type-of($values) == 'list' { 34 | $sep: list-separator($values); 35 | } 36 | @else { 37 | $value-list: append((), $values); 38 | } 39 | 40 | $normalized-values: (); 41 | @each $value in $value-list { 42 | @if unitless($value) and $value != 0 { 43 | $value: $value * normalize-rhythm($base-line-height, $relative-to); 44 | } 45 | $normalized-values: append($normalized-values, $value, $sep); 46 | } 47 | #{$property}: $normalized-values; 48 | } 49 | 50 | @mixin normalize-margin($values, $relative-to: $base-font-size) { 51 | @include normalize-rhythm(margin, $values, $relative-to); 52 | } 53 | 54 | @mixin normalize-line-height($font-size, $min-line-padding: 2px) { 55 | $lines: ceil($font-size / $base-line-height); 56 | // If lines are cramped include some extra leading. 57 | @if ($lines * $base-line-height - $font-size) < ($min-line-padding * 2) { 58 | $lines: $lines + 1; 59 | } 60 | @include normalize-rhythm(line-height, $lines, $font-size); 61 | } 62 | -------------------------------------------------------------------------------- /src/es6/modules/svgObjects/MultiLineText.js: -------------------------------------------------------------------------------- 1 | import Tag from './Tag'; 2 | import Text from './Text'; 3 | 4 | /** @module svgObjects.MultiLineText */ 5 | 6 | /** 7 | * Multi line text element in SVG 8 | * 9 | * Multi line text is not natively supportend in SVG 1.1, 10 | * the workaround is to use the') 51 | .attr('xmlns', 'http://www.w3.org/1999/xhtml') 52 | .css('font-size', size) 53 | .append(text); 54 | 55 | $wrapper.append($paragraph); 56 | foreignObject.$el.append($wrapper); 57 | 58 | this.$el.append(foreignObject.$el).append(alternativeText.$el); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dist/img/gui/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/library/rs-nand-latch.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RS NAND latch", 3 | "boxes": [{ 4 | "name": "nand", 5 | "category": "gate", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [17, 5] 10 | }, { 11 | "name": "rotate", 12 | "args": ["0", 2, 5] 13 | }] 14 | }, 15 | "connections": [{ 16 | "index": 0, 17 | "wireId": 0 18 | }, { 19 | "index": 0, 20 | "wireId": 1 21 | }, { 22 | "index": 1, 23 | "wireId": 2 24 | }, { 25 | "index": 2, 26 | "wireId": 3 27 | }] 28 | }, { 29 | "name": "nand", 30 | "category": "gate", 31 | "transform": { 32 | "items": [{ 33 | "name": "translate", 34 | "args": [17, 14] 35 | }, { 36 | "name": "rotate", 37 | "args": ["0", 2, 5] 38 | }] 39 | }, 40 | "connections": [{ 41 | "index": 0, 42 | "wireId": 3 43 | }, { 44 | "index": 0, 45 | "wireId": 4 46 | }, { 47 | "index": 1, 48 | "wireId": 1 49 | }, { 50 | "index": 2, 51 | "wireId": 5 52 | }] 53 | }, { 54 | "name": "input", 55 | "category": "other", 56 | "transform": { 57 | "items": [{ 58 | "name": "translate", 59 | "args": [5, 4] 60 | }, { 61 | "name": "rotate", 62 | "args": ["0", 2, 3] 63 | }] 64 | }, 65 | "connections": [{ 66 | "index": 0, 67 | "wireId": 2 68 | }], 69 | "isOn": false 70 | }, { 71 | "name": "input", 72 | "category": "other", 73 | "transform": { 74 | "items": [{ 75 | "name": "translate", 76 | "args": [5, 15] 77 | }, { 78 | "name": "rotate", 79 | "args": ["0", 2, 3] 80 | }] 81 | }, 82 | "connections": [{ 83 | "index": 0, 84 | "wireId": 5 85 | }], 86 | "isOn": false 87 | }, { 88 | "name": "output", 89 | "category": "other", 90 | "transform": { 91 | "items": [{ 92 | "name": "translate", 93 | "args": [31, 14] 94 | }] 95 | }, 96 | "connections": [{ 97 | "index": 0, 98 | "wireId": 4 99 | }] 100 | }, { 101 | "name": "output", 102 | "category": "other", 103 | "transform": { 104 | "items": [{ 105 | "name": "translate", 106 | "args": [31, 5] 107 | }] 108 | }, 109 | "connections": [{ 110 | "index": 0, 111 | "wireId": 0 112 | }] 113 | }] 114 | } -------------------------------------------------------------------------------- /src/scss/docs.scss: -------------------------------------------------------------------------------- 1 | @import "lib/normalize"; 2 | @include normalize(); 3 | 4 | @import "colors"; 5 | 6 | html { 7 | background: white; 8 | color: $help_darkCasual; 9 | } 10 | 11 | body { 12 | font-family: 'Open Sans', sans-serif; 13 | font-weight: 400; 14 | padding: 2em; 15 | } 16 | 17 | b, 18 | strong { 19 | font-weight: 700; 20 | } 21 | 22 | h1, h2, h3, h4, h5, h6 { 23 | font-weight: 700; 24 | } 25 | 26 | a { 27 | color: $help_darkCasual; 28 | border-style: none none dashed none; 29 | border-color: $help_darkCasual; 30 | border-width: 1px; 31 | text-decoration: none; 32 | transition: all 200ms; 33 | } 34 | 35 | a:hover { 36 | color: $help_darkAccent; 37 | border-color: $help_darkAccent; 38 | } 39 | 40 | h1 { 41 | display: block; 42 | color: $help_darkCasual; 43 | border-style: none none solid none; 44 | border-color: $help_darkAccent; 45 | border-width: 2px; 46 | padding-bottom: 0.2em; 47 | } 48 | 49 | h2 { 50 | color: $help_darkAccent; 51 | border-style: none none solid none; 52 | border-color: $help_darkCasual; 53 | border-width: 1px; 54 | padding-bottom: 0.2em; 55 | } 56 | 57 | h3 { 58 | color: $help_darkAccent; 59 | font-style: italic; 60 | border-style: none none solid none; 61 | border-color: $help_lightAccent; 62 | border-width: 1px; 63 | padding-bottom: 0.2em; 64 | } 65 | 66 | h4 { 67 | color: $help_darkAccent; 68 | font-style: italic; 69 | } 70 | 71 | code { 72 | font-family: 'Inconsolata', monospace; 73 | display: inline-block; 74 | background: $help_codeBackground; 75 | color: $help_codeColor; 76 | padding: 2px 4px; 77 | border: 1px solid lighten($help_codeColor, 20%); 78 | border-radius: 2px; 79 | font-weight: 400; 80 | } 81 | 82 | table { 83 | text-align: left; 84 | td, th { 85 | padding: 0.1em 0.2em; 86 | } 87 | } 88 | 89 | .color { 90 | $colorSize: 0.8em; 91 | &:after { 92 | content: ''; 93 | width: $colorSize; 94 | height: $colorSize; 95 | border: thin solid black; 96 | border-radius: $colorSize; 97 | vertical-align: middle; 98 | 99 | display: none; 100 | } 101 | &.unknown, &.on, &.off, &.oscillating { 102 | &:after { 103 | display: inline-block; 104 | } 105 | } 106 | &.unknown:after { 107 | background: $unknownColor; 108 | } 109 | &.on:after { 110 | background: $onColor; 111 | } 112 | &.off:after { 113 | background: $offColor; 114 | } 115 | &.oscillating:after { 116 | background: $oscillatingColor; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /dist/docs/gen/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /src/es6/modules/editorElements/InputBox.js: -------------------------------------------------------------------------------- 1 | import Logic from '../Logic'; 2 | 3 | import Box from './Box'; 4 | 5 | /** @module editorElements.InputBox */ 6 | 7 | /** 8 | * InputBox has only output connectors and is used to set the input states for the logic network. 9 | * @extends Box 10 | */ 11 | export default class InputBox extends Box { 12 | /** 13 | * @param {App} appInstance instance of [App](./module-App.html) 14 | * @param {Boolean} [isOn=false] the initial state of the inputbox (`true` is *on*, `false` is *off*) 15 | */ 16 | constructor(appInstance, isOn = false) { 17 | const gridWidth = 7; 18 | const gridHeight = 4; 19 | 20 | super(appInstance, 'input', 'other', gridWidth, gridHeight); 21 | 22 | this.addConnector(gridWidth, gridHeight / 2, false); 23 | 24 | this.on = isOn; 25 | 26 | this.generateBlockNodes(); 27 | } 28 | 29 | /** 30 | * get data of this input box as a JSON-ready object 31 | * @return {Object} javascript object containing essential data for this input box 32 | */ 33 | get exportData() { 34 | let data = super.exportData; 35 | data.isOn = this.isOn; 36 | 37 | return data; 38 | } 39 | 40 | generateBlockNodes() { 41 | // block the input connector node 42 | const specialNode = { 43 | x: this.gridWidth, 44 | y: this.gridHeight / 2 45 | }; 46 | super.generateBlockNodes(0, 1, 1, 0, specialNode); 47 | } 48 | 49 | /** 50 | * start a new simulation from the output connector 51 | */ 52 | refreshState() { 53 | this.appInstance.startNewSimulation(this.connectors[0], this.connectors[0].state); 54 | } 55 | 56 | /** 57 | * set the state of the inputbox to the corresponding value 58 | * @param {Boolean} isOn set to *on* if `true`, set to *off* if `false` 59 | */ 60 | set on(isOn) { 61 | if (isOn) { 62 | // turn on 63 | this.changeImage('on'); 64 | this.connectors[0].setState(Logic.state.on); 65 | this.refreshState(); 66 | } else { 67 | // turn off 68 | this.changeImage(); 69 | this.connectors[0].setState(Logic.state.off); 70 | this.refreshState(); 71 | } 72 | 73 | this.isOn = isOn; 74 | } 75 | 76 | /** 77 | * get the state of the inputbox (`true` if *on*, `false` if *off*) 78 | * @return {Boolean} [description] 79 | */ 80 | get on() { 81 | return this.isOn; 82 | } 83 | 84 | /** 85 | * toggle the state of the inputbox 86 | */ 87 | onClick() { 88 | this.on = !this.on; 89 | 90 | if (this.appInstance.tutorial) { 91 | this.appInstance.tutorial.onChangeInputBoxState(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /dist/library/half-adder.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Half adder", 3 | "boxes": [{ 4 | "name": "and", 5 | "category": "gate", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [13, 11] 10 | }] 11 | }, 12 | "connections": [{ 13 | "index": 0, 14 | "wireId": 0 15 | }, { 16 | "index": 1, 17 | "wireId": 1 18 | }, { 19 | "index": 2, 20 | "wireId": 2 21 | }] 22 | }, { 23 | "name": "xor", 24 | "category": "gate", 25 | "transform": { 26 | "items": [{ 27 | "name": "translate", 28 | "args": [13, 4] 29 | }] 30 | }, 31 | "connections": [{ 32 | "index": 0, 33 | "wireId": 3 34 | }, { 35 | "index": 1, 36 | "wireId": 4 37 | }, { 38 | "index": 2, 39 | "wireId": 5 40 | }] 41 | }, { 42 | "name": "input", 43 | "category": "other", 44 | "transform": { 45 | "items": [{ 46 | "name": "translate", 47 | "args": [1, 3] 48 | }] 49 | }, 50 | "connections": [{ 51 | "index": 0, 52 | "wireId": 1 53 | }, { 54 | "index": 0, 55 | "wireId": 4 56 | }], 57 | "isOn": false 58 | }, { 59 | "name": "input", 60 | "category": "other", 61 | "transform": { 62 | "items": [{ 63 | "name": "translate", 64 | "args": [1, 12] 65 | }] 66 | }, 67 | "connections": [{ 68 | "index": 0, 69 | "wireId": 2 70 | }, { 71 | "index": 0, 72 | "wireId": 5 73 | }], 74 | "isOn": false 75 | }, { 76 | "name": "output", 77 | "category": "other", 78 | "transform": { 79 | "items": [{ 80 | "name": "translate", 81 | "args": [27, 4] 82 | }] 83 | }, 84 | "connections": [{ 85 | "index": 0, 86 | "wireId": 3 87 | }] 88 | }, { 89 | "name": "output", 90 | "category": "other", 91 | "transform": { 92 | "items": [{ 93 | "name": "translate", 94 | "args": [27, 11] 95 | }] 96 | }, 97 | "connections": [{ 98 | "index": 0, 99 | "wireId": 0 100 | }] 101 | }], 102 | "blackbox": { 103 | "inputs": 2, 104 | "outputs": 2, 105 | "table": [ 106 | [0, 0, 0, 0], 107 | [1, 0, 1, 0], 108 | [0, 1, 1, 0], 109 | [1, 1, 0, 1] 110 | ] 111 | } 112 | } -------------------------------------------------------------------------------- /src/img/gui/import.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 79 | -------------------------------------------------------------------------------- /src/es6/modules/other/helperFunctions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module HelperFunctions 3 | */ 4 | 5 | import stringify from 'json-stringify-pretty-compact'; // note: imported from a module 6 | 7 | /** 8 | * add a cross browser event listener on a mouse scroll 9 | * @param {string} query DOM query of the element that the listener will be added to 10 | * @param {Function} func Function that will be called when the event occurs. The function takes as a parameter an event object. 11 | */ 12 | export function addMouseScrollEventListener(query, func) { 13 | let MouseWheelHandler = event => { 14 | // redeclare for old IE support 15 | var event = window.event || event; // eslint-disable-line no-redeclare 16 | 17 | event.delta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail)); 18 | 19 | func(event); 20 | 21 | return false; 22 | }; 23 | 24 | let svgelement; 25 | 26 | // if the query is a simple DOM id selector, we can use getElementById which has better backwards compatibility 27 | if (query.match(/^#\w+$/)) { 28 | svgelement = document.getElementById(query.substr(1)); 29 | } else { 30 | svgelement = document.querySelector(query); 31 | } 32 | 33 | if (svgelement.addEventListener) { 34 | // IE9, Chrome, Safari, Opera 35 | svgelement.addEventListener('mousewheel', MouseWheelHandler, false); 36 | // Firefox 37 | svgelement.addEventListener('DOMMouseScroll', MouseWheelHandler, false); 38 | } else { 39 | // IE 6/7/8 40 | svgelement.attachEvent('onmousewheel', MouseWheelHandler); 41 | } 42 | svgelement.addEventListener( 43 | 'mousewheel', 44 | function(e) { 45 | console.log('event', e); 46 | }, 47 | false 48 | ); 49 | } 50 | 51 | /** 52 | * convert a data object to JSON string or to a data URI containing a JSON string 53 | * @param {Object} data object that will be serialized into a JSON string 54 | * @param {Boolean} [pretty=false] if `true`, the code will be proprerly indented, else a more compact syntax will be used 55 | * @param {Boolean} [dataUri=false] return dataUri containing the JSON string instead of the pure JSON string 56 | * @return {string} 57 | */ 58 | export function getJSONString(data, pretty = false, dataUri = false) { 59 | if (dataUri) { 60 | return ( 61 | 'data:application/json;charset=utf-8,' + encodeURIComponent(getJSONString(data, pretty)) 62 | ); 63 | } else { 64 | if (pretty) return stringify(data, { maxLength: 50 }); 65 | 66 | return JSON.stringify(data); 67 | } 68 | } 69 | 70 | /** 71 | * returns the Manhattan distance between the points _a_ and _b_ 72 | * @param {Object} a object containing numeric attributes `x` and `y` 73 | * @param {Object} b object containing numeric attributes `x` and `y` 74 | * @return {number} 75 | */ 76 | export function manhattanDistance(a, b) { 77 | return Math.abs(a.x - b.x) + Math.abs(a.y - b.y); 78 | } 79 | -------------------------------------------------------------------------------- /src/img/gui/help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 95 | -------------------------------------------------------------------------------- /src/es6/routeWorker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** @module routeWorker */ 4 | 5 | import findPath from './modules/findPath'; 6 | 7 | /** 8 | * callback when a message is sent to the web worker 9 | * 10 | * @param {Object} event web worker event object (the `data` item of the event object is expected to contain 11 | * these items: `wires` (array), `nonRoutableNodes` (iterable) and `inconvenientNodes` (iterable)) 12 | */ 13 | onmessage = event => { 14 | const { wires, nonRoutableNodes, inconvenientNodes } = event.data; 15 | 16 | const paths = findPaths(wires, nonRoutableNodes, inconvenientNodes); 17 | 18 | postMessage({ paths }); 19 | close(); 20 | }; 21 | 22 | /** 23 | * find paths for all the specified wires 24 | * @param {Array} wires array of objects with attributes `from` and `to`, both of them which are objects 25 | * with values `x` and `y` containing coordinates of the wire endpoints 26 | * @param {Iterable} nonRoutableNodes Set or array of non routable nodes 27 | * @param {Iterable} inconvenientNodes Set or array of inconvenient nodes 28 | * @return {Array} array of paths, each item is an array of points of the path 29 | * the returned array contains paths for the wires with corresponding indexes from the `wires` parameter 30 | */ 31 | function findPaths(wires, nonRoutableNodes, inconvenientNodes) { 32 | let paths = []; 33 | 34 | for (const [from, to] of wires) { 35 | const path = findPath(from, to, nonRoutableNodes, inconvenientNodes); 36 | 37 | if (!path) { 38 | console.log('path not found'); 39 | console.log(from, to); 40 | } else { 41 | console.log('path found'); 42 | } 43 | 44 | paths.push(path); 45 | 46 | // add new inconvenient nodes created by this new path 47 | let prevPoint; 48 | for (const point of path) { 49 | if (prevPoint) { 50 | if (point.x === prevPoint.x) { 51 | // horizontal section of the path 52 | for ( 53 | let y = Math.min(point.y, prevPoint.y); 54 | y <= Math.max(point.y, prevPoint.y); 55 | ++y 56 | ) { 57 | inconvenientNodes.add({ 58 | x: point.x, 59 | y: y 60 | }); 61 | } 62 | } else if (point.y === prevPoint.y) { 63 | // vertical section of the path 64 | for ( 65 | let x = Math.min(point.x, prevPoint.x); 66 | x <= Math.max(point.x, prevPoint.x); 67 | ++x 68 | ) { 69 | inconvenientNodes.add({ 70 | x: x, 71 | y: point.y 72 | }); 73 | } 74 | } 75 | } 76 | 77 | prevPoint = point; 78 | } 79 | } 80 | 81 | return paths; 82 | } 83 | -------------------------------------------------------------------------------- /src/es6/modules/svgObjects/Tag.js: -------------------------------------------------------------------------------- 1 | import Id from '../other/id'; 2 | 3 | /** @module svgObjects.Tag */ 4 | 5 | /** 6 | * Parent class for all svgObjects 7 | */ 8 | export default class Tag { 9 | /** 10 | * @param {string} tagName SVG tag identifier (`rect`, `image`, `PolyLine`) 11 | */ 12 | constructor(tagName) { 13 | /** 14 | * SVG tag identifier (`rect`, `image`, `PolyLine`) 15 | * @type {string} 16 | */ 17 | this.tagName = tagName; 18 | 19 | /** 20 | * jQuery element for this tag 21 | * @type {jQuery.element} 22 | */ 23 | this.$el = $('<' + this.tagName + '>'); 24 | 25 | /** 26 | * unique ID of this SVG object 27 | * @type {string} 28 | */ 29 | this.id = new Id().unique; 30 | } 31 | 32 | /** 33 | * add a class to this element 34 | * @param {string} name class name to be added 35 | */ 36 | addClass(name) { 37 | this.$el.addClass(name); 38 | } 39 | 40 | /** 41 | * remove class names from this element 42 | * @param {string} classes class names to be removed 43 | */ 44 | removeClasses(...classes) { 45 | for (let item of classes) { 46 | this.$el.removeClass(item); 47 | } 48 | } 49 | 50 | /** 51 | * set attributes of this element 52 | * @param {Object} assoc javascript object that will be mapped into attributes (`{key: value}` -> `key="value"`) 53 | */ 54 | addAttr(assoc) { 55 | this.checkIfElementExistsInDOM(); 56 | 57 | // add attributes to the element 58 | this.$el.attr(assoc); 59 | } 60 | 61 | /** 62 | * get attribute value by name 63 | * @param {string} name name of the attribute 64 | * @return {string} value of the attribute 65 | */ 66 | getAttr(name) { 67 | this.checkIfElementExistsInDOM(); 68 | 69 | return this.$el.attr(name); 70 | } 71 | 72 | /** 73 | * remove attribute by value 74 | * @param {string} name name of the attribute to be removed 75 | */ 76 | removeAttr(name) { 77 | this.checkIfElementExistsInDOM(); 78 | 79 | this.$el.removeAttr(name); 80 | } 81 | 82 | /** 83 | * set id of this SVG object 84 | * @param {string} id new id for this object 85 | */ 86 | set id(id) { 87 | this.addAttr({ id: id }); 88 | } 89 | 90 | /** 91 | * get id of this SVG object 92 | * @return {string} 93 | */ 94 | get id() { 95 | return this.getAttr('id'); 96 | } 97 | 98 | /** 99 | * get jQuery element for this SVG object 100 | * @return {jQuery.element} 101 | */ 102 | get() { 103 | this.checkIfElementExistsInDOM(); 104 | return this.$el; 105 | } 106 | 107 | /** 108 | * check if the element exists in dom, if so, refetch it from DOM using jQuery 109 | */ 110 | checkIfElementExistsInDOM() { 111 | let $jqElement = $('#' + this.$el.attr('id')); 112 | if ($jqElement.length) { 113 | this.$el = $jqElement; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /dist/css/lib/lity.min.css: -------------------------------------------------------------------------------- 1 | /*! Lity - v2.4.1 - 2020-04-26 2 | * http://sorgalla.com/lity/ 3 | * Copyright (c) 2015-2020 Jan Sorgalla; Licensed MIT */.lity{z-index:9990;position:fixed;top:0;right:0;bottom:0;left:0;white-space:nowrap;background:#0b0b0b;background:rgba(0,0,0,0.9);outline:none !important;opacity:0;-webkit-transition:opacity .3s ease;-o-transition:opacity .3s ease;transition:opacity .3s ease}.lity.lity-opened{opacity:1}.lity.lity-closed{opacity:0}.lity *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.lity-wrap{z-index:9990;position:fixed;top:0;right:0;bottom:0;left:0;text-align:center;outline:none !important}.lity-wrap:before{content:'';display:inline-block;height:100%;vertical-align:middle;margin-right:-0.25em}.lity-loader{z-index:9991;color:#fff;position:absolute;top:50%;margin-top:-0.8em;width:100%;text-align:center;font-size:14px;font-family:Arial,Helvetica,sans-serif;opacity:0;-webkit-transition:opacity .3s ease;-o-transition:opacity .3s ease;transition:opacity .3s ease}.lity-loading .lity-loader{opacity:1}.lity-container{z-index:9992;position:relative;text-align:left;vertical-align:middle;display:inline-block;white-space:normal;max-width:100%;max-height:100%;outline:none !important}.lity-content{z-index:9993;width:100%;-webkit-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1);-webkit-transition:-webkit-transform .3s ease;transition:-webkit-transform .3s ease;-o-transition:-o-transform .3s ease;transition:transform .3s ease;transition:transform .3s ease, -webkit-transform .3s ease, -o-transform .3s ease}.lity-loading .lity-content,.lity-closed .lity-content{-webkit-transform:scale(.8);-ms-transform:scale(.8);-o-transform:scale(.8);transform:scale(.8)}.lity-content:after{content:'';position:absolute;left:0;top:0;bottom:0;display:block;right:0;width:auto;height:auto;z-index:-1;-webkit-box-shadow:0 0 8px rgba(0,0,0,0.6);box-shadow:0 0 8px rgba(0,0,0,0.6)}.lity-close{z-index:9994;width:35px;height:35px;position:fixed;right:0;top:0;-webkit-appearance:none;cursor:pointer;text-decoration:none;text-align:center;padding:0;color:#fff;font-style:normal;font-size:35px;font-family:Arial,Baskerville,monospace;line-height:35px;text-shadow:0 1px 2px rgba(0,0,0,0.6);border:0;background:none;outline:none;-webkit-box-shadow:none;box-shadow:none}.lity-close::-moz-focus-inner{border:0;padding:0}.lity-close:hover,.lity-close:focus,.lity-close:active,.lity-close:visited{text-decoration:none;text-align:center;padding:0;color:#fff;font-style:normal;font-size:35px;font-family:Arial,Baskerville,monospace;line-height:35px;text-shadow:0 1px 2px rgba(0,0,0,0.6);border:0;background:none;outline:none;-webkit-box-shadow:none;box-shadow:none}.lity-close:active{top:1px}.lity-image img{max-width:100%;display:block;line-height:0;border:0}.lity-iframe .lity-container,.lity-youtube .lity-container,.lity-vimeo .lity-container,.lity-facebookvideo .lity-container,.lity-googlemaps .lity-container{width:100%;max-width:964px}.lity-iframe-container{width:100%;height:0;padding-top:56.25%;overflow:auto;pointer-events:auto;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-overflow-scrolling:touch}.lity-iframe-container iframe{position:absolute;display:block;top:0;left:0;width:100%;height:100%;-webkit-box-shadow:0 0 8px rgba(0,0,0,0.6);box-shadow:0 0 8px rgba(0,0,0,0.6);background:#000}.lity-hide{display:none} -------------------------------------------------------------------------------- /src/img/gui/tutorial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 71 | -------------------------------------------------------------------------------- /src/scss/_tutorial.scss: -------------------------------------------------------------------------------- 1 | .tutorialTextButton { 2 | text-decoration: none; 3 | text-transform: uppercase; 4 | 5 | color: $t_darkCasual; 6 | background: $t_lightCasual; 7 | 8 | padding: 0.3em 0.5em; 9 | border-radius: 3px; 10 | margin: 0.3em; 11 | display: inline-block; 12 | 13 | transition: background 200ms; 14 | 15 | &:hover { 16 | background: $t_lightAccent; 17 | } 18 | 19 | &:active, &:focus { 20 | background: $t_darkAccent; 21 | } 22 | } 23 | 24 | #tutorial { 25 | position: absolute; 26 | left: 1em; 27 | top: 1em; 28 | 29 | padding: 0.6em 1em 1em 1em; 30 | 31 | min-width: 20em; 32 | max-width: 35em; 33 | 34 | color: $t_darkCasual; 35 | background: white; 36 | 37 | .topButtons { 38 | display: flex; 39 | margin-bottom: 0.5em; 40 | 41 | .left { 42 | text-align: left; 43 | } 44 | 45 | .right { 46 | text-align: right; 47 | } 48 | 49 | .left, .right { 50 | flex-grow: 1; 51 | 52 | .button { 53 | display: inline-block; 54 | 55 | &:after { 56 | display: inline-block; 57 | font-family: $fa-font-family; 58 | color: $ui_darkCasual; 59 | 60 | padding: 0.2em 0.4em; 61 | background: $ui_lightCasual; 62 | border-radius: 3px; 63 | 64 | margin-right: 0.5em; 65 | 66 | cursor: hand; 67 | cursor: pointer; 68 | transition: opacity 200ms; 69 | } 70 | 71 | &:last-of-type:after { 72 | margin-right: 0; 73 | } 74 | 75 | &:hover:after { 76 | opacity: 0.5; 77 | } 78 | 79 | &.close { 80 | &:after { 81 | content: $fa-var-close; 82 | } 83 | } 84 | 85 | &.prev { 86 | &:after { 87 | content: $fa-var-chevron-left; 88 | } 89 | } 90 | 91 | &.next { 92 | &:after { 93 | content: $fa-var-chevron-right; 94 | } 95 | } 96 | 97 | &.disabled { 98 | &:after { 99 | opacity: 0.5; 100 | cursor: default; 101 | } 102 | 103 | &:hover:after { 104 | opacity: 0.5; 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | .content { 112 | margin: 0; 113 | padding: 0; 114 | line-height: 1.5em; 115 | 116 | p { 117 | margin: 0 0 1em 0; 118 | 119 | &:last-of-type { 120 | margin-bottom: 0; 121 | } 122 | } 123 | } 124 | 125 | ol.choices { 126 | list-style: none; 127 | margin: 0; 128 | padding: 0; 129 | text-align: center; 130 | 131 | li a { 132 | @extend .tutorialTextButton; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /dist/library/half-subtractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Half subtractor", 3 | "boxes": [{ 4 | "name": "input", 5 | "category": "other", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [7, 4] 10 | }] 11 | }, 12 | "connections": [{ 13 | "index": 0, 14 | "wireId": 0 15 | }, { 16 | "index": 0, 17 | "wireId": 1 18 | }], 19 | "isOn": false 20 | }, { 21 | "name": "input", 22 | "category": "other", 23 | "transform": { 24 | "items": [{ 25 | "name": "translate", 26 | "args": [7, 15] 27 | }] 28 | }, 29 | "connections": [{ 30 | "index": 0, 31 | "wireId": 2 32 | }, { 33 | "index": 0, 34 | "wireId": 3 35 | }], 36 | "isOn": false 37 | }, { 38 | "name": "xor", 39 | "category": "gate", 40 | "transform": { 41 | "items": [{ 42 | "name": "translate", 43 | "args": [28, 5] 44 | }] 45 | }, 46 | "connections": [{ 47 | "index": 0, 48 | "wireId": 4 49 | }, { 50 | "index": 1, 51 | "wireId": 0 52 | }, { 53 | "index": 2, 54 | "wireId": 2 55 | }] 56 | }, { 57 | "name": "not", 58 | "category": "gate", 59 | "transform": { 60 | "items": [{ 61 | "name": "translate", 62 | "args": [19, 10] 63 | }] 64 | }, 65 | "connections": [{ 66 | "index": 0, 67 | "wireId": 5 68 | }, { 69 | "index": 1, 70 | "wireId": 1 71 | }] 72 | }, { 73 | "name": "output", 74 | "category": "other", 75 | "transform": { 76 | "items": [{ 77 | "name": "translate", 78 | "args": [41, 5] 79 | }] 80 | }, 81 | "connections": [{ 82 | "index": 0, 83 | "wireId": 4 84 | }] 85 | }, { 86 | "name": "output", 87 | "category": "other", 88 | "transform": { 89 | "items": [{ 90 | "name": "translate", 91 | "args": [41, 14] 92 | }] 93 | }, 94 | "connections": [{ 95 | "index": 0, 96 | "wireId": 6 97 | }] 98 | }, { 99 | "name": "and", 100 | "category": "gate", 101 | "transform": { 102 | "items": [{ 103 | "name": "translate", 104 | "args": [28, 14] 105 | }] 106 | }, 107 | "connections": [{ 108 | "index": 0, 109 | "wireId": 6 110 | }, { 111 | "index": 1, 112 | "wireId": 5 113 | }, { 114 | "index": 2, 115 | "wireId": 3 116 | }] 117 | }], 118 | "blackbox": { 119 | "name": "HALF SUBTR", 120 | "inputs": 2, 121 | "outputs": 2, 122 | "table": [ 123 | [0, 0, 0, 0], 124 | [0, 1, 1, 1], 125 | [1, 0, 1, 0], 126 | [1, 1, 0, 0] 127 | ] 128 | } 129 | } -------------------------------------------------------------------------------- /dist/css/docs.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize-scss | MIT/GPLv2 License | bit.ly/normalize-scss */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}main{display:block}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}input{overflow:visible}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;display:table;max-width:100%;padding:0;color:inherit;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}details{display:block}summary{display:list-item}menu{display:block}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{background:#fff;color:#2b2d42}body{font-family:'Open Sans',sans-serif;font-weight:400;padding:2em}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:700}a{color:#2b2d42;border-style:none none dashed none;border-color:#2b2d42;border-width:1px;text-decoration:none;-webkit-transition:all .2s;transition:all .2s}a:hover{color:#ed254e;border-color:#ed254e}h1{display:block;color:#2b2d42;border-style:none none solid none;border-color:#ed254e;border-width:2px;padding-bottom:.2em}h2{color:#ed254e;border-style:none none solid none;border-color:#2b2d42;border-width:1px;padding-bottom:.2em}h3{color:#ed254e;font-style:italic;border-style:none none solid none;border-color:#f9dc5c;border-width:1px;padding-bottom:.2em}h4{color:#ed254e;font-style:italic}code{font-family:Inconsolata,monospace;display:inline-block;background:#edf2f4;color:#2b2d42;padding:2px 4px;border:1px solid #535780;border-radius:2px;font-weight:400}table{text-align:left}table td,table th{padding:.1em .2em}.color:after{content:'';width:.8em;height:.8em;border:thin solid #000;border-radius:.8em;vertical-align:middle;display:none}.color.off:after,.color.on:after,.color.oscillating:after,.color.unknown:after{display:inline-block}.color.unknown:after{background:#8b8b8b}.color.on:after{background:#0f0}.color.off:after{background:red}.color.oscillating:after{background:#00f} -------------------------------------------------------------------------------- /src/es6/modules/editorElements/Gate.js: -------------------------------------------------------------------------------- 1 | import Logic from '../Logic'; 2 | 3 | import Box from './Box'; 4 | 5 | /** @module editorElements.Gate */ 6 | 7 | /** 8 | * Gate is a box that processes the states of its input connectors and returns the result in its output connectors. 9 | * @extends Box 10 | */ 11 | export default class Gate extends Box { 12 | /** 13 | * @param {App} appInstance instance of [App](./module-App.html) 14 | * @param {string} name name of the gate (and, not, xor...) 15 | */ 16 | constructor(appInstance, name) { 17 | const width = 9; 18 | const height = 4; 19 | 20 | super(appInstance, name, 'gate', width, height); 21 | 22 | // ADD CONNECTORS 23 | 24 | let specialNodes = []; 25 | 26 | // output 27 | this.addConnector(width, height / 2, false); 28 | 29 | // block the output connector 30 | specialNodes.push({ 31 | x: width, 32 | y: height / 2 33 | }); 34 | 35 | if (this.name === 'not' || this.name === 'repeater') { 36 | // input 37 | this.addConnector(0, height / 2, true); 38 | // block the input connector 39 | specialNodes.push({ 40 | x: 0, 41 | y: height / 2 42 | }); 43 | } else { 44 | // input 45 | this.addConnector(0, height / 4, true); 46 | this.addConnector(0, height / (4 / 3), true); 47 | 48 | // block the input connectors 49 | specialNodes.push({ 50 | x: 0, 51 | y: height / 4 52 | }); 53 | specialNodes.push({ 54 | x: 0, 55 | y: height / (4 / 3) 56 | }); 57 | 58 | // add one blocked node between the inputs (for better looking wiring) 59 | specialNodes.push({ 60 | x: 0, 61 | y: height / 2 62 | }); 63 | } 64 | 65 | this.generateBlockNodes(...specialNodes); 66 | 67 | this.refreshState(); 68 | } 69 | 70 | /** 71 | * array of valid gate names 72 | * @type {Set} 73 | */ 74 | static get validGates() { 75 | // return new Set(["not", "and", "or", "nand", "nor", "xor", "xnor", "repeater"]); 76 | return new Set(['not', 'and', 'or', 'nand', 'nor', 'xor', 'xnor']); 77 | } 78 | 79 | generateBlockNodes(...specialNodes) { 80 | if (specialNodes !== undefined) { 81 | super.generateBlockNodes(0, 1, 0, 1, ...specialNodes); 82 | } else { 83 | super.generateBlockNodes(0, 1, 0, 1); 84 | } 85 | } 86 | 87 | /** 88 | * proccess the input connector states and reflect them in the output connector states according 89 | * to the logic corresponding to this gate's name 90 | */ 91 | refreshState() { 92 | // map gate names to their logic functions 93 | const stateMap = { 94 | and: () => Logic.and(this.connectors[1].state, this.connectors[2].state), 95 | nand: () => Logic.nand(this.connectors[1].state, this.connectors[2].state), 96 | nor: () => Logic.nor(this.connectors[1].state, this.connectors[2].state), 97 | not: () => Logic.not(this.connectors[1].state), 98 | or: () => Logic.or(this.connectors[1].state, this.connectors[2].state), 99 | xnor: () => Logic.xnor(this.connectors[1].state, this.connectors[2].state), 100 | xor: () => Logic.xor(this.connectors[1].state, this.connectors[2].state), 101 | repeater: () => this.connectors[1].state 102 | }; 103 | 104 | let state = Logic.state.unknown; 105 | 106 | if (stateMap[this.name]) { 107 | state = stateMap[this.name](); 108 | } 109 | 110 | // notify the simulator about this change 111 | this.appInstance.simulation.notifyChange(this.connectors[0].id, state); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/img/gui/library.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 79 | -------------------------------------------------------------------------------- /library/rs-nand-latch.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RS NAND latch", 3 | "boxes": [ 4 | { 5 | "name": "nand", 6 | "category": "gate", 7 | "transform": { 8 | "items": [ 9 | { 10 | "name": "translate", 11 | "args": [ 12 | 17, 13 | 5 14 | ] 15 | }, 16 | { 17 | "name": "rotate", 18 | "args": [ 19 | "0", 20 | 2, 21 | 5 22 | ] 23 | } 24 | ] 25 | }, 26 | "connections": [ 27 | { 28 | "index": 0, 29 | "wireId": 0 30 | }, 31 | { 32 | "index": 0, 33 | "wireId": 1 34 | }, 35 | { 36 | "index": 1, 37 | "wireId": 2 38 | }, 39 | { 40 | "index": 2, 41 | "wireId": 3 42 | } 43 | ] 44 | }, 45 | { 46 | "name": "nand", 47 | "category": "gate", 48 | "transform": { 49 | "items": [ 50 | { 51 | "name": "translate", 52 | "args": [ 53 | 17, 54 | 14 55 | ] 56 | }, 57 | { 58 | "name": "rotate", 59 | "args": [ 60 | "0", 61 | 2, 62 | 5 63 | ] 64 | } 65 | ] 66 | }, 67 | "connections": [ 68 | { 69 | "index": 0, 70 | "wireId": 3 71 | }, 72 | { 73 | "index": 0, 74 | "wireId": 4 75 | }, 76 | { 77 | "index": 1, 78 | "wireId": 1 79 | }, 80 | { 81 | "index": 2, 82 | "wireId": 5 83 | } 84 | ] 85 | }, 86 | { 87 | "name": "input", 88 | "category": "other", 89 | "transform": { 90 | "items": [ 91 | { 92 | "name": "translate", 93 | "args": [ 94 | 5, 95 | 4 96 | ] 97 | }, 98 | { 99 | "name": "rotate", 100 | "args": [ 101 | "0", 102 | 2, 103 | 3 104 | ] 105 | } 106 | ] 107 | }, 108 | "connections": [ 109 | { 110 | "index": 0, 111 | "wireId": 2 112 | } 113 | ], 114 | "isOn": false 115 | }, 116 | { 117 | "name": "input", 118 | "category": "other", 119 | "transform": { 120 | "items": [ 121 | { 122 | "name": "translate", 123 | "args": [ 124 | 5, 125 | 15 126 | ] 127 | }, 128 | { 129 | "name": "rotate", 130 | "args": [ 131 | "0", 132 | 2, 133 | 3 134 | ] 135 | } 136 | ] 137 | }, 138 | "connections": [ 139 | { 140 | "index": 0, 141 | "wireId": 5 142 | } 143 | ], 144 | "isOn": false 145 | }, 146 | { 147 | "name": "output", 148 | "category": "other", 149 | "transform": { 150 | "items": [ 151 | { 152 | "name": "translate", 153 | "args": [ 154 | 31, 155 | 14 156 | ] 157 | } 158 | ] 159 | }, 160 | "connections": [ 161 | { 162 | "index": 0, 163 | "wireId": 4 164 | } 165 | ] 166 | }, 167 | { 168 | "name": "output", 169 | "category": "other", 170 | "transform": { 171 | "items": [ 172 | { 173 | "name": "translate", 174 | "args": [ 175 | 31, 176 | 5 177 | ] 178 | } 179 | ] 180 | }, 181 | "connections": [ 182 | { 183 | "index": 0, 184 | "wireId": 0 185 | } 186 | ] 187 | } 188 | ] 189 | } 190 | -------------------------------------------------------------------------------- /library/half-adder.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Half adder", 3 | "boxes": [{ 4 | "name": "and", 5 | "category": "gate", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [ 10 | 13, 11 | 11 12 | ] 13 | }] 14 | }, 15 | "connections": [{ 16 | "index": 0, 17 | "wireId": 0 18 | }, 19 | { 20 | "index": 1, 21 | "wireId": 1 22 | }, 23 | { 24 | "index": 2, 25 | "wireId": 2 26 | } 27 | ] 28 | }, 29 | { 30 | "name": "xor", 31 | "category": "gate", 32 | "transform": { 33 | "items": [{ 34 | "name": "translate", 35 | "args": [ 36 | 13, 37 | 4 38 | ] 39 | }] 40 | }, 41 | "connections": [{ 42 | "index": 0, 43 | "wireId": 3 44 | }, 45 | { 46 | "index": 1, 47 | "wireId": 4 48 | }, 49 | { 50 | "index": 2, 51 | "wireId": 5 52 | } 53 | ] 54 | }, 55 | { 56 | "name": "input", 57 | "category": "other", 58 | "transform": { 59 | "items": [{ 60 | "name": "translate", 61 | "args": [ 62 | 1, 63 | 3 64 | ] 65 | }] 66 | }, 67 | "connections": [{ 68 | "index": 0, 69 | "wireId": 1 70 | }, 71 | { 72 | "index": 0, 73 | "wireId": 4 74 | } 75 | ], 76 | "isOn": false 77 | }, 78 | { 79 | "name": "input", 80 | "category": "other", 81 | "transform": { 82 | "items": [{ 83 | "name": "translate", 84 | "args": [ 85 | 1, 86 | 12 87 | ] 88 | }] 89 | }, 90 | "connections": [{ 91 | "index": 0, 92 | "wireId": 2 93 | }, 94 | { 95 | "index": 0, 96 | "wireId": 5 97 | } 98 | ], 99 | "isOn": false 100 | }, 101 | { 102 | "name": "output", 103 | "category": "other", 104 | "transform": { 105 | "items": [{ 106 | "name": "translate", 107 | "args": [ 108 | 27, 109 | 4 110 | ] 111 | }] 112 | }, 113 | "connections": [{ 114 | "index": 0, 115 | "wireId": 3 116 | }] 117 | }, 118 | { 119 | "name": "output", 120 | "category": "other", 121 | "transform": { 122 | "items": [{ 123 | "name": "translate", 124 | "args": [ 125 | 27, 126 | 11 127 | ] 128 | }] 129 | }, 130 | "connections": [{ 131 | "index": 0, 132 | "wireId": 0 133 | }] 134 | } 135 | ], 136 | "blackbox": { 137 | "inputs": 2, 138 | "outputs": 2, 139 | "table": [ 140 | [0, 0, 0, 0], 141 | [1, 0, 1, 0], 142 | [0, 1, 1, 0], 143 | [1, 1, 0, 1] 144 | ] 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /dist/library/xor-and-or-adder.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XOR, AND, OR adder", 3 | "boxes": [{ 4 | "name": "and", 5 | "category": "gate", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [42, 22] 10 | }] 11 | }, 12 | "connections": [{ 13 | "index": 0, 14 | "wireId": 0 15 | }, { 16 | "index": 1, 17 | "wireId": 1 18 | }, { 19 | "index": 2, 20 | "wireId": 2 21 | }] 22 | }, { 23 | "name": "xor", 24 | "category": "gate", 25 | "transform": { 26 | "items": [{ 27 | "name": "translate", 28 | "args": [42, 15] 29 | }] 30 | }, 31 | "connections": [{ 32 | "index": 0, 33 | "wireId": 3 34 | }, { 35 | "index": 1, 36 | "wireId": 4 37 | }, { 38 | "index": 2, 39 | "wireId": 5 40 | }] 41 | }, { 42 | "name": "output", 43 | "category": "other", 44 | "transform": { 45 | "items": [{ 46 | "name": "translate", 47 | "args": [63, 15] 48 | }] 49 | }, 50 | "connections": [{ 51 | "index": 0, 52 | "wireId": 3 53 | }] 54 | }, { 55 | "name": "and", 56 | "category": "gate", 57 | "transform": { 58 | "items": [{ 59 | "name": "translate", 60 | "args": [27, 30] 61 | }] 62 | }, 63 | "connections": [{ 64 | "index": 0, 65 | "wireId": 6 66 | }, { 67 | "index": 1, 68 | "wireId": 7 69 | }, { 70 | "index": 2, 71 | "wireId": 8 72 | }] 73 | }, { 74 | "name": "xor", 75 | "category": "gate", 76 | "transform": { 77 | "items": [{ 78 | "name": "translate", 79 | "args": [27, 23] 80 | }] 81 | }, 82 | "connections": [{ 83 | "index": 0, 84 | "wireId": 2 85 | }, { 86 | "index": 0, 87 | "wireId": 5 88 | }, { 89 | "index": 1, 90 | "wireId": 9 91 | }, { 92 | "index": 2, 93 | "wireId": 10 94 | }] 95 | }, { 96 | "name": "input", 97 | "category": "other", 98 | "transform": { 99 | "items": [{ 100 | "name": "translate", 101 | "args": [16, 14] 102 | }] 103 | }, 104 | "connections": [{ 105 | "index": 0, 106 | "wireId": 4 107 | }, { 108 | "index": 0, 109 | "wireId": 1 110 | }], 111 | "isOn": false 112 | }, { 113 | "name": "input", 114 | "category": "other", 115 | "transform": { 116 | "items": [{ 117 | "name": "translate", 118 | "args": [16, 22] 119 | }] 120 | }, 121 | "connections": [{ 122 | "index": 0, 123 | "wireId": 7 124 | }, { 125 | "index": 0, 126 | "wireId": 9 127 | }], 128 | "isOn": false 129 | }, { 130 | "name": "input", 131 | "category": "other", 132 | "transform": { 133 | "items": [{ 134 | "name": "translate", 135 | "args": [16, 31] 136 | }] 137 | }, 138 | "connections": [{ 139 | "index": 0, 140 | "wireId": 8 141 | }, { 142 | "index": 0, 143 | "wireId": 10 144 | }], 145 | "isOn": false 146 | }, { 147 | "name": "or", 148 | "category": "gate", 149 | "transform": { 150 | "items": [{ 151 | "name": "translate", 152 | "args": [51, 29] 153 | }] 154 | }, 155 | "connections": [{ 156 | "index": 0, 157 | "wireId": 11 158 | }, { 159 | "index": 1, 160 | "wireId": 0 161 | }, { 162 | "index": 2, 163 | "wireId": 6 164 | }] 165 | }, { 166 | "name": "output", 167 | "category": "other", 168 | "transform": { 169 | "items": [{ 170 | "name": "translate", 171 | "args": [63, 29] 172 | }] 173 | }, 174 | "connections": [{ 175 | "index": 0, 176 | "wireId": 11 177 | }] 178 | }] 179 | } -------------------------------------------------------------------------------- /library/half-subtractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Half subtractor", 3 | "boxes": [{ 4 | "name": "input", 5 | "category": "other", 6 | "transform": { 7 | "items": [{ 8 | "name": "translate", 9 | "args": [ 10 | 7, 11 | 4 12 | ] 13 | }] 14 | }, 15 | "connections": [{ 16 | "index": 0, 17 | "wireId": 0 18 | }, 19 | { 20 | "index": 0, 21 | "wireId": 1 22 | } 23 | ], 24 | "isOn": false 25 | }, 26 | { 27 | "name": "input", 28 | "category": "other", 29 | "transform": { 30 | "items": [{ 31 | "name": "translate", 32 | "args": [ 33 | 7, 34 | 15 35 | ] 36 | }] 37 | }, 38 | "connections": [{ 39 | "index": 0, 40 | "wireId": 2 41 | }, 42 | { 43 | "index": 0, 44 | "wireId": 3 45 | } 46 | ], 47 | "isOn": false 48 | }, 49 | { 50 | "name": "xor", 51 | "category": "gate", 52 | "transform": { 53 | "items": [{ 54 | "name": "translate", 55 | "args": [ 56 | 28, 57 | 5 58 | ] 59 | }] 60 | }, 61 | "connections": [{ 62 | "index": 0, 63 | "wireId": 4 64 | }, 65 | { 66 | "index": 1, 67 | "wireId": 0 68 | }, 69 | { 70 | "index": 2, 71 | "wireId": 2 72 | } 73 | ] 74 | }, 75 | { 76 | "name": "not", 77 | "category": "gate", 78 | "transform": { 79 | "items": [{ 80 | "name": "translate", 81 | "args": [ 82 | 19, 83 | 10 84 | ] 85 | }] 86 | }, 87 | "connections": [{ 88 | "index": 0, 89 | "wireId": 5 90 | }, 91 | { 92 | "index": 1, 93 | "wireId": 1 94 | } 95 | ] 96 | }, 97 | { 98 | "name": "output", 99 | "category": "other", 100 | "transform": { 101 | "items": [{ 102 | "name": "translate", 103 | "args": [ 104 | 41, 105 | 5 106 | ] 107 | }] 108 | }, 109 | "connections": [{ 110 | "index": 0, 111 | "wireId": 4 112 | }] 113 | }, 114 | { 115 | "name": "output", 116 | "category": "other", 117 | "transform": { 118 | "items": [{ 119 | "name": "translate", 120 | "args": [ 121 | 41, 122 | 14 123 | ] 124 | }] 125 | }, 126 | "connections": [{ 127 | "index": 0, 128 | "wireId": 6 129 | }] 130 | }, 131 | { 132 | "name": "and", 133 | "category": "gate", 134 | "transform": { 135 | "items": [{ 136 | "name": "translate", 137 | "args": [ 138 | 28, 139 | 14 140 | ] 141 | }] 142 | }, 143 | "connections": [{ 144 | "index": 0, 145 | "wireId": 6 146 | }, 147 | { 148 | "index": 1, 149 | "wireId": 5 150 | }, 151 | { 152 | "index": 2, 153 | "wireId": 3 154 | } 155 | ] 156 | } 157 | ], 158 | "blackbox": { 159 | "name": "HALF SUBTR", 160 | "inputs": 2, 161 | "outputs": 2, 162 | "table": [ 163 | [0, 0, 0, 0], 164 | [0, 1, 1, 1], 165 | [1, 0, 1, 0], 166 | [1, 1, 0, 0] 167 | ] 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/es6/modules/editorElements/Connector.js: -------------------------------------------------------------------------------- 1 | import NetworkElement from './NetworkElement'; 2 | import { Rectangle } from '../svgObjects'; 3 | import Logic from '../Logic'; 4 | 5 | import stateClasses from './stateClasses'; 6 | 7 | /** @module editorElements.Connector */ 8 | 9 | /** 10 | * parent class for input and output connectors 11 | * @extends NetworkElement 12 | */ 13 | export default class Connector extends NetworkElement { 14 | /** 15 | * @param {App} appInstance link to the [App](./module-App.html) instance that this connector will belong to 16 | * @param {number} gridSize size of the grid in SVG pixels 17 | * @param {number} left horizontal position defined in grid units (SVG pixels divided by the grid size) 18 | * @param {number} top vertical position defined in grid units (SVG pixels divided by the grid size) 19 | */ 20 | constructor(appInstance, left, top) { 21 | super(appInstance); 22 | 23 | /** 24 | * size of the grid in SVG pixels 25 | * @type {number} 26 | */ 27 | this.gridSize = appInstance.gridSize; 28 | /** 29 | * size of the connector in SVG pixels 30 | * @type {number} 31 | */ 32 | this.connectorSize = appInstance.gridSize; 33 | /** 34 | * offset of the connector from the grid in SVG pixels 35 | * @type {number} 36 | */ 37 | this.connectorOffset = this.connectorSize / 2; 38 | 39 | /** 40 | * instance of {@link svgObjects.svgObj} that holds all SVG information about this connector 41 | * @type {svgObj} 42 | */ 43 | this.svgObj = new Rectangle( 44 | left * this.gridSize - this.connectorOffset, 45 | top * this.gridSize - this.connectorOffset, 46 | this.connectorSize, 47 | this.connectorSize, 48 | 'none', 49 | 'black' 50 | ); 51 | 52 | this.svgObj.$el.addClass('connector'); 53 | 54 | /** 55 | * this flag describes whether this connector is an input connector 56 | * @type {Boolean} 57 | */ 58 | this.isInputConnector = false; 59 | 60 | /** 61 | * current logical state of this connector 62 | * @type {Logic.state} 63 | */ 64 | this.elementState = Logic.state.unknown; 65 | this.svgObj.addClass(stateClasses[Logic.state.unknown]); 66 | 67 | /** 68 | * set of ids of all wires connected to this connector 69 | * @type {Set} 70 | */ 71 | this.wireIds = new Set(); 72 | } 73 | 74 | /** 75 | * whether this connector is an output connector 76 | * @return {Boolean} 77 | */ 78 | get isOutputConnector() { 79 | return !this.isInputConnector; 80 | } 81 | 82 | /** 83 | * whether this connector is an output connector 84 | * @return {Boolean} 85 | */ 86 | set isOutputConnector(value) { 87 | this.isInputConnector = !value; 88 | } 89 | 90 | /** 91 | * add a wire id to the list of wire ids 92 | * @param {string} wireId 93 | */ 94 | addWireId(wireId) { 95 | this.wireIds.add(wireId); 96 | } 97 | 98 | /** 99 | * remove a wire id from the list of wire ids 100 | * @param {string} wireId 101 | */ 102 | removeWireId(wireId) { 103 | this.wireIds.delete(wireId); 104 | } 105 | 106 | /** 107 | * remove a wire specified by ID and update the connector 108 | * @param {string} wireId ID of the wire to be removed 109 | */ 110 | removeWireIdAndUpdate(wireId) { 111 | this.removeWireId(wireId); 112 | } 113 | 114 | /** 115 | * set logical state of the connector 116 | * @param {Logic.state} state new state of the connector 117 | */ 118 | setState(state) { 119 | this.svgObj.removeClasses(...stateClasses); 120 | this.svgObj.addClass(stateClasses[state]); 121 | 122 | this.elementState = state; 123 | } 124 | 125 | /** 126 | * get state of this connector 127 | * @return {Logic.state} 128 | */ 129 | get state() { 130 | return this.elementState; 131 | } 132 | 133 | /** 134 | * get svgObj instance content of this connector 135 | * @return {svgObjects.Rectangle} 136 | */ 137 | get() { 138 | return this.svgObj; 139 | } 140 | 141 | /** 142 | * call [wireCreationHelper](./module-App.html#wireCreationHelper) on mouse up 143 | */ 144 | onMouseUp(event) { 145 | // only left click counts 146 | if (event.which === 1) { 147 | event = this.appInstance.viewbox.transformEvent(event); 148 | 149 | const mousePosition = { 150 | x: event.pageX, 151 | y: event.pageY 152 | }; 153 | 154 | this.appInstance.wireCreationHelper(this.svgObj.id, mousePosition); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/img/svg/other/output.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 145 | -------------------------------------------------------------------------------- /src/img/svg/other/output-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 145 | -------------------------------------------------------------------------------- /src/img/svg/other/output-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 145 | -------------------------------------------------------------------------------- /src/img/svg/other/output-osc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 145 | -------------------------------------------------------------------------------- /src/es6/modules/ui/ViewBox.js: -------------------------------------------------------------------------------- 1 | /** @module ViewBox */ 2 | /** 3 | * ViewBox provides an api for oprerating with the viewBox argument of the