Compare commits
No commits in common. "master" and "0.2.10" have entirely different histories.
160
README.md
160
README.md
@ -1,173 +1,19 @@
|
||||
# MatCat BrowserLogic
|
||||
|
||||
MatCat BrowserLogic https://www.logic.parts/ is a logic simulator written purely in HTML5 / Javascript using 2D Canvas. This code will always work standalone, just download and browse to index.html with your favorite browser.
|
||||
|
||||
The goal of this project is to offer a logic simulator that is full featured conceptual simulation of logic. It does not aim to be an electrical simulator, so realistic propagation delays and such are not simulated, however that is not to say that there aren't (as some elements such as clocks and buffers and IC I/O do have delayed buffers).
|
||||
|
||||
I designed this to design CPU's in, I am sure someone with some imagination could design nearly anything in it :).
|
||||
MatCat BrowserLogic https://www.logic.parts/ is a logic simulator written purely in HTML5 / Javascript using 2D Canvas.
|
||||
|
||||
## Status
|
||||
|
||||
Currently, in pre-alpha early dev stage; however the simulator is about to enter the alpha stage, this means that the simulator core functionality is mostly in place. The Alpha stage will be dedicated to polishing the simulator itself, finding and fixing bugs, introduction of local storage for saves, introduction of server storage, and development of the website including adding integration features. Outside contribution is welcome, please contact me (MatCat) in #LogicParts on Freenode IRC network.
|
||||
This simulator is in extremely early stages, it is not even Alpha at this point but early development. Outside contribution is welcome, please contact me in #LogicParts on Freenode IRC network.
|
||||
|
||||
## License
|
||||
|
||||
To be decided, but at this moment this code is open source and free to use for non-commercial uses.
|
||||
|
||||
Copyright (c) 2021, MatCat
|
||||
|
||||
LZ-String, Copyright 2013 pieroxy under MIT license https://github.com/pieroxy/lz-string/blob/master/LICENSE
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.4.16
|
||||
|
||||
* Fixed bug where properties window didn't disappear at certain times when it should
|
||||
|
||||
### 0.4.15
|
||||
|
||||
* Prepending ! or ~ (active low) before a pin name will now overline it when displayed on element
|
||||
* Fixed a bug that disabled a switch after it was disconnected
|
||||
* Fixed bug in how JK flipflops handled PRE and CLR and CLK
|
||||
|
||||
### 0.4.14
|
||||
|
||||
* Added WireNode element, allows you to better manage wires. Please note that signals only flow in the direction of connection! It is NOT a bidirectional element
|
||||
* Added draw speed to FPS display (in uS)
|
||||
|
||||
### 0.4.13
|
||||
|
||||
* Clicking on an element on the toolbar no longer spawns an element to the work area directly, but rather lets the user place it.
|
||||
|
||||
### 0.4.12
|
||||
|
||||
* Fixed bug where pastes would get offset when canvas is panned
|
||||
|
||||
### 0.4.11
|
||||
|
||||
* Copy and Paste, note that Paste in the menu may not work on some browsers because of security permissions, but standard keyboard shortcuts will work.
|
||||
* Added !PRE and !CLR to flipflops, these are ACTIVE LOW inputs so you must make them high to use the flipflop.
|
||||
|
||||
### 0.4.10
|
||||
|
||||
* Draw optimizations, the drawing loop has been considerably optimized.
|
||||
* Edit button added to IC properties, note that there is currently no mechanism yet to go back to what you where doing, or to save any changes you make to whatever it was in. This is a WIP feature, I provided the button for those that need to change an IC, they can atleast go into it, make changes and save it, then delete the IC from their current design (presumably pre-saved and loaded after making edits / saves), and place it back in.
|
||||
|
||||
### 0.4.9
|
||||
|
||||
* Fixed a bug where the JS engine would hang if attempting to place a new element when the visual work area is too full of elements.
|
||||
|
||||
### 0.4.8
|
||||
|
||||
* Fixed bug where disconnects where not propagating properly
|
||||
|
||||
### 0.4.7
|
||||
|
||||
* Added LZW compression to save files (can be disabled on save)
|
||||
* Settings are now saved persistently
|
||||
* Added a basic help window, accessible via the top menu Help->Help, or pressing F1.
|
||||
* Hovering over an output will now hilight the links from it
|
||||
* Fixed a bug where keybindings could conflict while typing in textboxes
|
||||
|
||||
### 0.4.6
|
||||
|
||||
* Added a new keybinding system, and added many keyboard shortcuts (look in menu)
|
||||
* Fixed a bug where a button would stay on off the mouse was moved away from it while being eld down
|
||||
* Fixed a bug where you could link from elements with no outouts
|
||||
|
||||
### 0.4.5
|
||||
|
||||
* Keypad now supports either Switch, or Button mode for Function keys, also has Clear output that pulses for 100ms when Clr is pressed
|
||||
* Changed max input count for basic elements to 64 (from 12)
|
||||
|
||||
### 0.4.4
|
||||
|
||||
* Fixed a delinking bug
|
||||
* Fixed a bug where selection only worked in certain directions
|
||||
|
||||
### 0.4.3
|
||||
|
||||
* There is now multi-selection, including movement, deleting, disconnecting, etc.
|
||||
|
||||
### 0.4.2
|
||||
|
||||
* Added Hex keypad with 4 bit output, also has 3 function keys
|
||||
|
||||
### 0.4.1
|
||||
|
||||
* Fixed a signal propogation bug with IC's
|
||||
|
||||
### 0.4.0
|
||||
|
||||
* You can now pan the work area, hold ctrl key while mouse dragging an empty area.
|
||||
* Right Click Menu
|
||||
* If an item is selected you can delete or disconnect them
|
||||
* Top menu
|
||||
* New / Open / Save moved to File menu
|
||||
* You can now disable connection drawing, or put them above or below elements in View menu
|
||||
* There is now a pan to center feature in View menu
|
||||
* Create IC has been moved to Tools menu
|
||||
* Help menu -> About opens the Welcome Window
|
||||
* Save dialog
|
||||
* Fixed various small bugs
|
||||
|
||||
### 0.3.10
|
||||
* Added BCD and Decimal options to the 4 bit output display, as well as bit labels in inputs
|
||||
|
||||
### 0.3.9
|
||||
|
||||
* Added grid alignment, hold CTRL key while moving to get pixel level positioning.
|
||||
* Fixed property restoration issue with labels on loading a save
|
||||
|
||||
### 0.3.8
|
||||
|
||||
* Added labels! You can specify the text, size, and color
|
||||
|
||||
### 0.3.7
|
||||
|
||||
* Added 4 bit display! Currently shows hex format
|
||||
* Fixed a bug with flip flop save states for connections
|
||||
|
||||
### 0.3.6
|
||||
|
||||
* Fixed a bug where flip flops weren't firing output changes when only !Q changed.
|
||||
|
||||
### 0.3.5
|
||||
|
||||
* Fixed a bug where newly linked objects weren't being polled to the proper output
|
||||
|
||||
### 0.3.4
|
||||
|
||||
* Added Flip-Flops! There are 4 to choose from, JK, SR, D, and T!
|
||||
|
||||
### 0.3.3
|
||||
|
||||
* Fixed a bug that crashed the engine when attempting to place an IC with no inputs
|
||||
|
||||
### 0.3.2
|
||||
|
||||
* Added IC Element, allows for converting a logic design to an IC for use in designs.
|
||||
* Added IC Input and IC Output which allow for external connections when a design is used as an IC
|
||||
* Added a New button for creating a new design
|
||||
|
||||
### 0.3.1
|
||||
* Toolbox is now dynamically created and categorized
|
||||
* The delete button is gone for now (use delete key on keyboard), it will come back when toolbar is added.
|
||||
|
||||
### 0.3.0
|
||||
* Saving / Loading of designs
|
||||
* New Element: Buffer, allows for construction of circuits that would otherwise be recursive
|
||||
* Added pulse count to the delay block printout
|
||||
* Fixed grid draw bug
|
||||
|
||||
### 0.2.12
|
||||
* Fixed floating active link when deleting an element while linking
|
||||
* Fixed the delay element so that it properly buffers all input pulses
|
||||
|
||||
### 0.2.11
|
||||
* Added Delay element
|
||||
|
||||
### 0.2.10
|
||||
|
||||
* New elements will now do overlap detection on placement
|
||||
* Newly placed elements will now be selected
|
||||
|
||||
|
||||
290
css/main.css
290
css/main.css
@ -12,10 +12,8 @@
|
||||
========================================================================== */
|
||||
body {
|
||||
background-color: #54545d;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
color: #ddd;
|
||||
font-size: 1em;
|
||||
@ -267,126 +265,22 @@ textarea {
|
||||
}
|
||||
|
||||
#left-menu {
|
||||
width: 212px;
|
||||
height: 99vh;
|
||||
width: 200px;
|
||||
max-width: 200px;
|
||||
height: 100vh;
|
||||
display: inline-block;
|
||||
border: 1px solid black;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
#inner-left-menu {
|
||||
width: 100%;
|
||||
height: 99.4%;
|
||||
overflow-y: scroll;
|
||||
background-color: #54545d;
|
||||
}
|
||||
|
||||
#inner-left-menu::-webkit-scrollbar, #HelpWindowContent::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
#inner-left-menu::-webkit-scrollbar-track, #HelpWindowContent::-webkit-scrollbar-track {
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#575a60+0,292b2d+14,000000+49,292b2d+82,575a60+100 */
|
||||
background: rgb(87,90,96); /* Old browsers */
|
||||
background: -moz-linear-gradient(left, rgba(87,90,96,1) 0%, rgba(41,43,45,1) 14%, rgba(0,0,0,1) 49%, rgba(41,43,45,1) 82%, rgba(87,90,96,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(left, rgba(87,90,96,1) 0%,rgba(41,43,45,1) 14%,rgba(0,0,0,1) 49%,rgba(41,43,45,1) 82%,rgba(87,90,96,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to right, rgba(87,90,96,1) 0%,rgba(41,43,45,1) 14%,rgba(0,0,0,1) 49%,rgba(41,43,45,1) 82%,rgba(87,90,96,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#575a60', endColorstr='#575a60',GradientType=1 ); /* IE6-9 */
|
||||
}
|
||||
|
||||
#inner-left-menu::-webkit-scrollbar-thumb, #HelpWindowContent::-webkit-scrollbar-thumb {
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#000000+0,000000+50,000000+50,8e8e8e+50,000000+100&0+0,1+50,0+100 */
|
||||
background: -moz-linear-gradient(left, rgba(0,0,0,0) 0%, rgba(142,142,142,1) 50%, rgba(0,0,0,0) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(left, rgba(0,0,0,0) 0%,rgba(142,142,142,1) 50%,rgba(0,0,0,0) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to right, rgba(0,0,0,0) 0%,rgba(142,142,142,1) 50%,rgba(0,0,0,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#00000000',GradientType=1 ); /* IE6-9 */
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
#top-bar {
|
||||
height: 30px;
|
||||
overflow: none;
|
||||
height: 50px;
|
||||
overflow: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
background-color: #222;
|
||||
font-size: 2em;
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#a7cfdf+0,23538a+100;Blue+3d+%238 */
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#4c4c4c+0,595959+12,666666+25,474747+39,2c2c2c+50,000000+51,111111+60,2b2b2b+76,1c1c1c+91,131313+100;Black+Gloss+%231 */
|
||||
background: rgb(76,76,76); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(76,76,76,1) 0%, rgba(89,89,89,1) 12%, rgba(102,102,102,1) 25%, rgba(71,71,71,1) 39%, rgba(44,44,44,1) 50%, rgba(0,0,0,1) 51%, rgba(17,17,17,1) 60%, rgba(43,43,43,1) 76%, rgba(28,28,28,1) 91%, rgba(19,19,19,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, rgba(76,76,76,1) 0%,rgba(89,89,89,1) 12%,rgba(102,102,102,1) 25%,rgba(71,71,71,1) 39%,rgba(44,44,44,1) 50%,rgba(0,0,0,1) 51%,rgba(17,17,17,1) 60%,rgba(43,43,43,1) 76%,rgba(28,28,28,1) 91%,rgba(19,19,19,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, rgba(76,76,76,1) 0%,rgba(89,89,89,1) 12%,rgba(102,102,102,1) 25%,rgba(71,71,71,1) 39%,rgba(44,44,44,1) 50%,rgba(0,0,0,1) 51%,rgba(17,17,17,1) 60%,rgba(43,43,43,1) 76%,rgba(28,28,28,1) 91%,rgba(19,19,19,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
|
||||
}
|
||||
|
||||
|
||||
.TOOLBOX_CATEGORY_HEADER {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#8c9b3f+0,6d8e20+50,456000+51,799e24+100 */
|
||||
background: rgb(140,155,63); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(140,155,63,1) 0%, rgba(109,142,32,1) 50%, rgba(69,96,0,1) 51%, rgba(121,158,36,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, rgba(140,155,63,1) 0%,rgba(109,142,32,1) 50%,rgba(69,96,0,1) 51%,rgba(121,158,36,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, rgba(140,155,63,1) 0%,rgba(109,142,32,1) 50%,rgba(69,96,0,1) 51%,rgba(121,158,36,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#8c9b3f', endColorstr='#799e24',GradientType=0 ); /* IE6-9 */
|
||||
|
||||
}
|
||||
.TOOLBOX_CATEGORY_HEADER h2 {
|
||||
margin: 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.TOOLBOX_CATEGORY_BODY input {
|
||||
font-size: 0.9em;
|
||||
padding: 1px;
|
||||
text-shadow: 0 1px 0 rgba(180, 180, 180, 0.4),
|
||||
1px 0 0 rgba(180, 180, 180, 0.4);
|
||||
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#f5f6f6+0,dbdce2+21,b8bac6+49,dddfe3+80,f5f6f6+100;Grey+Pipe */
|
||||
background: rgb(245,246,246); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(245,246,246,1) 0%, rgba(219,220,226,1) 21%, rgba(184,186,198,1) 49%, rgba(221,223,227,1) 80%, rgba(245,246,246,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, rgba(245,246,246,1) 0%,rgba(219,220,226,1) 21%,rgba(184,186,198,1) 49%,rgba(221,223,227,1) 80%,rgba(245,246,246,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, rgba(245,246,246,1) 0%,rgba(219,220,226,1) 21%,rgba(184,186,198,1) 49%,rgba(221,223,227,1) 80%,rgba(245,246,246,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f5f6f6', endColorstr='#f5f6f6',GradientType=0 ); /* IE6-9 */
|
||||
|
||||
}
|
||||
|
||||
.CloseWindow:hover {
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#efc5ca+0,d24b5a+50,ba2737+51,f18e99+100;Red+Gloss+%233 */
|
||||
background: rgb(239,197,202); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(239,197,202,1) 0%, rgba(210,75,90,1) 50%, rgba(186,39,55,1) 51%, rgba(241,142,153,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, rgba(239,197,202,1) 0%,rgba(210,75,90,1) 50%,rgba(186,39,55,1) 51%,rgba(241,142,153,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, rgba(239,197,202,1) 0%,rgba(210,75,90,1) 50%,rgba(186,39,55,1) 51%,rgba(241,142,153,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#efc5ca', endColorstr='#f18e99',GradientType=0 ); /* IE6-9 */
|
||||
|
||||
}
|
||||
|
||||
.CloseWindow {
|
||||
cursor: default;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
font-family: Calibri;
|
||||
border-radius: 5px;
|
||||
margin: 3px;
|
||||
width: 1em;
|
||||
font-size: 0.8em;
|
||||
|
||||
/* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#f85032+0,f16f5c+50,f6290c+51,f02f17+71,e73827+100;Red+Gloss+%231 */
|
||||
background: rgb(248,80,50); /* Old browsers */
|
||||
background: -moz-linear-gradient(top, rgba(248,80,50,1) 0%, rgba(241,111,92,1) 50%, rgba(246,41,12,1) 51%, rgba(240,47,23,1) 71%, rgba(231,56,39,1) 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, rgba(248,80,50,1) 0%,rgba(241,111,92,1) 50%,rgba(246,41,12,1) 51%,rgba(240,47,23,1) 71%,rgba(231,56,39,1) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, rgba(248,80,50,1) 0%,rgba(241,111,92,1) 50%,rgba(246,41,12,1) 51%,rgba(240,47,23,1) 71%,rgba(231,56,39,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f85032', endColorstr='#e73827',GradientType=0 ); /* IE6-9 */
|
||||
}
|
||||
|
||||
#floatCanvas {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#darkout-overlay {
|
||||
@ -439,7 +333,7 @@ textarea {
|
||||
#WelcomeWindow label {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
#PropertiesBox, #CreateICBox, #SaveWindow, #HelpWindow {
|
||||
#PropertiesBox {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
@ -448,184 +342,18 @@ textarea {
|
||||
height: 200px;
|
||||
background-color: #444455;
|
||||
border: 1px solid black;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#HelpWindow {
|
||||
width: 60vw;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.HelpZone {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.HelpZone h2 {
|
||||
background-color: #222222;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.HelpZone h3 {
|
||||
padding-left: 10px;
|
||||
border-bottom: 1px inset #444;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
color: #eeffff;
|
||||
}
|
||||
|
||||
|
||||
.HelpZone p {
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#HelpWindowContent {
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
height: 93%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#CreateICBox {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
z-index: 9999999;
|
||||
}
|
||||
|
||||
#CreateICBox input[pattern]:invalid {
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
#SaveWindow {
|
||||
width: 350px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
#SaveWindowContent {
|
||||
margin: auto;
|
||||
width: 90%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#PropertiesBoxTitle, #CreateICBoxTitle, #SaveWindowTitle, #HelpWindowTitle {
|
||||
#PropertiesBoxTitle {
|
||||
text-align: center;
|
||||
background-color: #222222;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#PropertiesBoxContent, #CreateICBoxContent, #SaveWindowContent {
|
||||
#PropertiesBoxContent {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#RightClickMenu, .top-menu-div {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
background-color: #444455;
|
||||
border: 1px solid #232323;
|
||||
z-index: 9999999999;
|
||||
}
|
||||
|
||||
#left-top-menu {
|
||||
width: 70%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#SiteTitle {
|
||||
width: 29%;
|
||||
font-size: 0.6em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#top-menu {
|
||||
|
||||
}
|
||||
|
||||
#top-menu a {
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#top-menu a:visited {
|
||||
color: #ddd;
|
||||
}
|
||||
#top-menu ul {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
#top-menu li {
|
||||
display: inline-block;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
list-style-type: none;
|
||||
margin: 0px;
|
||||
padding: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.top-menu-div {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
|
||||
.menuitem-label {
|
||||
}
|
||||
.menuitem-label, .menuitem-shortcut {
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
}
|
||||
.menuitem-shortcut {
|
||||
padding-left: 10px;
|
||||
float: right;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
#top-menu li span.checkbox, #top-menu li span.blankbox {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
width: 1em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#RightClickMenu ul, .top-menu-div ul {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#RightClickMenu li, .top-menu-div li {
|
||||
display: block !important;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
list-style-type: none;
|
||||
margin: 0px;
|
||||
padding: 2px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
#RightClickMenu li:hover, .top-menu-div li:hover {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
#RightClickMenu .disabled, #top-menu .disabled {
|
||||
color: #777777;
|
||||
}
|
||||
|
||||
.rcm_seperator:hover, .tfm_seperator:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.rcm_seperator, .tfm_seperator {
|
||||
height: 2px;
|
||||
margin-bottom: 7px;
|
||||
border-bottom: 2px groove #54545d;
|
||||
}
|
||||
|
||||
#LogicPlane {
|
||||
outline: none;
|
||||
-webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */
|
||||
|
||||
179
index.html
179
index.html
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html class="no-js" lang="" style="height: 100%;">
|
||||
<html class="no-js" lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@ -21,90 +21,32 @@
|
||||
<meta name="theme-color" content="#fafafa">
|
||||
</head>
|
||||
|
||||
<body style="height: 100%;">
|
||||
<body>
|
||||
|
||||
<div id="WelcomeWindow">
|
||||
<h2>MatCat BrowserLogic Engine</h2>
|
||||
<p>Welcome to logic.parts, a free to use logic simulator for anyone to use. This is opensource software, you can find the project at <a href="https://www.mygit.space/MatCat.OpenSource/BrowserLogic">https://www.mygit.space/MatCat.OpenSource/BrowserLogic</a>. This is an early development project, so expect bugs and some things to not work :) If you would like to contribute please feel free to find me on IRC on Freenode in channel #LogicParts.</p>
|
||||
<p><span style="font-weight: bold; font-size: 1.2em;">Now on Version 0.4!</span> This new version brings an improved UI, panning, more settings options, and much more!</p>
|
||||
<input type="button" id="btn_CloseWelcome" value="Simulate Some Logic!"><br />
|
||||
<input type="checkbox" id="chk_dontDisplayWelcome" name="chk_dontDisplayWelcome" value="1">
|
||||
<label for="chk_dontDisplayWelcome">Don't show welcome window again</label>
|
||||
<input type="file" id="file_Load" accept=".LogicParts" style="display: none;" />
|
||||
</div>
|
||||
<label for="chk_dontDisplayWelcome">Don't show welcome window again</label></div>
|
||||
<script src="js/vendor/modernizr-3.11.2.min.js"></script>
|
||||
<div id="top-bar">
|
||||
<div id="top-menu">
|
||||
<div id="left-top-menu">
|
||||
<ul>
|
||||
<li id="tm_File" class="top-menu-item">File
|
||||
<div id="tm_FileMenu" class="top-menu-div">
|
||||
<ul>
|
||||
<li id="tfm_New" value="FileNew">New</li>
|
||||
<li id="tfm_Open" value="FileOpen">Open</li>
|
||||
<li id="tfm_Save" value="FileSave">Save</li>
|
||||
<li id="tfm_SaveAs" class="disabled" title="Feature coming soon">Save As</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li id="tm_Edit" class="top-menu-item">Edit
|
||||
<div id="tm_EditMenu" class="top-menu-div">
|
||||
<ul>
|
||||
<li id="tfm_Undo" value="EditUndo" class="disabled" title="Feature coming soon">Undo</li>
|
||||
<li id="tfm_Redo" value="EditRedo" class="disabled" title="Feature coming soon">Redo</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_Cut" value="Cut">Cut</li>
|
||||
<li id="tfm_Copy" value="Copy">Copy</li>
|
||||
<li id="tfm_Paste" value="Paste">Paste</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_Delete" value="DeleteElements" class="disabled">Delete</li>
|
||||
<li id="tfm_Disconnect" value="DisconnectElements" class="disabled">Disconnect</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_SelectAll" value="SelectAll" title="Select all elements">Select All</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li id="tm_View" class="top-menu-item">View
|
||||
<div id="tm_ViewMenu" class="top-menu-div">
|
||||
<ul>
|
||||
<li id="tfm_Pan2Center" value="ResetCanvas"><span class="blankbox"> </span>Pan to Center</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_ShowConnections" value="ShowConnections"><span class="checkbox">✓</span>Show Connections</li>
|
||||
<li id="tfm_ConnectionLayer" value="ConnectionLayer"><span class="blankbox"> </span>Connections Below</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_ShowGrid" value="ShowGrid"><span class="checkbox">✓</span>Show Grid</li>
|
||||
<li id="tfm_SnapGrid" value="SnapGrid"><span class="checkbox">✓</span>Snap to Grid</li>
|
||||
<li id="tfm_GridSize"><span class="blankbox"> </span>Grid Size <input type="number" id="in_GridSize" value="20" min="2" max="100" style="margin-left: 10px; width: 40px;"><span class="menuitem-shortcut">Shift+(-,+)</span></li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_ShowFPS" value="ShowFPS"><span class="checkbox">✓</span>Show FPS</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li id="tm_Tools" class="top-menu-item">Tools
|
||||
<div id="tm_ToolsMenu" class="top-menu-div">
|
||||
<ul>
|
||||
<li id="tfm_CreateIC" value="CreateIC" class="disabled" title="Turn the current design into an IC, must have atleast one IC Output to create">Create IC</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li id="tm_Help" class="top-menu-item">Help
|
||||
<div id="tm_HelpMenu" class="top-menu-div">
|
||||
<ul>
|
||||
<li id="tfm_Help" value="Help">Help</li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_GITRepo"><a target="new" href="https://mygit.space/MatCat.OpenSource/BrowserLogic">GIT Repo</a></li>
|
||||
<li class="tfm_seperator"></li>
|
||||
<li id="tfm_About">About</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="SiteTitle">MatCat BrowserLogic <span id="version"> </span></div>
|
||||
</div>
|
||||
MatCat BrowserLogic <span id="version"> </span>
|
||||
</div>
|
||||
<div id="left-menu">
|
||||
<div id="inner-left-menu">
|
||||
LOADING
|
||||
<div style="margin: 5px;">
|
||||
<input type="button" id="btn_AddAND" value="& AND"/><br />
|
||||
<input type="button" id="btn_AddNAND" value="!& NAND"/><br />
|
||||
<input type="button" id="btn_AddOR" value="| OR"/><br />
|
||||
<input type="button" id="btn_AddNOR" value="!| NOR"/><br />
|
||||
<input type="button" id="btn_AddXOR" value="^ XOR"/><br />
|
||||
<input type="button" id="btn_AddXNOR" value="!^ XNOR"/><br />
|
||||
<input type="button" id="btn_AddNOT" value="! NOT"/><br />
|
||||
<input type="button" id="btn_AddSWITCH" value="|- SWITCH"/><br />
|
||||
<input type="button" id="btn_AddBTN" value="[o] BUTTON"/><br />
|
||||
<input type="button" id="btn_AddCLK" value="🕑 Clock"/><br />
|
||||
<input type="button" id="btn_AddPULSE" value="_|\_ Pulse"/><br />
|
||||
<input type="button" id="btn_Delete" value="🗑 Delete"/>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="GridPlane" width="400" height="300" style="position: absolute; top: 50px; left 202px;"></canvas>
|
||||
@ -117,93 +59,10 @@
|
||||
Content
|
||||
</div>
|
||||
</div>
|
||||
<div id="CreateICBox">
|
||||
<div id="CreateICBoxTitle">
|
||||
Create new IC
|
||||
</div>
|
||||
<div id="CreateICBoxContent">
|
||||
<p>To create an IC you must give it a few parameters below:</p>
|
||||
<div><span style="display: inline-block; text-align: right; vertical-align: middle; width: 100px; height: 100%; padding-right: 10px;">Name</span><input type="text" id="ICName" style="width: 250px" pattern="[a-zA-Z0-9_]+" title="Only letters, numbers, and underscore, no special characters or spaces! Spaces will be converted to _"></div>
|
||||
<div><span style="display: inline-block; text-align: right; vertical-align: middle; width: 100px; height: 100%; padding-right: 10px;">Description</span><textarea id="ICDescription" style="width: 250px" rows="5"></textarea></div>
|
||||
<center><input type="button" id="btn_CreateIC_Create" value="Create IC" disabled> <input type="button" id="btn_CreateIC_Cancel" value="Cancel"></center>
|
||||
</div>
|
||||
</div>
|
||||
<div id="RightClickMenu">
|
||||
<ul>
|
||||
<li id="rcm_New">New</li>
|
||||
<li id="rcm_seperator1" class="rcm_seperator"></li>
|
||||
<li id="rcm_CreateIC">Create IC</li>
|
||||
<li id="rcm_seperator2" class="rcm_seperator"></li>
|
||||
<li id="rcm_Delete">Delete</li>
|
||||
<li id="rcm_Disconnect">Disconnect</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="SaveWindow">
|
||||
<div id="SaveWindowTitle">
|
||||
Save Design
|
||||
</div>
|
||||
<div id="SaveWindowContent">
|
||||
<div>
|
||||
<span style="padding-right: 10px;">Design Name</span><span><input type="text" id="saveName" value="My Design"></span>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="saveCompressed" checked /> Save Compressed
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<center><input type="button" id="btn_SaveDesign" value="Save"> <input type="button" id="btn_CancelSave" value="Cancel"></center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="HelpWindow">
|
||||
<div id="HelpWindowTitle">
|
||||
MatCat Logic Engine Help
|
||||
<div class="CloseWindow">X</div>
|
||||
</div>
|
||||
<div id="HelpWindowContent">
|
||||
<div class="HelpZone">
|
||||
<h2>Basic Concept</h2>
|
||||
<h3>Signal Propagation</h3>
|
||||
<p>This engine simulates digital logic conceptually, it does not try to perfectly emulate real world devices, I.E. there is no impedance, propagation delay simulation, and the like. However it does try to simulate high/low as realistic as it can, so for example if you have multiple outputs connected to a single input, if any of them are high, that input will see high, it will not go low until ALL connections to it have gone low, this does somewhat mimic the idea of a tristate, and allows for using connections at bus points, as well as line level OR. </p>
|
||||
<h3>Simulation Speed</h3>
|
||||
<p>Logic signals are allowed to flow as fast as the javascript interpreter can run the code, when using IC's input and outputs ARE buffered, which means they will have a propagation delay of the shortest possible javascript timing, which on most browsers is 4ms. Using a buffer element would have the same delay time as an IC I/O.</p>
|
||||
</div>
|
||||
<div class="HelpZone">
|
||||
<h2>Linking</h2>
|
||||
<p>You can only link from an output to an input, to do so click on an output, a dashed line will now follow your mouse, click on an input of another element to finish the link.</p>
|
||||
<p>To delete a link simply do the same thing you did to connect the link and it will disconnect that link.</p>
|
||||
</div>
|
||||
<div class="HelpZone">
|
||||
<h2>IC's</h2>
|
||||
<h3>Concept</h3>
|
||||
<p>The IC functionality is a core powerful function of this engine as it allows you to design a circuit and turn it into a single self contained element you can use in other designs, including inside of OTHER IC's. IC's are unique only in that you must place input and output elements to interface to the IC. The Input and Output elements you place will determine what pins are shown on the IC when created. Note that IC Input and Outputs are buffered connections.</p>
|
||||
<h3>Creating</h3>
|
||||
<p>To create an IC the minimal needed circuitry is atleast 1 IC Output in the design, once you have a design you wish to turn into an IC you can do so either via the top menu Tools->Create IC, or the right click menu Create IC option, or keyboard shortcut (default ctrl+c). This will open a dialog box where you can name and describe your IC. Once it is created it will appear in the left hand toolbox under the IC's category.</p>
|
||||
</div>
|
||||
<div class="HelpZone">
|
||||
<h2>Work Area</h2>
|
||||
<h3>Panning</h3>
|
||||
<p>You can pan the design by holding control key while clicking and dragging with the left mouse button on unoccupied space.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="floatCanvas"><canvas></canvas></div>
|
||||
<div id="darkout-overlay"></div>
|
||||
<script src="js/vendor/lz-string.min.js"></script>
|
||||
<script src="js/topmenu/topmenu.js"></script>
|
||||
<script src="js/rightclickmenu/rightclickmenu.js"></script>
|
||||
<script src="js/globalfunctions.js"></script>
|
||||
<script src="js/baseclasses.js"></script>
|
||||
<script src="js/scheduler.js"></script>
|
||||
<script src="js/elements/BaseElementClasses.js"></script>
|
||||
<script src="js/elements/InputElements.js"></script>
|
||||
<script src="js/elements/DisplayElements.js"></script>
|
||||
<script src="js/elements/BasicElements.js"></script>
|
||||
<script src="js/elements/FFElements.js"></script>
|
||||
<script src="js/elements/TimingElements.js"></script>
|
||||
<script src="js/elements/ICElements.js"></script>
|
||||
<script src="js/logicengine.js"></script>
|
||||
<script src="js/mainevents.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@ -1,355 +0,0 @@
|
||||
class CanvasTools {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
textSize(ctx,text,fontStyle) {
|
||||
ctx.save();
|
||||
ctx.font = fontStyle;
|
||||
let tHeight = Math.round(ctx.measureText(text).actualBoundingBoxAscent + ctx.measureText(text).actualBoundingBoxDescent);
|
||||
let tWidth = Math.round(ctx.measureText(text).width);
|
||||
ctx.restore();
|
||||
return {
|
||||
width: tWidth,
|
||||
height: tHeight
|
||||
};
|
||||
}
|
||||
|
||||
drawBorderBox(ctx,x,y,drawWidth,drawHeight,borderWidth=1,borderColor="#000",fillColor="#f7e979",shadowColor = "transparent") {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = borderColor;
|
||||
if (shadowColor != "transparent") {
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.fillRect(x,y,drawWidth,drawHeight);
|
||||
ctx.fillStyle = fillColor;
|
||||
ctx.fillRect(x+borderWidth,y+borderWidth,drawWidth-(borderWidth*2),drawHeight-(borderWidth*2));
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawTextCentered(ctx,x,y,x2,y2,text,fontStyle="24px Console",fontColor = "#555", Overline = false) {
|
||||
ctx.save();
|
||||
ctx.font = fontStyle;
|
||||
ctx.fillStyle = fontColor;
|
||||
let textSize = this.textSize(ctx,text,fontStyle);
|
||||
let tHeight = ctx.measureText(text).actualBoundingBoxAscent + ctx.measureText(text).actualBoundingBoxDescent;
|
||||
let tX = x+((x2/2)-(ctx.measureText(text).width/2));
|
||||
let tY = y+tHeight+((y2/2)-(tHeight/2));
|
||||
if (Overline) {
|
||||
ctx.strokeStyle = fontColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(tX,tY-textSize.height-1);
|
||||
ctx.lineTo(tX+textSize.width,tY-textSize.height-1);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.fillText(text,tX,tY);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawText(ctx,x,y,text,fontStyle="24px Console",fontColor = "#555",Overline = false) {
|
||||
let textSize = this.textSize(ctx,text,fontStyle);
|
||||
ctx.save();
|
||||
ctx.font = fontStyle;
|
||||
ctx.fillStyle = fontColor;
|
||||
ctx.fillText(text,x,y);
|
||||
if (Overline) {
|
||||
ctx.strokeStyle = fontColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x,y-textSize.height-2);
|
||||
ctx.lineTo(x+textSize.width,y-textSize.height-2);
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerConnection {
|
||||
constructor(fromElementContainer,toElementContainer,fromElement,toElement,input) {
|
||||
this.fromElementContainer = fromElementContainer;
|
||||
this.toElementContainer = toElementContainer;
|
||||
this.fromElement = fromElement;
|
||||
this.toElement = toElement;
|
||||
this.Input = input;
|
||||
}
|
||||
toJSON(key) {
|
||||
return {
|
||||
fromElement: (this.fromElement) ? this.fromElement.Designator : null,
|
||||
toElement: (this.toElement) ? this.toElement.Designator : null,
|
||||
Input: parseInt(this.Input)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class elementContainer {
|
||||
constructor() {
|
||||
this.Elements = new Array();
|
||||
this.Selected = false;
|
||||
this.Inputs = new Array();
|
||||
this.Outputs = new Array();
|
||||
this.StaticImages = {};
|
||||
this.ICOutputs = 0;
|
||||
}
|
||||
|
||||
|
||||
isSelected(element) {
|
||||
if (this.Selected) {
|
||||
for (let a = 0; a < this.Selected.length; a++) {
|
||||
if (this.Selected[a] == element) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isHigh(element,input) {
|
||||
let isHigh = false;
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
let conn = this.Elements[a].ConnectsTo(element,input);
|
||||
if (conn !== false) {
|
||||
if (this.Elements[a].getOutput(conn)) isHigh = true;
|
||||
}
|
||||
}
|
||||
return isHigh;
|
||||
}
|
||||
|
||||
clearRedraw() {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
this.Elements[a].redraw = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
toJSON(key) {
|
||||
let elements = new Array();
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
elements.push(this.Elements[a].toJSON());
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
AddElement(element) {
|
||||
let designatorNumber = 1;
|
||||
let designatorTest = element.Name + designatorNumber;
|
||||
let unused = false;
|
||||
|
||||
while (!unused) {
|
||||
let foundMatch = false;
|
||||
for (let a=0;a < this.Elements.length;a++) {
|
||||
if (this.Elements[a].Designator == designatorTest) foundMatch = true;
|
||||
}
|
||||
if (foundMatch) {
|
||||
designatorNumber++;
|
||||
designatorTest = element.Name + designatorNumber;
|
||||
} else {
|
||||
unused = true;
|
||||
element.Designator = designatorTest;
|
||||
if (logicEngine.ActiveContainer === this) {
|
||||
element.clearStatic();
|
||||
element.drawElement(0, 0, element.StaticCtx);
|
||||
}
|
||||
this.Elements.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeleteElement(element) {
|
||||
// Can pass object or Designator, or array
|
||||
if (Array.isArray(element)) {
|
||||
let returnval = false;
|
||||
for (let a = 0; a < element.length; a++) {
|
||||
for (let b = 0; b < this.Elements.length; b++) {
|
||||
if ((this.Elements[b] == element[a]) || (this.Elements[b].Designator == element[a])) {
|
||||
this.Elements[b].Delete();
|
||||
this.Elements.splice(b, 1);
|
||||
b--;
|
||||
returnval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnval;
|
||||
} else {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
if ((this.Elements[a] == element) || (this.Elements[a].Designator == element)) {
|
||||
this.Elements[a].Delete();
|
||||
this.Elements.splice(a, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HasElement(element) {
|
||||
// Can pass object or Designator
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
if ((this.Elements[a] == element) || (this.Elements[a].Designator == element)) {
|
||||
return this.Elements[a];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Disconnect(element) {
|
||||
if (!element) return false;
|
||||
|
||||
if (Array.isArray(element)) {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
for (let b = 0; b < element.length; b++) {
|
||||
this.Elements[a].Disconnect(element[b]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
this.Elements[a].Disconnect(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawAll(ctx,settings) {
|
||||
let ICOuts = 0;
|
||||
|
||||
if (!logicEngine.Settings.TopConnections && !logicEngine.Settings.HideConnections) {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
// Not ideal to loop twice but we need the connections drawn all at once to prevent layer issues
|
||||
this.Elements[a].drawConnections(ctx, settings);
|
||||
}
|
||||
}
|
||||
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
if (this.Elements[a] instanceof ICOutput) ICOuts++;
|
||||
if (this.Elements[a].isVisible()) {
|
||||
ctx.save();
|
||||
if (this.isSelected(this.Elements[a])) this.Elements[a].drawBorderBox(ctx, this.Elements[a].X - 2, this.Elements[a].Y - 2, this.Elements[a].Width + 4, this.Elements[a].Height + 4, 1, "rgba(100,200,255,0.25)", "rgba(100,200,255,0.25)");
|
||||
//this.Elements[a].drawElement(this.Elements[a].X, this.Elements[a].Y, ctx);
|
||||
ctx.drawImage(this.Elements[a].StaticCanvas,this.Elements[a].X,this.Elements[a].Y);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
this.ICOutputs = ICOuts;
|
||||
if (!this.Selected) {
|
||||
if (this.Selected.length == 1) {
|
||||
let PropertiesBox = document.getElementById("PropertiesBox");
|
||||
if (PropertiesBox.style.display != "none") PropertiesBox.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
if (logicEngine.Settings.TopConnections && !logicEngine.Settings.HideConnections) {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
// Not ideal to loop twice but we need the connections drawn all at once to prevent layer issues
|
||||
this.Elements[a].drawConnections(ctx, settings);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SelectWithin(x1,y1,x2,y2) {
|
||||
let selectedArray = new Array();
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
if ((this.Elements[a].X >= x1) && ((this.Elements[a].X + this.Elements[a].Width) <= x2) && (this.Elements[a].Y >= y1) && ((this.Elements[a].Y + this.Elements[a].Height) <= y2)) selectedArray.push(this.Elements[a]);
|
||||
}
|
||||
this.Selected = selectedArray;
|
||||
}
|
||||
|
||||
Select(element) {
|
||||
this.Selected = new Array(element);
|
||||
let PropertiesBox = document.getElementById("PropertiesBox");
|
||||
let PropertiesBoxTitle = document.getElementById("PropertiesBoxTitle");
|
||||
let PropertiesBoxContent = document.getElementById("PropertiesBoxContent");
|
||||
PropertiesBoxTitle.innerText = this.Selected[0].Designator + " Properties";
|
||||
let contentString = "<table id='propertiesTable'>";
|
||||
for (let a = 0; a < this.Selected[0].Properties.length;a++) {
|
||||
contentString += "<tr><td>" + this.Selected[0].Properties[a].Name + "</td><td>";
|
||||
switch (this.Selected[0].Properties[a].Type) {
|
||||
case "button":
|
||||
contentString += "<input type='button' id='prop_" + this.Selected[0].Properties[a].Name + "' value='" + this.Selected[0].Properties[a].CurrentValue + "' onclick='logicEngine.PropertyChange(" + String.fromCharCode(34) + this.Selected[0].Properties[a].Name + String.fromCharCode(34) +");'>";
|
||||
break;
|
||||
case "int":
|
||||
contentString += "<input type='number' id='prop_" + this.Selected[0].Properties[a].Name + "' min='" + this.Selected[0].Properties[a].Minimium + "' max='" + this.Selected[0].Properties[a].Maximium + "' value='" + this.Selected[0].Properties[a].CurrentValue + "' onchange='logicEngine.PropertyChange(" + String.fromCharCode(34) + this.Selected[0].Properties[a].Name + String.fromCharCode(34) +");'>";
|
||||
break;
|
||||
case "string":
|
||||
contentString += '<input type="text" id="prop_' + this.Selected[0].Properties[a].Name + '" minlength="' + this.Selected[0].Properties[a].Minimium + '" maxlength="' + this.Selected[0].Properties[a].Maximium + '" value="' + this.Selected[0].Properties[a].CurrentValue + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected[0].Properties[a].Name + String.fromCharCode(39) + ');">';
|
||||
break;
|
||||
case "color":
|
||||
contentString += '<input type="color" id="prop_' + this.Selected[0].Properties[a].Name + '" value="' + this.Selected[0].Properties[a].CurrentValue + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected[0].Properties[a].Name + String.fromCharCode(39) + ');">';
|
||||
break;
|
||||
case "list":
|
||||
contentString += '<select id="prop_' + this.Selected[0].Properties[a].Name + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected[0].Properties[a].Name + String.fromCharCode(39) + ');">';
|
||||
for (let b = 0; b < this.Selected[0].Properties[a].Values.length; b++) {
|
||||
contentString += '<option value="' + this.Selected[0].Properties[a].Values[b].Value + '" '+ ((this.Selected[0].Properties[a].Values[b].Value == this.Selected[0].Properties[a].CurrentValue) ? ' selected' : '') + '>' + this.Selected[0].Properties[a].Values[b].String + '</option>';
|
||||
}
|
||||
contentString += '</select>';
|
||||
break;
|
||||
}
|
||||
contentString += "</td></tr>";
|
||||
}
|
||||
PropertiesBoxContent.innerHTML = contentString;
|
||||
PropertiesBox.style.display = "block";
|
||||
}
|
||||
|
||||
checkMouseBounds(mousePos) {
|
||||
// We go backwards so that the newest (highest drawn) element is clicked before one lower.
|
||||
for (let a = (this.Elements.length - 1); a >= 0; a--) {
|
||||
if (this.Elements[a].mouseInside(mousePos)) return this.Elements[a];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
checkOverlayBounds(x,y,width,height) {
|
||||
for (let a = 0; a < this.Elements.length; a++) {
|
||||
if ((x >= this.Elements[a].X) && (x <= (this.Elements[a].X + this.Elements[a].Width)) && (y >= this.Elements[a].Y) && (y <= (this.Elements[a].Y + this.Elements[a].Height))) return this.Elements[a];
|
||||
if (((x + width) >= this.Elements[a].X) && ((x + width) <= (this.Elements[a].X + this.Elements[a].Width)) && (y >= this.Elements[a].Y) && (y <= (this.Elements[a].Y + this.Elements[a].Height))) return this.Elements[a];
|
||||
if ((x >= this.Elements[a].X) && (x <= (this.Elements[a].X + this.Elements[a].Width)) && ((y + height) >= this.Elements[a].Y) && ((y + height) <= (this.Elements[a].Y + this.Elements[a].Height))) return this.Elements[a];
|
||||
if (((x + width) >= this.Elements[a].X) && ((x + width) <= (this.Elements[a].X + this.Elements[a].Width)) && ((y + height) >= this.Elements[a].Y) && ((y + height) <= (this.Elements[a].Y + this.Elements[a].Height))) return this.Elements[a];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ElementCatalog_Category {
|
||||
constructor(name,icon) {
|
||||
this.Name = name;
|
||||
this.Icon = icon;
|
||||
this.Elements = new Array();
|
||||
}
|
||||
addElement(element) {
|
||||
if (element instanceof ElementCatalog_Element) {
|
||||
this.Elements.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ElementCatalog_Element {
|
||||
constructor(name,description,icon,classref,args) {
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Icon = icon;
|
||||
this.Class = classref;
|
||||
this.Args = args;
|
||||
}
|
||||
}
|
||||
|
||||
class ElementCatalog {
|
||||
constructor(categories = new Array()) {
|
||||
this.Categories = categories;
|
||||
for (let a = 0; a < this.Categories.length; a++) {
|
||||
if (!this.Categories[a] instanceof ElementCatalog_Category) {
|
||||
this.Categories.splice(a,1);
|
||||
a--;
|
||||
}
|
||||
}
|
||||
}
|
||||
addCategory(category) {
|
||||
if (category instanceof ElementCatalog_Category) {
|
||||
this.Categories.push(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,519 +0,0 @@
|
||||
let ElementReferenceTable = new Array();
|
||||
|
||||
let ElementCategory_Inputs = new ElementCatalog_Category("Inputs","");
|
||||
let ElementCategory_Outputs = new ElementCatalog_Category("Outputs","");
|
||||
let ElementCategory_LOGIC = new ElementCatalog_Category("Logic" ,"");
|
||||
let ElementCategory_FlipFlop = new ElementCatalog_Category("Flip-Flops" ,"");
|
||||
let ElementCategory_Timing = new ElementCatalog_Category("Timing" ,"");
|
||||
let ElementCategory_ICs = new ElementCatalog_Category("ICs" ,"");
|
||||
let ElementCategory_Other = new ElementCatalog_Category("Other" ,"");
|
||||
let elementCatalog = new ElementCatalog([ElementCategory_Inputs,
|
||||
ElementCategory_Outputs,
|
||||
ElementCategory_LOGIC,
|
||||
ElementCategory_FlipFlop,
|
||||
ElementCategory_Timing,
|
||||
ElementCategory_ICs,
|
||||
ElementCategory_Other]);
|
||||
|
||||
class ElementProperty {
|
||||
constructor(name,type,callback,defaultValue,currentValue = false,values=false,min=0,max=64) {
|
||||
/*
|
||||
Types
|
||||
---------------------------------------
|
||||
bool Boolean Values
|
||||
int Integer Value
|
||||
string String Value
|
||||
list Dropdown box of values
|
||||
|
||||
Callback is an object of:
|
||||
---------------------------------------
|
||||
CBObject Object to call function on
|
||||
CBFunction The function
|
||||
|
||||
*/
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
this.Callback = callback;
|
||||
this.DefaultValue = defaultValue;
|
||||
if (!currentValue) currentValue = defaultValue;
|
||||
this.CurrentValue = currentValue;
|
||||
this.Values = values;
|
||||
if (!values) this.Values = new Array();
|
||||
this.Minimium = min;
|
||||
this.Maximium = max;
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
return {
|
||||
Name: this.Name,
|
||||
DefaultValue: this.DefaultValue,
|
||||
CurrentValue: this.CurrentValue,
|
||||
Values: this.Values,
|
||||
Minimium: this.Minimium,
|
||||
Maximium: this.Maximium
|
||||
};
|
||||
}
|
||||
|
||||
Call(value) {
|
||||
this.Callback.CBObject[this.Callback.CBFunction](value);
|
||||
}
|
||||
}
|
||||
|
||||
class ElementConnection {
|
||||
constructor(elementContainer,element,input,output = 0) {
|
||||
this.Container = elementContainer;
|
||||
this.Element = element;
|
||||
this.Input = input;
|
||||
this.Output = output;
|
||||
}
|
||||
toJSON(key) {
|
||||
return {
|
||||
Element: this.Element.Designator,
|
||||
Input: parseInt(this.Input),
|
||||
Output: this.Output
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Element extends CanvasTools {
|
||||
|
||||
constructor(_Container,RestoreData = null,logicengine,Inputs) {
|
||||
super();
|
||||
this.Name = "Element";
|
||||
this.Designator = "";
|
||||
this.Inputs = new Array(Inputs);
|
||||
this.InputLabels = new Array(1);
|
||||
this.Width = 100;
|
||||
this.Height = 60;
|
||||
this.inputCircleRadius = 10;
|
||||
this.outputCircleRadius = 10;
|
||||
this.X = 0;
|
||||
this.Y = 0;
|
||||
this.OutputConnections = new Array();
|
||||
this.MouseOver = false;
|
||||
this.MousePosition = {x: 0, y: 0};
|
||||
this.Properties = new Array();
|
||||
this.LogicEngine = logicengine;
|
||||
this.Outputs = new Array(1);
|
||||
this.OutputLabels = new Array(1);
|
||||
this.NoOutput = false;
|
||||
this.OutputLink = 0;
|
||||
this._Container = _Container;
|
||||
this.Disconnecting = false;
|
||||
this.redraw = true;
|
||||
this.StaticCanvas = document.createElement("canvas");
|
||||
this.StaticCtx = this.StaticCanvas.getContext('2d');
|
||||
|
||||
let inputProperty = new ElementProperty("Inputs","int",{CBObject: this,CBFunction: "ChangeInputs"},2,Inputs,false,2);
|
||||
this.Properties.push(inputProperty);
|
||||
if (RestoreData) {
|
||||
this.Designator = RestoreData.Designator;
|
||||
this.Width = RestoreData.Width;
|
||||
this.Height = RestoreData.Height;
|
||||
this.X = RestoreData.X;
|
||||
this.Y = RestoreData.Y;
|
||||
if (RestoreData.Properties.length > 0) {
|
||||
if (RestoreData.Properties[0].Name == "Inputs") {
|
||||
this.ChangeInputs(RestoreData.Properties[0].CurrentValue);
|
||||
this.Properties[0].DefaultValue = RestoreData.Properties[0].DefaultValue;
|
||||
this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
this.Properties[0].Values = RestoreData.Properties[0].Values;
|
||||
this.Properties[0].Minimium = RestoreData.Properties[0].Minimium;
|
||||
this.Properties[0].Maximium = RestoreData.Properties[0].Maximium;
|
||||
}
|
||||
}
|
||||
this.Inputs = RestoreData.Inputs;
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
clearStatic() {
|
||||
this.StaticCtx.clearRect(0, 0, this.StaticCanvas.width,this.StaticCanvas.height);
|
||||
}
|
||||
|
||||
ConnectsTo(element,input = false) {
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
if (this.OutputConnections[a].Element == element || this.OutputConnections[a].Element.Designator == element) {
|
||||
if (input !== false) {
|
||||
if (this.OutputConnections[a].Input == input) return this.OutputConnections[a].Output;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
let isvisible = false;
|
||||
if (this.LogicEngine) {
|
||||
let LeftX = this.LogicEngine.Panning.OffsetX;
|
||||
if (LeftX < 0) {
|
||||
LeftX = Math.abs(LeftX);
|
||||
} else {
|
||||
LeftX = -Math.abs(LeftX);
|
||||
}
|
||||
let RightX = LeftX + this.LogicEngine.Canvas.width;
|
||||
let TopY = this.LogicEngine.Panning.OffsetY;
|
||||
if (TopY < 0) {
|
||||
TopY = Math.abs(TopY);
|
||||
} else {
|
||||
TopY = -Math.abs(TopY);
|
||||
}
|
||||
let BottomY = TopY + this.LogicEngine.Canvas.height;
|
||||
if (((this.X + this.Width) >= LeftX) && ((this.X) <= RightX) && ((this.Y + this.Height) >= TopY) && ((this.Y) <= BottomY)) isvisible = true;
|
||||
}
|
||||
return isvisible;
|
||||
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
return {
|
||||
Name: this.Name,
|
||||
Designator: this.Designator,
|
||||
Inputs: this.Inputs,
|
||||
Outputs: this.OutputConnections,
|
||||
Output: this.getOutput(),
|
||||
Width: this.Width,
|
||||
Height: this.Height,
|
||||
X: this.X,
|
||||
Y: this.Y,
|
||||
Properties: this.Properties
|
||||
};
|
||||
}
|
||||
|
||||
getProperty(property) {
|
||||
for (let a = 0; a < this.Properties.length;a++) {
|
||||
if (this.Properties[a].Name == property) return this.Properties[a];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
removeProperty(property) {
|
||||
for (let a = 0; a < this.Properties.length;a++) {
|
||||
if (this.Properties[a].Name == property) {
|
||||
this.Properties.splice(a,1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
totalInputs() {
|
||||
return this.Inputs.length;
|
||||
}
|
||||
|
||||
ChangeInputs(inputs) {
|
||||
inputs = parseInt(inputs,10);
|
||||
this.Inputs = new Array(inputs);
|
||||
this.getProperty("Inputs").CurrentValue = inputs;
|
||||
this.Height = inputs*25;
|
||||
if (this.Height < 60) this.Height = 60;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
Delete() {
|
||||
// if we are active linking stop doing so
|
||||
if (this.LogicEngine.ActiveLink == this) this.LogicEngine.ActiveLink = false;
|
||||
// Just to clean up connections
|
||||
this.Disconnect();
|
||||
}
|
||||
|
||||
Disconnect(element = false) {
|
||||
if (element) {
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
if (this.OutputConnections[a].Element == element) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
let element = this.OutputConnections[a].Element;
|
||||
let input = this.OutputConnections[a].Input;
|
||||
this.OutputConnections.splice(a,1);
|
||||
element.setInput(input, false);
|
||||
a--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.Disconnecting = true;
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, false);
|
||||
this.OutputConnections.splice(a,1);
|
||||
a--;
|
||||
}
|
||||
this.Disconnecting = false;
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
MouseDown(mousePos) {
|
||||
return;
|
||||
}
|
||||
|
||||
MouseUp(mousePos) {
|
||||
return;
|
||||
}
|
||||
|
||||
LinkOutLocation() {
|
||||
return this.OutputLink;
|
||||
}
|
||||
|
||||
MouseClick(mousePos) {
|
||||
|
||||
let ctxMousePos = this.MousePosition;
|
||||
let mouseDistOutput = length2D(this.X+(this.Width-10),
|
||||
this.Y+(this.Height/2),
|
||||
ctxMousePos.x,
|
||||
ctxMousePos.y);
|
||||
if (this.LogicEngine.ActiveLink) {
|
||||
// We need to see if an input is being clicked on to be linked to
|
||||
let foundInput = false;
|
||||
for (let a = 0; a < this.Inputs.length;a++) {
|
||||
let centerY = this.Y + Math.round(this.Height / 2);
|
||||
let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
|
||||
let firstY = (centerY - (totalHeight/2)) + 12;
|
||||
|
||||
let mouseDist = length2D(this.X+10,
|
||||
firstY+ (a*24),
|
||||
ctxMousePos.x,
|
||||
ctxMousePos.y);
|
||||
if (mouseDist <= (this.inputCircleRadius)) {
|
||||
this.LogicEngine.Link(a);
|
||||
foundInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let foundOutput = false;
|
||||
for (let a = 0; a < this.Outputs.length;a++) {
|
||||
let centerY = this.Y + Math.round(this.Height / 2);
|
||||
let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
|
||||
let firstY = (centerY - (totalHeight/2)) + 12;
|
||||
|
||||
let mouseDist = length2D(this.X+(this.Width-10),
|
||||
firstY+ (a*24),
|
||||
ctxMousePos.x,
|
||||
ctxMousePos.y);
|
||||
if (mouseDist <= (this.outputCircleRadius)) {
|
||||
this.OutputLink = {Output: a,x: this.X+(this.Width-10), y: firstY+ (a*24)};
|
||||
this.LogicEngine.Link();
|
||||
foundOutput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mouseInside(mousePos) {
|
||||
mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
|
||||
let oldMouseOver = this.MouseOver;
|
||||
this.MouseOver = false;
|
||||
if (((mousePos.x >= this.X ) && (mousePos.x <= (this.X + this.Width))) && ((mousePos.y >= this.Y ) && (mousePos.y <= (this.Y + this.Height)))) this.MouseOver = true;
|
||||
this.MousePosition = mousePos;
|
||||
if (this.MouseOver) this.drawInputs(this.StaticCtx, 0,0);
|
||||
if (this.MouseOver) this.drawOutputs(this.StaticCtx, 0,0);
|
||||
if (oldMouseOver && !this.MouseOver) this.drawInputs(this.StaticCtx, 0,0); // Make sure we clear hover
|
||||
if (oldMouseOver && !this.MouseOver) this.drawOutputs(this.StaticCtx, 0,0);
|
||||
return this.MouseOver;
|
||||
}
|
||||
|
||||
addConnection(container, element, input,output=0) {
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
if (this.OutputConnections[a].Element == element && this.OutputConnections[a].Input == input) {
|
||||
// Already existing link, we will remove it instead
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections.splice(a,1);
|
||||
element.setInput(input,false);
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let newConnection = new ElementConnection(container,element,input,output);
|
||||
this.OutputConnections.push(newConnection);
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
element.setInput(input,this.getOutput(output));
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
return newConnection;
|
||||
}
|
||||
|
||||
drawInputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff",drawFresh = false) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
ctx.save();
|
||||
//this.inputCircleRadius = 10;
|
||||
let centerY = y + Math.round(this.Height / 2);
|
||||
let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
|
||||
let firstY = (centerY - (totalHeight/2)) + 12;
|
||||
let centerYReal = this.Y + Math.round(this.Height / 2);
|
||||
let firstYReal = (centerYReal - (totalHeight/2)) + 12;
|
||||
|
||||
for (let a = 0; a < this.totalInputs();a++) {
|
||||
let mouseDist = length2D(this.X+10, firstYReal + (a*24),this.MousePosition.x,this.MousePosition.y);
|
||||
ctx.beginPath();
|
||||
ctx.arc(x+10,firstY + (a*24),this.inputCircleRadius,0,2*Math.PI);
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = circleColorFalse;
|
||||
if (this.Inputs[a]) ctx.fillStyle = circleColorTrue;
|
||||
if ((mouseDist <= (this.inputCircleRadius)) && this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
let drawOverline = false;
|
||||
let label = this.InputLabels[a];
|
||||
|
||||
if (this.InputLabels[a] && drawFresh) {
|
||||
if (this.InputLabels[a].charAt(0) == "!" || this.InputLabels[a].charAt(0) == "~") {
|
||||
// Draw a NOT line
|
||||
drawOverline = true;
|
||||
label = this.InputLabels[a].substring(1);
|
||||
}
|
||||
this.drawText(ctx,x+(this.inputCircleRadius*2)+ 5,(firstY + (a*24)) + 5,label,"10px Console","#000",drawOverline);
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawOutputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff",drawFresh = false) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
ctx.save();
|
||||
let centerY = y + Math.round(this.Height / 2);
|
||||
let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
|
||||
let firstY = (centerY - (totalHeight/2)) + 12;
|
||||
let centerYReal = this.Y + Math.round(this.Height / 2);
|
||||
let firstYReal = (centerYReal - (totalHeight/2)) + 12;
|
||||
|
||||
for (let a = 0; a < this.Outputs.length;a++) {
|
||||
let mouseDist = length2D(this.X+(this.Width - 10), firstYReal + (a*24),this.MousePosition.x,this.MousePosition.y);
|
||||
ctx.beginPath();
|
||||
ctx.arc(x+(this.Width-10),firstY + (a*24),this.outputCircleRadius,0,2*Math.PI);
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = circleColorFalse;
|
||||
if (this.getOutput(a)) ctx.fillStyle = circleColorTrue;
|
||||
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
let textSize = false;
|
||||
let drawOverline = false;
|
||||
let label = this.OutputLabels[a];
|
||||
if (this?.OutputLabels[a]?.charAt(0) == "!" || this?.OutputLabels[a]?.charAt(0) == "~") {
|
||||
// Draw a NOT line
|
||||
drawOverline = true;
|
||||
label = this.OutputLabels[a].substring(1);
|
||||
}
|
||||
|
||||
if (this.OutputLabels[a]) textSize = this.textSize(ctx,label,"10px Console");
|
||||
if (this.OutputLabels[a] && drawFresh) {
|
||||
this.drawText(ctx,(x+(this.Width)) - (textSize.width + 5 + (this.outputCircleRadius*2)),(firstY + (a*24)) + 5,label,"10px Console","#000",drawOverline);
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawConnections(ctx,settings) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
let centerY = this.Y + Math.round(this.Height / 2);
|
||||
let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
|
||||
let firstY = (centerY - (totalHeight/2)) + 12;
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length;a++) {
|
||||
let mouseDist = length2D(this.X+(this.Width - 10), firstY + (this.OutputConnections[a].Output*24),this.MousePosition.x,this.MousePosition.y);
|
||||
if (!this.OutputConnections[a].Container.HasElement(this.OutputConnections[a].Element)) {
|
||||
// This is a ghosted connection, lets get rid of it
|
||||
this.OutputConnections.splice(a,1);
|
||||
a--;
|
||||
} else {
|
||||
let endCenterY = this.OutputConnections[a].Element.Y + Math.round(this.OutputConnections[a].Element.Height / 2);
|
||||
let endTotalHeight = this.OutputConnections[a].Element.totalInputs() * ((this.OutputConnections[a].Element.inputCircleRadius*2)+4);
|
||||
let endFirstY = (endCenterY - (endTotalHeight/2)) + 12;
|
||||
|
||||
let startX = this.X + this.Width;
|
||||
let startY = firstY+ (this.OutputConnections[a].Output*24);
|
||||
let endX = this.OutputConnections[a].Element.X;
|
||||
//let endY = this.OutputConnections[a].Element.Y+(this.OutputConnections[a].Element.inputCircleRadius + 2)+(((this.OutputConnections[a].Input*(4+(this.OutputConnections[a].Element.inputCircleRadius*2))))-2)+(this.OutputConnections[a].Element.inputCircleRadius/2);
|
||||
let endY = endFirstY + (this.OutputConnections[a].Input*24);
|
||||
let startMidX = startX + ((endX - startX)/2);
|
||||
let startMidY = startY;
|
||||
let midX = startMidX;
|
||||
let midY = startY + ((endY - startY)/2);
|
||||
let endMidX = startMidX;
|
||||
let endMidY = endY;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = settings.LinkWidth;
|
||||
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.lineWidth = (settings.LinkWidth * 2);
|
||||
ctx.setLineDash(settings.LinkDash);
|
||||
ctx.moveTo(startX, startY);
|
||||
//ctx.lineTo(endX, endY);
|
||||
ctx.quadraticCurveTo(startMidX,startMidY,midX,midY);
|
||||
ctx.quadraticCurveTo(endMidX,endMidY,endX,endY);
|
||||
ctx.strokeStyle = settings.ActiveConnectionColor;
|
||||
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.ActiveConnectionHoverColor;
|
||||
if (!this.getOutput(this.OutputConnections[a].Output)) {
|
||||
ctx.strokeStyle = settings.InactiveConnectionColor;
|
||||
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.InactiveConnectionHoverColor;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setConnections() {
|
||||
for (let a = 0; a < this.OutputConnections.length;a++) {
|
||||
this.LogicEngine.RecursionCount++;
|
||||
if (this.LogicEngine.RecursionCount > 1000) {
|
||||
if (!this.LogicEngine.RecursionError) {
|
||||
console.log("RECURSION ERROR");
|
||||
this.LogicEngine.RecursionError = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput(this.OutputConnections[a].Output));
|
||||
this.LogicEngine.RecursionCount--;
|
||||
}
|
||||
}
|
||||
setInput(Input,Value) {
|
||||
if (Value) {
|
||||
Value = true;
|
||||
} else {
|
||||
Value = false;
|
||||
}
|
||||
let oldInput = this.Inputs[Input];
|
||||
if (Input < this.totalInputs()) {
|
||||
this.Inputs[Input] = Value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let isHigh = this._Container.isHigh(this,Input);
|
||||
if (isHigh !== false) this.Inputs[Input] = true;
|
||||
if (isHigh === false) this.Inputs[Input] = false;
|
||||
if (oldInput != this.Inputs[Input]) {
|
||||
this.setConnections();
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
/*
|
||||
Should return true or false
|
||||
*/
|
||||
if (this.Disconnecting) return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
/*
|
||||
Draw routine for the element
|
||||
*/
|
||||
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx,x+10,y,this.Width-20,this.Height);
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,"LOGIC");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
@ -1,547 +0,0 @@
|
||||
class LogicWireNode extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Name = "WireNode";
|
||||
this.Width = 20;
|
||||
this.Height = 20;
|
||||
}
|
||||
getOutput(Output = 0) {
|
||||
if (Output > 0) return false;
|
||||
return this.Inputs[0];
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_WireNode = new ElementCatalog_Element("WireNode","A simple compact wire node for organizing wiring","-O-",LogicWireNode,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_WireNode);
|
||||
ElementCategory_Other.addElement(ElementCatalog_WireNode);
|
||||
|
||||
class LogicAND extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
||||
super(_Container, RestoreData,logicengine,Inputs);
|
||||
this.Name = "AND";
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
let ANDResult = true;
|
||||
for (let a = 0; a < this.totalInputs();a++) {
|
||||
if (!this.Inputs[a]) ANDResult = false;
|
||||
}
|
||||
return ANDResult;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
//this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height);
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+xOffset,y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - xOffset),y,x+(this.Width-xOffset),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - xOffset),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_AND = new ElementCatalog_Element("AND","The AND gate only outputs high when all of the inputs are high.","&",LogicAND,[2]);
|
||||
ElementReferenceTable.push(ElementCatalog_AND);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_AND);
|
||||
|
||||
class LogicNAND extends LogicAND {
|
||||
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
||||
super(_Container, RestoreData,logicengine,Inputs);
|
||||
this.Name = "NAND";
|
||||
this.Width = 110;
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (this.Disconnecting) return false;
|
||||
if (super.getOutput()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+xOffset,y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = "1";
|
||||
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_NAND = new ElementCatalog_Element("NAND","The NAND gate always outputs a high signal unless all the inputs are high then it goes low.","!&",LogicNAND,[2]);
|
||||
ElementReferenceTable.push(ElementCatalog_NAND);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_NAND);
|
||||
|
||||
class LogicOR extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
||||
super(_Container, RestoreData,logicengine,Inputs);
|
||||
this.Name = "OR";
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
let ORResult = false;
|
||||
for (let a = 0; a < this.totalInputs();a++) {
|
||||
if (this.Inputs[a]) ORResult = true;
|
||||
}
|
||||
return ORResult;
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawWidth = this.Width;
|
||||
let drawHeight = this.Height;
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_OR = new ElementCatalog_Element("OR","The OR gate outputs a high when any input is HIGH.","|",LogicOR,[2]);
|
||||
ElementReferenceTable.push(ElementCatalog_OR);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_OR);
|
||||
|
||||
class LogicNOR extends LogicOR {
|
||||
constructor(_Container, RestoreData = null, logicengine,Inputs) {
|
||||
super(_Container, RestoreData,logicengine,Inputs);
|
||||
this.Name = "NOR";
|
||||
this.Width = 110;
|
||||
}
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
if (super.getOutput()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = "1";
|
||||
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_NOR = new ElementCatalog_Element("NOR","The NOR gate outputs a high only when all inputs are low.","!|",LogicNOR,[2]);
|
||||
ElementReferenceTable.push(ElementCatalog_NOR);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_NOR);
|
||||
|
||||
class LogicXOR extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
|
||||
this.Name = "XOR";
|
||||
this.removeProperty("Inputs");
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
let ORResult = false;
|
||||
if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
|
||||
return ORResult;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawWidth = this.Width;
|
||||
let drawHeight = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.lineWidth = "2";
|
||||
ctx.beginPath();
|
||||
ctx.shadowColor = "transparent";
|
||||
ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_XOR = new ElementCatalog_Element("XOR","The XOR gate outputs a high when only 1 input is high.","^",LogicXOR,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_XOR);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_XOR);
|
||||
|
||||
class LogicXNOR extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
|
||||
this.Name = "XNOR";
|
||||
this.removeProperty("Inputs");
|
||||
this.Width = 110;
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
let ORResult = false;
|
||||
if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
|
||||
return !ORResult;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawWidth = this.Width;
|
||||
let drawHeight = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y);
|
||||
ctx.lineTo((x+xOffset)+ this.Width/4,y);
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
|
||||
ctx.lineTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = "1";
|
||||
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.lineWidth = "2";
|
||||
ctx.beginPath();
|
||||
ctx.shadowColor = "transparent";
|
||||
ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+(xOffset/4),y+this.Height);
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
|
||||
ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
|
||||
this.drawTextCentered(ctx,x,y,this.Width-(xOffset/2),this.Height,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
|
||||
let ElementCatalog_XNOR = new ElementCatalog_Element("XNOR","The XNOR gate outputs a high only when both inputs are either low or high.","!^",LogicXNOR,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_XNOR);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_XNOR);
|
||||
|
||||
class LogicNOT extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1); // Only 1 inputs on NOT
|
||||
this.Name = "NOT";
|
||||
this.removeProperty("Inputs");
|
||||
this.Width = 110;
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
if (this.Inputs[0]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawWidth = this.Width;
|
||||
let drawHeight = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+xOffset,y);
|
||||
ctx.lineTo(x+(this.Width-(xOffset+10)),y+(this.Height/2));
|
||||
ctx.lineTo(x+xOffset,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.strokeStyle = "#000000";
|
||||
ctx.lineWidth = "1";
|
||||
ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_NOT = new ElementCatalog_Element("NOT","The NOT gate outputs the opposite of the input.","!",LogicNOT,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_NOT);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_NOT);
|
||||
|
||||
class LogicBuffer extends Element {
|
||||
ClockTick() {
|
||||
this.Output = this.Inputs[0];
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = 0;
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input > 0) return;
|
||||
//super.setInput(Input, Value);
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (Value !== false) Value = true;
|
||||
this.Inputs[Input] = Value;
|
||||
this.redraw = true;
|
||||
|
||||
if (!this.Task.Enabled) {
|
||||
this.Task.LastCall = 0;
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Output = false;
|
||||
this.Name = "Buffer";
|
||||
this.Width = 100;
|
||||
this.Task = new Task("BufferTask","Buffer",0,9999999,this.ClockTick.bind(this));
|
||||
this.Task.Time = 4;
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = 0;
|
||||
this.removeProperty("Inputs");
|
||||
if (RestoreData) this.Output = RestoreData.Output;
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawWidth = this.Width;
|
||||
let drawHeight = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
let shadowColor = this.LogicEngine.Settings.ShadowColor;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x+xOffset,y);
|
||||
ctx.lineTo(x+(this.Width-(xOffset)),y+(this.Height/2));
|
||||
ctx.lineTo(x+xOffset,y+this.Height);
|
||||
ctx.lineTo(x+xOffset,y);
|
||||
ctx.lineWidth = "3";
|
||||
ctx.fillStyle = "#f7e979";
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = "6";
|
||||
ctx.shadowOffsetX = 2;
|
||||
ctx.shadowOffsetY = 2;
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_BUFFER = new ElementCatalog_Element("Buffer","The buffer is a special gate to allow the prevention of recursion loops.","|>",LogicBuffer,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_BUFFER);
|
||||
ElementCategory_LOGIC.addElement(ElementCatalog_BUFFER);
|
||||
@ -1,172 +0,0 @@
|
||||
class LabelElement extends Element {
|
||||
constructor(_Container, RestoreData = null,logicengine) {
|
||||
super(_Container, RestoreData,logicengine,0);
|
||||
this.removeProperty("Inputs");
|
||||
this.Name = "Label";
|
||||
this.Font = "48px Console";
|
||||
this.FontStyle = "black";
|
||||
this.Label = this.Name;
|
||||
this.NoOutput = true;
|
||||
let LabelProperty = new ElementProperty("Label","string",{CBObject: this,CBFunction: "setLabel"},"Label","Label",false,1,255);
|
||||
this.Properties.push(LabelProperty);
|
||||
let LabelSize = new ElementProperty("Size","int",{CBObject: this,CBFunction: "setSize"},"48","48",false,6,99999);
|
||||
this.Properties.push(LabelSize);
|
||||
let LabelColor = new ElementProperty("Color","color",{CBObject: this,CBFunction: "setColor"},"#000000","#000000",false,0,0);
|
||||
this.Properties.push(LabelColor);
|
||||
|
||||
if (RestoreData) {
|
||||
this.Label = RestoreData.Properties[0].CurrentValue;
|
||||
this.Properties[0].CurrentValue = this.Label;
|
||||
this.Font = RestoreData.Properties[1].CurrentValue + "px Console";
|
||||
this.Properties[1].CurrentValue = RestoreData.Properties[1].CurrentValue;
|
||||
this.FontStyle = RestoreData.Properties[2].CurrentValue;
|
||||
this.Properties[2].CurrentValue = this.FontStyle;
|
||||
}
|
||||
|
||||
let textWidth = this.textSize(logicengine.Ctx,this.Label,this.Font);
|
||||
this.Width = textWidth.width;
|
||||
this.Height = textWidth.height;
|
||||
|
||||
}
|
||||
setLabel(label) {
|
||||
this.Label = label;
|
||||
this.Properties[0].CurrentValue = label;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
setSize(size) {
|
||||
this.Font = size + "px Console";
|
||||
this.Properties[1].CurrentValue = size;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
this.FontStyle = color;
|
||||
this.Properties[2].CurrentValue = color;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
|
||||
// No outputs to draw
|
||||
}
|
||||
|
||||
drawInputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
|
||||
// No outputs to draw
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
let textWidth = this.textSize(ctx,this.Label,this.Font);
|
||||
this.Width = textWidth.width;
|
||||
this.Height = textWidth.height;
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawTextCentered(ctx,x,y,textWidth.width,textWidth.height,this.Label,this.Font,this.FontStyle);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_Label = new ElementCatalog_Element("Label","Allows you to place a label","🏷",LabelElement,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_Label);
|
||||
ElementCategory_Other.addElement(ElementCatalog_Label);
|
||||
|
||||
class output4bitDisplay extends Element {
|
||||
constructor(_Container, RestoreData = null,logicengine) {
|
||||
super(_Container, RestoreData,logicengine,4);
|
||||
this.Name = "4B Display";
|
||||
this.Output = false;
|
||||
this.OutputChar = "0";
|
||||
this.NoOutput = true;
|
||||
this.Width = 100;
|
||||
this.Height = 100;
|
||||
this.Inputs = [0,0,0,0];
|
||||
this.InputLabels = ["I0","I1","I2","I3"];
|
||||
this.Type = 0;
|
||||
this.removeProperty("Inputs");
|
||||
let typeProperty = new ElementProperty("Type","list",{CBObject: this,CBFunction: "setType"},0,0,[{Value: "0", String: "Hex"},{Value: "1", String: "Decimal"},{Value: "2", String: "BCD"}],0,0);
|
||||
this.Properties.push(typeProperty);
|
||||
|
||||
if (RestoreData) {
|
||||
if (RestoreData.Properties.length > 0) {
|
||||
this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
this.Properties[0].Values = RestoreData.Properties[0].Values;
|
||||
this.setType(this.Properties[0].CurrentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setType(type) {
|
||||
this.Properties[0].CurrentValue = type;
|
||||
switch (type) {
|
||||
case "0":
|
||||
this.Type = 0;
|
||||
break;
|
||||
case "1":
|
||||
this.Type = 1;
|
||||
break;
|
||||
case "2":
|
||||
this.Type = 2;
|
||||
break;
|
||||
}
|
||||
this.setInput(0,this.Inputs[0]); // Trigger a proper recalc of the display.
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
this.Inputs[Input] = (Value !== false) ? 1 : 0;
|
||||
let outchar = (this.Inputs[3] << 3) + (this.Inputs[2] << 2) + (this.Inputs[1] << 1) + (this.Inputs[0]);
|
||||
|
||||
this.OutputChar = (outchar);
|
||||
switch (outchar) {
|
||||
case 10:
|
||||
if (this.Type == 0) this.OutputChar = 'A';
|
||||
if (this.Type == 1) this.OutputChar = '10';
|
||||
if (this.Type == 2) this.OutputChar = '0';
|
||||
break;
|
||||
case 11:
|
||||
if (this.Type == 0) this.OutputChar = 'B';
|
||||
if (this.Type == 1) this.OutputChar = '11';
|
||||
if (this.Type == 2) this.OutputChar = '1';
|
||||
break;
|
||||
case 12:
|
||||
if (this.Type == 0) this.OutputChar = 'C';
|
||||
if (this.Type == 1) this.OutputChar = '12';
|
||||
if (this.Type == 2) this.OutputChar = '2';
|
||||
break;
|
||||
case 13:
|
||||
if (this.Type == 0) this.OutputChar = 'D';
|
||||
if (this.Type == 1) this.OutputChar = '13';
|
||||
if (this.Type == 2) this.OutputChar = '3';
|
||||
break;
|
||||
case 14:
|
||||
if (this.Type == 0) this.OutputChar = 'E';
|
||||
if (this.Type == 1) this.OutputChar = '14';
|
||||
if (this.Type == 2) this.OutputChar = '4';
|
||||
break;
|
||||
case 15:
|
||||
if (this.Type == 0) this.OutputChar = 'F';
|
||||
if (this.Type == 1) this.OutputChar = '15';
|
||||
if (this.Type == 2) this.OutputChar = '5';
|
||||
break;
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
|
||||
// Do nothing, we want to overide
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height);
|
||||
let fontStyle = "72px Console";
|
||||
if (this.Type == 1) fontStyle = "36px Console";
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.OutputChar,fontStyle);
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
|
||||
let ElementCatalog_Output_4BDisplay = new ElementCatalog_Element("4B Display","A display! Takes a 4 bit input and shows hex value of the input.","[8]",output4bitDisplay,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_Output_4BDisplay);
|
||||
ElementCategory_Outputs.addElement(ElementCatalog_Output_4BDisplay);
|
||||
@ -1,321 +0,0 @@
|
||||
class FlipFlopJK extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,5);
|
||||
this.Name = "JK-FF";
|
||||
this.Outputs = new Array(2);
|
||||
this.InputLabels = new Array("J","!CLK","K","!PRE", "!CLR");
|
||||
this.OutputLabels = new Array("Q","~Q");
|
||||
this.removeProperty("Inputs");
|
||||
this.Height = 140;
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = true;
|
||||
if (RestoreData) {
|
||||
this.Outputs = RestoreData.OutputStates;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let $superjson = super.toJSON(key);
|
||||
|
||||
$superjson.OutputStates = this.Outputs;
|
||||
return $superjson;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input >= this.Inputs.length) return false;
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (Value !== false) Value = true;
|
||||
let oldOutput = this.Outputs[0];
|
||||
let oldOutput2 = this.Outputs[1];
|
||||
let oldInput2 = this.Inputs[1];
|
||||
let oldInput3 = this.Inputs[3];
|
||||
let oldInput4 = this.Inputs[4];
|
||||
this.Inputs[Input] = Value;
|
||||
|
||||
if (!this.Inputs[1] && oldInput2) {
|
||||
if (!this.Inputs[0] && this.Inputs[2]) {
|
||||
// set Q low
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
} else if (this.Inputs[0] && !this.Inputs[2]) {
|
||||
// set Q low
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
} else if (this.Inputs[0] && this.Inputs[2]) {
|
||||
// set Q low
|
||||
this.Outputs[0] = !this.Outputs[0];
|
||||
this.Outputs[1] = !this.Outputs[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.Inputs[4] && !oldInput4 && this.Outputs[0] && this.Outputs[1]) {
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (this.Inputs[3] && !oldInput3 && this.Outputs[0] && this.Outputs[1]) {
|
||||
this.Outputs[0] = false;
|
||||
}
|
||||
if (!this.Inputs[3] && oldInput3) {
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (!this.Inputs[4] && oldInput4) {
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
}
|
||||
if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
|
||||
this.setConnections();
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Outputs[Output];
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_JKFlipFlop = new ElementCatalog_Element("JK-FF","The JK Flip-Flop is a common type of flip-flop that allows for either setting in a specific state, or toggling state.","",FlipFlopJK,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_JKFlipFlop);
|
||||
ElementCategory_FlipFlop.addElement(ElementCatalog_JKFlipFlop);
|
||||
|
||||
class FlipFlopSR extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,5);
|
||||
this.Name = "SR-FF";
|
||||
this.Outputs = new Array(2);
|
||||
this.InputLabels = new Array("S","CLK","R","!PRE", "!CLR");
|
||||
this.OutputLabels = new Array("Q","~Q");
|
||||
this.removeProperty("Inputs");
|
||||
this.Height = 140;
|
||||
|
||||
if (RestoreData) {
|
||||
this.Outputs = RestoreData.OutputStates;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let $superjson = super.toJSON(key);
|
||||
|
||||
$superjson.OutputStates = this.Outputs;
|
||||
return $superjson;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input >= this.Inputs.length) return false;
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (Value !== false) Value = true;
|
||||
let oldOutput = this.Outputs[0];
|
||||
let oldOutput2 = this.Outputs[1];
|
||||
let oldInput1 = this.Inputs[1];
|
||||
let oldInput3 = this.Inputs[3];
|
||||
let oldInput4 = this.Inputs[4];
|
||||
|
||||
this.Inputs[Input] = Value;
|
||||
this.redraw = true;
|
||||
|
||||
if (this.Inputs[1] && !oldInput1) {
|
||||
if (!this.Inputs[0] && this.Inputs[2]) {
|
||||
// set Q low
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
} else if (this.Inputs[0] && !this.Inputs[2]) {
|
||||
// set Q high
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.Inputs[4] && !oldInput4 && !this.Inputs[3] && this.Outputs[1]) {
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (this.Inputs[3] && !oldInput3 && !this.Inputs[4] && this.Outputs[0]) {
|
||||
this.Outputs[0] = false;
|
||||
}
|
||||
|
||||
if (!this.Inputs[3] && oldInput3) {
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (!this.Inputs[4] && oldInput4) {
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
}
|
||||
if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
|
||||
this.setConnections();
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Outputs[Output];
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_SRFlipFlop = new ElementCatalog_Element("SR-FF","The SR Flip-Flop is a common type of flip-flop that allows for either setting, or resetting the output state.","",FlipFlopSR,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_SRFlipFlop);
|
||||
ElementCategory_FlipFlop.addElement(ElementCatalog_SRFlipFlop);
|
||||
|
||||
class FlipFlopT extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,4);
|
||||
this.Name = "T-FF";
|
||||
this.Outputs = new Array(2);
|
||||
this.InputLabels = new Array("T","CLK","!PRE","!CLR");
|
||||
this.OutputLabels = new Array("Q","~Q");
|
||||
this.removeProperty("Inputs");
|
||||
this.Height = 120;
|
||||
|
||||
if (RestoreData) {
|
||||
this.Outputs = RestoreData.OutputStates;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let $superjson = super.toJSON(key);
|
||||
|
||||
$superjson.OutputStates = this.Outputs;
|
||||
return $superjson;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input >= this.Inputs.length) return false;
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (Value !== false) Value = true;
|
||||
let oldOutput = this.Outputs[0];
|
||||
let oldOutput2 = this.Outputs[1];
|
||||
let oldInput2 = this.Inputs[2];
|
||||
let oldInput3 = this.Inputs[3];
|
||||
this.Inputs[Input] = Value;
|
||||
this.redraw = true;
|
||||
|
||||
if (this.Inputs[0] && this.Inputs[1]) {
|
||||
this.Outputs[0] = !this.Outputs[0];
|
||||
this.Outputs[1] = !this.Outputs[0];
|
||||
}
|
||||
|
||||
if (!this.Inputs[2] && oldInput2) {
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (!this.Inputs[3] && oldInput3) {
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
}
|
||||
|
||||
if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
|
||||
this.setConnections();
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Outputs[Output];
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_TFlipFlop = new ElementCatalog_Element("T-FF","The T Flip-Flop is a common type of flip-flop that toggles the output when T is high and CLK goes high.","",FlipFlopT,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_TFlipFlop);
|
||||
ElementCategory_FlipFlop.addElement(ElementCatalog_TFlipFlop);
|
||||
|
||||
class FlipFlopD extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,4);
|
||||
this.Name = "D-FF";
|
||||
this.Outputs = new Array(2);
|
||||
this.InputLabels = new Array("D","CLK","!PRE","!CLR");
|
||||
this.OutputLabels = new Array("Q","~Q");
|
||||
this.removeProperty("Inputs");
|
||||
this.Height = 120;
|
||||
|
||||
if (RestoreData) {
|
||||
this.Outputs = RestoreData.OutputStates;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let $superjson = super.toJSON(key);
|
||||
|
||||
$superjson.OutputStates = this.Outputs;
|
||||
return $superjson;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input >= this.Inputs.length) return false;
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (Value !== false) Value = true;
|
||||
let oldOutput = this.Outputs[0];
|
||||
let oldOutput2 = this.Outputs[1];
|
||||
let oldInput = this.Inputs[1];
|
||||
let oldInput2 = this.Inputs[2];
|
||||
let oldInput3 = this.Inputs[3];
|
||||
this.Inputs[Input] = Value;
|
||||
|
||||
if (this.Inputs[1] && !oldInput) {
|
||||
this.Outputs[0] = this.Inputs[0];
|
||||
this.Outputs[1] = !this.Outputs[0];
|
||||
}
|
||||
if (!this.Inputs[2] && oldInput2) {
|
||||
this.Outputs[0] = true;
|
||||
this.Outputs[1] = false;
|
||||
}
|
||||
if (!this.Inputs[3] && oldInput3) {
|
||||
this.Outputs[0] = false;
|
||||
this.Outputs[1] = true;
|
||||
}
|
||||
if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
|
||||
this.setConnections();
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Outputs[Output];
|
||||
}
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let xOffset = 20;
|
||||
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_DFlipFlop = new ElementCatalog_Element("D-FF","The D Flip-Flop is a common type of flip-flop that sets the output to equal D if the clock goes high","",FlipFlopD,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_DFlipFlop);
|
||||
ElementCategory_FlipFlop.addElement(ElementCatalog_DFlipFlop);
|
||||
@ -1,363 +0,0 @@
|
||||
class ICInput extends Element {
|
||||
setPinName(pinname) {
|
||||
// Dont actually need it to do anything
|
||||
this.Properties[0].CurrentValue = pinname;
|
||||
this.Output = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null,logicengine) {
|
||||
super(_Container, RestoreData ,logicengine,0);
|
||||
this.removeProperty("Inputs");
|
||||
this.Task = new Task("ICInputTask","CLOCK",0,1,this.ClockTick.bind(this));
|
||||
this.Name = "ICInput";
|
||||
this.Input = false;
|
||||
|
||||
let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
|
||||
this.Properties.push(pinNameProperty);
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
|
||||
if (RestoreData) {
|
||||
pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
setInput(notused = 0,value) {
|
||||
|
||||
if (notused > 0) return;
|
||||
|
||||
if (value === false) this.Input = false;
|
||||
if (value !== false) this.Input = true;
|
||||
this.Inputs[0] = this.Input;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
if (!this.Task.Enabled) {
|
||||
this.Task.LastCall = 0;
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
ClockTick() {
|
||||
this.Output = this.Input;
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = 0;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
/*
|
||||
Draw routine for the element
|
||||
*/
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawOverline = false;
|
||||
let label = (this.Properties[0].CurrentValue) ? this.Properties[0].CurrentValue.toString() : "";
|
||||
if (label.charAt(0) == "!" || label.charAt(0) == "~") {
|
||||
// Draw a NOT line
|
||||
drawOverline = true;
|
||||
label = label.substring(1);
|
||||
}
|
||||
|
||||
this.drawBorderBox(ctx,x+10,y,this.Width-30,this.Height);
|
||||
this.drawTextCentered(ctx,x,y+2,this.Width-10,14,this.Designator,"12px Console");
|
||||
this.drawTextCentered(ctx,x,y,this.Width-10,this.Height,label,"12px Console",undefined,drawOverline);
|
||||
this.drawOutputs(ctx,x,y);
|
||||
}
|
||||
|
||||
}
|
||||
let ElementCatalog_ICInput = new ElementCatalog_Element("ICInput","Allows for external signal to be applied to the design when used as an IC.","-<|",ICInput,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_ICInput);
|
||||
ElementCategory_ICs.addElement(ElementCatalog_ICInput);
|
||||
|
||||
class ICOutput extends Element {
|
||||
setPinName(pinname) {
|
||||
// Dont actually need it to do anything
|
||||
this.Properties[0].CurrentValue = pinname;
|
||||
this.Output = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null,logicengine) {
|
||||
super(_Container, RestoreData ,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Task = new Task("ICOutputTask","CLOCK",0,1,this.ClockTick.bind(this));
|
||||
this.Name = "ICOutput";
|
||||
this.Input = false;
|
||||
this.NoOutput = true;
|
||||
this.ParentIC = false;
|
||||
|
||||
let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
|
||||
this.Properties.push(pinNameProperty);
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
if (RestoreData) {
|
||||
pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setParent(element) {
|
||||
this.ParentIC = element;
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
setInput(notused = 0,value) {
|
||||
if (notused > 0) return;
|
||||
value = this._Container.isHigh(this,0);
|
||||
if (value === false) this.Input = false;
|
||||
if (value !== false) this.Input = true;
|
||||
this.Inputs[0] = this.Input;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
if (!this.Task.Enabled) {
|
||||
this.Task.LastCall = 0;
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
ClockTick() {
|
||||
this.Output = this.Input;
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = 0;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
|
||||
if (this.ParentIC) {
|
||||
this.ParentIC.drawOutputs(this.ParentIC.StaticCtx,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
drawConnections(ctx, settings) {
|
||||
// Don't draw connections from IC Outputs
|
||||
}
|
||||
|
||||
drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
|
||||
// Don't draw outputs
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
/*
|
||||
Draw routine for the element
|
||||
*/
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
let drawOverline = false;
|
||||
let label = (this.Properties[0].CurrentValue) ? this.Properties[0].CurrentValue.toString() : "";
|
||||
if (label.charAt(0) == "!" || label.charAt(0) == "~") {
|
||||
// Draw a NOT line
|
||||
drawOverline = true;
|
||||
label = label.substring(1);
|
||||
}
|
||||
|
||||
this.drawBorderBox(ctx,x+20,y,this.Width-10,this.Height);
|
||||
this.drawTextCentered(ctx,x+20,y+2,this.Width-10,14,this.Designator,"12px Console");
|
||||
this.drawTextCentered(ctx,x+20,y,this.Width-10,this.Height,label,"12px Console",undefined,drawOverline);
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_ICOutput = new ElementCatalog_Element("ICOutput","Allows for routing a signal externally when the design is used as an IC.","|>-",ICOutput,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_ICOutput);
|
||||
ElementCategory_ICs.addElement(ElementCatalog_ICOutput);
|
||||
|
||||
class ICElement extends Element {
|
||||
constructor(_Container, RestoreData = null, logicengine,ICSettings = null) {
|
||||
|
||||
|
||||
let newContainer = false;
|
||||
let icContainer = false;
|
||||
try {
|
||||
newContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
|
||||
icContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
|
||||
} catch (ex) {
|
||||
newContainer = loadContainer(JSON.parse(ICSettings.Container));
|
||||
icContainer = loadContainer(JSON.parse(ICSettings.Container));
|
||||
}
|
||||
ICSettings.Inputs = new Array();
|
||||
ICSettings.Outputs = new Array();
|
||||
|
||||
let inputLabels = new Array();
|
||||
let outputLabels = new Array();
|
||||
|
||||
for (let a = 0; a < newContainer.Elements.length; a++) {
|
||||
if (newContainer.Elements[a] instanceof ICInput) {
|
||||
let cConn = new ContainerConnection(null,newContainer,null,newContainer.Elements[a],0);
|
||||
inputLabels.push(newContainer.Elements[a]?.Properties[0]?.CurrentValue);
|
||||
ICSettings.Inputs.push(cConn);
|
||||
}
|
||||
if (newContainer.Elements[a] instanceof ICOutput) {
|
||||
let cConn = new ContainerConnection(newContainer,null,newContainer.Elements[a],null,0);
|
||||
outputLabels.push(newContainer.Elements[a]?.Properties[0]?.CurrentValue);
|
||||
ICSettings.Outputs.push(cConn);
|
||||
}
|
||||
}
|
||||
|
||||
let totalInputs = ICSettings.Inputs.length;
|
||||
let totalOutputs = ICSettings.Outputs.length;
|
||||
|
||||
super(_Container, RestoreData, logicengine, totalInputs);
|
||||
this.removeProperty("Inputs");
|
||||
this.ICBlueprint = icContainer;
|
||||
this.Outputs = ICSettings.Outputs;
|
||||
this.InputConnections = ICSettings.Inputs;
|
||||
this.InputLabels = inputLabels;
|
||||
this.OutputLabels = outputLabels;
|
||||
|
||||
this.Container = newContainer;
|
||||
this.Name = ICSettings.Name;
|
||||
this.Icon = "≡[°]≡";
|
||||
this.Width = 140;
|
||||
this.Height = (totalInputs >= totalOutputs) ? totalInputs*25 : totalOutputs*25;
|
||||
if (this.Height < 60) this.Height = 60;
|
||||
|
||||
for (let a = 0; a < this.Outputs.length; a++) {
|
||||
this.Outputs[a].fromElement.setParent(this);
|
||||
}
|
||||
|
||||
let editProperty = new ElementProperty("Edit","button",{CBObject: this,CBFunction: "editIC"},"Edit IC","Edit IC",false,0,0);
|
||||
this.Properties.push(editProperty);
|
||||
|
||||
if (RestoreData) {
|
||||
//console.log(RestoreData);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.IsIC = true;
|
||||
let eCat = getElementInfo(this.Name);
|
||||
superjson.Description = eCat.Description;
|
||||
superjson.ICOutputs = this.Outputs;
|
||||
superjson.InputConnections = this.InputConnections;
|
||||
superjson.ICBlueprint = this.ICBlueprint;
|
||||
superjson.ICContainer = this.Container;
|
||||
return superjson;
|
||||
}
|
||||
|
||||
editIC(value) {
|
||||
if (this.LogicEngine.StashedContainer) {
|
||||
this.LogicEngine.StashedContainer.push(this.LogicEngine.ActiveContainer);
|
||||
} else {
|
||||
this.LogicEngine.StashedContainer = new Array();
|
||||
this.LogicEngine.StashedContainer.push(this.LogicEngine.ActiveContainer);
|
||||
}
|
||||
|
||||
this.LogicEngine.ActiveContainer = this.ICBlueprint;
|
||||
this.LogicEngine.RedrawStatics();
|
||||
}
|
||||
|
||||
Disconnect(element = false) {
|
||||
super.Disconnect(element);
|
||||
if (element) {
|
||||
for (let a = 0; a < this.Outputs.length; a++) {
|
||||
if (this.Outputs[a].toElement == element || this.Outputs[a].fromElement == element) {
|
||||
if (this.Outputs[a].toElement == element) {
|
||||
// It's a too element, we need to tell it we are not high anymore
|
||||
} else {
|
||||
// Its a from element, we need to tell it's too element
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We are disconnecting all of our own connections
|
||||
for (let a = 0; a < this.Outputs.length; a++) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addConnection(container, element, input, output = 0) {
|
||||
super.addConnection(container, element, input, output);
|
||||
this.Outputs[output].toElementContainer = container;
|
||||
this.Outputs[output].toElement = element;
|
||||
this.Outputs[output].Input = input;
|
||||
this.Outputs[output].fromElement.addConnection(container,element,input,0);
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (this.InputConnections.length >= Input) {
|
||||
// No need to worry about recursion as this goes to an input element which is buffered
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
this.Inputs[Input] = (Value === false) ? false : true;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
this.InputConnections[Input].toElement.setInput(0,Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getOutput(Output = 0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
if (this.Outputs.length >= Output) {
|
||||
return this?.Outputs[Output]?.fromElement?.getOutput(this?.Outputs[Output]?.Input);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
drawElement(x,y,ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
/*
|
||||
Draw routine for the element
|
||||
*/
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.clearStatic();
|
||||
this.drawBorderBox(ctx,x+20,y,this.Width-40,this.Height);
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Icon,"12px Console");
|
||||
this.drawTextCentered(ctx,x,(y+this.Height)-14,this.Width,14,this.Designator,"10px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
@ -1,320 +0,0 @@
|
||||
class inputElement extends Element {
|
||||
constructor(_Container, RestoreData = null,logicengine) {
|
||||
super(_Container, RestoreData,logicengine,0);
|
||||
this.Name = "InputElement";
|
||||
this.Output = false;
|
||||
this.Width = 100;
|
||||
this.removeProperty("Inputs");
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
drawInputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
|
||||
// There are no inputs
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x,y,this.Width,this.Height);
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,"IN");
|
||||
this.drawOutputs(ctx,x,y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class InputSwitch extends inputElement {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine);
|
||||
this.Name = "Switch";
|
||||
this.Height = 70;
|
||||
|
||||
if (RestoreData) this.Output = RestoreData.Output;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
MouseClick(mousePos) {
|
||||
mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
|
||||
super.MouseClick(mousePos);
|
||||
if ((mousePos.x >= (this.X + 5)) && (mousePos.x <= (this.X + 55)) && (mousePos.y >= (this.Y + 5)) && (mousePos.y <= (this.Y + 55))) {
|
||||
this.Output = !this.Output;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
//this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
|
||||
this.drawBorderBox(ctx,x+5,y+25,50,10,1,"#ccc","#777");
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
|
||||
if (this.getOutput()) {
|
||||
this.drawBorderBox(ctx,x+15,y+5,30,25,1,"#ccc","#777");
|
||||
this.drawTextCentered(ctx,x,y+this.Height - 30,this.Width-(this.outputCircleRadius*2)-20,12,"OFF","12px Console","#000");
|
||||
} else {
|
||||
this.drawBorderBox(ctx,x+15,y+30,30,25,1,"#ccc","#777");
|
||||
this.drawTextCentered(ctx,x,y+7,this.Width-(this.outputCircleRadius*2)-20,12,"ON","12px Console","#000");
|
||||
}
|
||||
this.drawOutputs(ctx,x,y);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_SWITCH = new ElementCatalog_Element("Switch","The switch allows for toggling an output low or high.","|-",InputSwitch,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_SWITCH);
|
||||
ElementCategory_Inputs.addElement(ElementCatalog_SWITCH);
|
||||
|
||||
class InputButton extends inputElement {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine);
|
||||
this.Name = "Button";
|
||||
this.Height = 70;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
MouseDown(mousePos) {
|
||||
mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
|
||||
if ((mousePos.x >= (this.X + 5)) && (mousePos.x <= (this.X + 55)) && (mousePos.y >= (this.Y + 5)) && (mousePos.y <= (this.Y + 55))) {
|
||||
let old_output = this.Output;
|
||||
this.Output = true;
|
||||
this.LogicEngine.MouseDown = false; // Prevent movement on the button when its being pushed
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
if (old_output != this.Output) {
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseUp(mousePos) {
|
||||
let old_output = this.Output;
|
||||
this.Output = false;
|
||||
if (old_output != this.Output) {
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mouseInside(mousePos) {
|
||||
let mousestat = super.mouseInside(mousePos);
|
||||
if (this.Output && !this.MouseOver) {
|
||||
this.Output = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
return mousestat;
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
|
||||
this.drawOutputs(ctx,x,y);
|
||||
}
|
||||
}
|
||||
|
||||
let ElementCatalog_BUTTON = new ElementCatalog_Element("Button","The button only outputs high when the button is pressed.","[o]",InputButton,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_BUTTON);
|
||||
ElementCategory_Inputs.addElement(ElementCatalog_BUTTON);
|
||||
|
||||
class InputKeypad extends inputElement {
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData, logicengine);
|
||||
this.Name = "Keypad";
|
||||
this.Height = 210;
|
||||
this.Width = 200;
|
||||
|
||||
this.Outputs = new Array(false,false,false,false,false,false,false,false);
|
||||
this.OutputLabels = new Array("O0","O1","O2","O3","F1","F2","F3","Clr");
|
||||
this.ButtonRows = 5;
|
||||
this.ButtonColumns = 4;
|
||||
this.Buttons = new Array(
|
||||
{x: 0, y: 0,Value: 7, Text: "7"}, {x: 1, y: 0,Value: 8, Text: "8"}, {x: 2, y: 0,Value: 9, Text: "9"}, {x: 3, y: 0,Value: 16, Text: "F1"},
|
||||
{x: 0, y: 1,Value: 4, Text: "4"}, {x: 1, y: 1,Value: 5, Text: "5"}, {x: 2, y: 1,Value: 6, Text: "6"}, {x: 3, y: 1,Value: 32, Text: "F2"},
|
||||
{x: 0, y: 2,Value: 1, Text: "1"}, {x: 1, y: 2,Value: 2, Text: "2"}, {x: 2, y: 2,Value: 3, Text: "3"}, {x: 3, y: 2,Value: 64, Text: "F3"},
|
||||
{x: 0, y: 3,Value: 10, Text: "A"}, {x: 1, y: 3,Value: 0, Text: "0"}, {x: 2, y: 3,Value: 11, Text: "B"}, {x: 3, y: 3,Value: 128, Text: "Clr"},
|
||||
{x: 0, y: 1,Value: 12, Text: "C"}, {x: 1, y: 1,Value: 13, Text: "D"}, {x: 2, y: 1,Value: 14, Text: "E"}, {x: 3, y: 1,Value: 15, Text: "F"}
|
||||
);
|
||||
let typeProperty = new ElementProperty("Function Type","list",{CBObject: this,CBFunction: "setType"},true,true,[{Value: true, String: "Switch"},{Value: false, String: "Button"}],0,0);
|
||||
this.Properties.push(typeProperty);
|
||||
|
||||
if (RestoreData) {
|
||||
if (RestoreData.OutputValues) {
|
||||
this.Outputs = RestoreData.OutputValues;
|
||||
}
|
||||
if (RestoreData.Properties?.length > 0) {
|
||||
this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
}
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.OutputValues = this.Outputs;
|
||||
return superjson;
|
||||
}
|
||||
|
||||
setType(type) {
|
||||
if (type == "false") type = false;
|
||||
if (type == "true") type = true;
|
||||
this.Properties[0].CurrentValue = type;
|
||||
if (!type) {
|
||||
this.Outputs[4] = false;
|
||||
this.Outputs[5] = false;
|
||||
this.Outputs[6] = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseDown(mousePos) {
|
||||
if (!this.Properties[0].CurrentValue) {
|
||||
mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
|
||||
let buttonWidth = 34;
|
||||
let buttonHeight = 34;
|
||||
let x = this.X;
|
||||
let y = this.Y;
|
||||
|
||||
for (let pY = 0; pY < this.ButtonRows; pY++) {
|
||||
for (let pX = 0; pX < this.ButtonColumns; pX++) {
|
||||
|
||||
if ((mousePos.x >= (x + (5 * (pX + 1)) + (buttonWidth * pX))) && (mousePos.x <= ((x + (5 * (pX + 1)) + (buttonWidth * pX)) + buttonWidth)) && (mousePos.y >= (y + (5 * (pY + 1)) + (buttonHeight * pY))) && (mousePos.y <= ((y + (5 * (pY + 1)) + (buttonHeight * pY)) + buttonHeight))) {
|
||||
let button = this.Buttons[(pY * this.ButtonColumns) + pX];
|
||||
if (button.Value & 0b0010000) this.Outputs[4] = true;
|
||||
if (button.Value & 0b0100000) this.Outputs[5] = true;
|
||||
if (button.Value & 0b1000000) this.Outputs[6] = true;
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
MouseUp(mousePos) {
|
||||
if (!this.Properties[0].CurrentValue) {
|
||||
this.Outputs[4] = false;
|
||||
this.Outputs[5] = false;
|
||||
this.Outputs[6] = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
}
|
||||
|
||||
MouseClick(mousePos) {
|
||||
mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
|
||||
super.MouseClick(mousePos);
|
||||
let buttonWidth = 34;
|
||||
let buttonHeight = 34;
|
||||
let x = this.X;
|
||||
let y = this.Y;
|
||||
|
||||
for (let pY = 0; pY < this.ButtonRows; pY++) {
|
||||
for (let pX = 0; pX < this.ButtonColumns; pX++) {
|
||||
|
||||
if ((mousePos.x >= (x + (5*(pX+1))+(buttonWidth*pX))) && (mousePos.x <= ((x + (5*(pX+1))+(buttonWidth*pX)) + buttonWidth)) && (mousePos.y >= (y + (5*(pY+1))+(buttonHeight*pY))) && (mousePos.y <= ((y + (5*(pY+1))+(buttonHeight*pY))+buttonHeight))) {
|
||||
let button = this.Buttons[(pY*this.ButtonColumns) + pX];
|
||||
|
||||
if (button.Value <= 15) {
|
||||
if (button.Value & 0b0000001) this.Outputs[0] = true; else this.Outputs[0] = false;
|
||||
if (button.Value & 0b0000010) this.Outputs[1] = true; else this.Outputs[1] = false;
|
||||
if (button.Value & 0b0000100) this.Outputs[2] = true; else this.Outputs[2] = false;
|
||||
if (button.Value & 0b0001000) this.Outputs[3] = true; else this.Outputs[3] = false;
|
||||
}
|
||||
|
||||
if (this.Properties[0].CurrentValue) {
|
||||
if (button.Value & 0b0010000) this.Outputs[4] = !this.Outputs[4];
|
||||
if (button.Value & 0b0100000) this.Outputs[5] = !this.Outputs[5];
|
||||
if (button.Value & 0b1000000) this.Outputs[6] = !this.Outputs[6];
|
||||
}
|
||||
if (button.Value & 0b10000000) {
|
||||
for (let a = 0; a < this.Outputs.length; a++) {
|
||||
this.Outputs[a] = false;
|
||||
}
|
||||
this.Outputs[7] = true;
|
||||
let tempTimer = new Task("keypadClearTask","Keypad Clear",0,100,this.ClockTick.bind(this),true);
|
||||
tempTimer.LastCall = Date.now();
|
||||
tempTimer.Enabled = true;
|
||||
this.LogicEngine.Scheduler.addTask(tempTimer);
|
||||
}
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
ClockTick() {
|
||||
console.log("Keypad clear signal");
|
||||
this.Outputs[7] = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
|
||||
}
|
||||
}
|
||||
|
||||
getOutput(Output = 0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return (this.Outputs[Output]) ? true : false;
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
let buttonWidth = 34;
|
||||
let buttonHeight = 34;
|
||||
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
|
||||
for (let pY = 0; pY < this.ButtonRows; pY++) {
|
||||
for (let pX = 0; pX < this.ButtonColumns; pX++) {
|
||||
this.drawBorderBox(ctx, x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, 1, "#ccc", "#777");
|
||||
this.drawTextCentered(ctx,x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, this.Buttons[(pY*this.ButtonColumns) + pX].Text,"16px Console", "#fff");
|
||||
}
|
||||
}
|
||||
|
||||
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
|
||||
let ElementCatalog_Keypad = new ElementCatalog_Element("Keypad","A hex entry keypad with 4 bit output","[][]",InputKeypad,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_Keypad);
|
||||
ElementCategory_Inputs.addElement(ElementCatalog_Keypad);
|
||||
@ -1,398 +0,0 @@
|
||||
class ClockElement extends Element {
|
||||
ClockTick() {
|
||||
if (this.Inputs[0]) {
|
||||
this.Output = ~this.Output;
|
||||
if (this.Output) {
|
||||
this.Task.Time = Math.round(this.Period * this.Duty);
|
||||
} else {
|
||||
this.Task.Time = this.Period - Math.round(this.Period * this.Duty);
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
|
||||
super.setInput(Input, Value);
|
||||
|
||||
if (!this.Inputs[0]) {
|
||||
this.Output = false;
|
||||
this.Task.LastCall = 0;
|
||||
this.Task.Enabled = false;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
for (let a = 0; a < this.OutputConnections.length;a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
||||
}
|
||||
} else {
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Name = "Clock";
|
||||
this.Period = 1000;
|
||||
this.Duty = 0.5;
|
||||
this.Output = false;
|
||||
this.Width = 100;
|
||||
this.Task = new Task("ClockTask","CLOCK",0,Math.round(this.Period * this.Duty),this.ClockTick.bind(this));
|
||||
this.setInput(0,true);
|
||||
this.removeProperty("Inputs");
|
||||
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},1000,false,false,4,999999);
|
||||
let dutyProperty = new ElementProperty("Duty","int",{CBObject: this,CBFunction: "setDuty"},50,false,false,0,100);
|
||||
if (RestoreData) {
|
||||
if (this.Height != RestoreData.Height) {
|
||||
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
||||
this.Height = RestoreData.Height;
|
||||
}
|
||||
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
||||
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
periodProperty.Values = RestoreData.Properties[0].Values;
|
||||
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
||||
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
||||
dutyProperty.DefaultValue = RestoreData.Properties[1].DefaultValue;
|
||||
dutyProperty.CurrentValue = RestoreData.Properties[1].CurrentValue;
|
||||
dutyProperty.Values = RestoreData.Properties[1].Values;
|
||||
dutyProperty.Minimium = RestoreData.Properties[1].Minimium;
|
||||
dutyProperty.Maximium = RestoreData.Properties[1].Maximium;
|
||||
|
||||
this.Period = periodProperty.CurrentValue;
|
||||
this.Duty = dutyProperty.CurrentValue/100;
|
||||
this.Task.Enabled = RestoreData.Task.Enabled;
|
||||
this.Task.Time = RestoreData.Task.Time;
|
||||
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
||||
this.Task.CallCount = RestoreData.Task.CallCount;
|
||||
}
|
||||
this.Properties.push(periodProperty);
|
||||
this.Properties.push(dutyProperty);
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
setPeriod(period) {
|
||||
this.Period = period;
|
||||
this.Task.LastCall = 0;
|
||||
this.getProperty("Period").CurrentValue = period;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
setDuty(duty) {
|
||||
this.Duty = duty/100;
|
||||
this.Task.LastCall = 0;
|
||||
this.getProperty("Duty").CurrentValue = duty;
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms " + (this.Duty * 100) + "%","10px Console");
|
||||
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_CLOCK = new ElementCatalog_Element("Clock","The clock will output a pulse for the length of the duty cycle for a given period. I.E. if a 1000ms period with 50% duty cycle is given, the output will be high for 500ms and low for 500ms. The clock can be disabled with a low input, this also resets the clocks start time to allow for syncing.","🕑",ClockElement,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_CLOCK);
|
||||
ElementCategory_Timing.addElement(ElementCatalog_CLOCK);
|
||||
|
||||
class PulseElement extends Element {
|
||||
ClockTick() {
|
||||
this.Output = false;
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = Date.now();
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input > 0) return;
|
||||
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
//super.setInput(Input, Value);
|
||||
this.Inputs[Input] = (Value === false) ? false : true;
|
||||
|
||||
if (this.Inputs[0] && !this.Task.Enabled) {
|
||||
this.Output = true;
|
||||
for (let a = 0; a < this.OutputConnections.length;a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
||||
}
|
||||
this.Task.LastCall = Date.now();
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Name = "Pulse";
|
||||
this.Period = 100;
|
||||
this.Output = false;
|
||||
this.Width = 100;
|
||||
this.Task = new Task("PulseTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
|
||||
this.removeProperty("Inputs");
|
||||
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
|
||||
if (RestoreData) {
|
||||
if (this.Height != RestoreData.Height) {
|
||||
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
||||
this.Height = RestoreData.Height;
|
||||
}
|
||||
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
||||
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
periodProperty.Values = RestoreData.Properties[0].Values;
|
||||
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
||||
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
||||
|
||||
this.Period = periodProperty.CurrentValue;
|
||||
|
||||
this.Task.Enabled = RestoreData.Task.Enabled;
|
||||
this.Task.Time = RestoreData.Task.Time;
|
||||
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
||||
this.Task.CallCount = RestoreData.Task.CallCount;
|
||||
}
|
||||
|
||||
|
||||
this.Properties.push(periodProperty);
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
}
|
||||
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
|
||||
setPeriod(period) {
|
||||
this.Period = parseInt(period);
|
||||
this.Task.Time = parseInt(period);
|
||||
this.getProperty("Period").CurrentValue = parseInt(period);
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
|
||||
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_PULSE = new ElementCatalog_Element("Pulse","The pulse upon input will output a high only for as long as the period set, I.E. if a period of 1000ms is set then any input no matter how long, will trigger a pulse 1000ms long.","|\\__",PulseElement,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_PULSE);
|
||||
ElementCategory_Timing.addElement(ElementCatalog_PULSE);
|
||||
|
||||
class DelayElement extends Element {
|
||||
ClockTick() {
|
||||
if (this.Output) {
|
||||
this.Output = false;
|
||||
this.Task.Enabled = false;
|
||||
this.Task.LastCall = Date.now();
|
||||
this.Task.Time = this.Period;
|
||||
if (this.InputPulses.length > 0) {
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
} else {
|
||||
this.Output = true;
|
||||
this.Task.Enabled = false;
|
||||
if (this.InputPulses.length > 0) {
|
||||
this.Task.Time = this.InputPulses[0];
|
||||
this.Task.Enabled = true;
|
||||
this.InputPulses.splice(0,1);
|
||||
} else {
|
||||
if (this.InputEnd > 0) {
|
||||
this.Task.Time = this.InputEnd - this.InputStart;
|
||||
this.Task.Enabled = true;
|
||||
}
|
||||
}
|
||||
this.Task.LastCall = Date.now();
|
||||
}
|
||||
|
||||
for (let a = 0; a < this.OutputConnections.length; a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
Delete() {
|
||||
super.Delete();
|
||||
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
||||
}
|
||||
|
||||
getOutput(Output=0) {
|
||||
if (super.getOutput() === 0) return false;
|
||||
return this.Output;
|
||||
}
|
||||
|
||||
setInput(Input, Value) {
|
||||
if (Input > 0) return;
|
||||
Value = this._Container.isHigh(this,Input);
|
||||
if (this.Inputs[Input] == (Value === false) ? false : true) return;
|
||||
//super.setInput(Input, Value);
|
||||
this.Inputs[Input] = Value;
|
||||
|
||||
if (this.Inputs[0] && !this.Task.Enabled) {
|
||||
this.InputStart = Date.now();
|
||||
this.InputEnd = 0;
|
||||
this.Output = false;
|
||||
for (let a = 0; a < this.OutputConnections.length;a++) {
|
||||
this.LogicEngine.RecursionCount = 0;
|
||||
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
||||
}
|
||||
this.Task.LastCall = Date.now();
|
||||
this.Task.Enabled = true;
|
||||
} else {
|
||||
if (!this.Inputs[Input]) {
|
||||
this.InputEnd = Date.now();
|
||||
if (this.InputPulses.length > 0) {
|
||||
this.InputPulses.push(this.InputEnd - this.InputStart);
|
||||
this.InputStart = 0;
|
||||
this.InputEnd = 0;
|
||||
} else {
|
||||
if (!this.Task.Enabled) {
|
||||
this.Task.LastCall = Date.now();
|
||||
this.Task.Time = (this.InputEnd - this.InputStart) - (Date.now() - this.InputEnd);
|
||||
this.Task.Enabled = true;
|
||||
} else {
|
||||
this.InputPulses.push(this.InputEnd - this.InputStart);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We are in a condition where input went true, but the task is already enabled
|
||||
this.InputStart = Date.now();
|
||||
}
|
||||
}
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
constructor(_Container, RestoreData = null, logicengine) {
|
||||
super(_Container, RestoreData,logicengine,1);
|
||||
this.removeProperty("Inputs");
|
||||
this.Name = "Delay";
|
||||
this.Period = 100;
|
||||
this.Output = false;
|
||||
this.Width = 100;
|
||||
this.Task = new Task("DelayTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
|
||||
this.Task.Enabled = false;
|
||||
this.removeProperty("Inputs");
|
||||
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
|
||||
if (RestoreData) {
|
||||
if (this.Height != RestoreData.Height) {
|
||||
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
||||
this.Height = RestoreData.Height;
|
||||
}
|
||||
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
||||
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
||||
periodProperty.Values = RestoreData.Properties[0].Values;
|
||||
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
||||
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
||||
|
||||
this.Period = periodProperty.CurrentValue;
|
||||
|
||||
this.Task.Enabled = RestoreData.Task.Enabled;
|
||||
this.Task.Time = RestoreData.Task.Time;
|
||||
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
||||
this.Task.CallCount = RestoreData.Task.CallCount;
|
||||
}
|
||||
this.Properties.push(periodProperty);
|
||||
this.InputStart = 0;
|
||||
this.InputEnd = 0;
|
||||
this.InputPulses = new Array();
|
||||
this.Inputs[0] = false;
|
||||
this.LogicEngine.Scheduler.addTask(this.Task);
|
||||
}
|
||||
toJSON(key) {
|
||||
let superjson = super.toJSON(key);
|
||||
superjson.InputStart = this.InputStart;
|
||||
superjson.InputEnd = this.InputEnd;
|
||||
superjson.InputPulses = this.InputPulses;
|
||||
superjson.Task = {
|
||||
Enabled: this.Task.Enabled,
|
||||
Time: this.Task.Time,
|
||||
LastCall: Date.now() - this.Task.LastCall,
|
||||
CallCount: this.Task.CallCount
|
||||
};
|
||||
return superjson;
|
||||
}
|
||||
|
||||
setPeriod(period) {
|
||||
this.Period = parseInt(period);
|
||||
this.Task.Time = parseInt(period);
|
||||
this.getProperty("Period").CurrentValue = parseInt(period);
|
||||
this.drawElement(0,0,this.StaticCtx);
|
||||
}
|
||||
|
||||
drawElement(x, y, ctx) {
|
||||
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
||||
this.StaticCanvas.width = this.Width;
|
||||
this.StaticCanvas.height = this.Height;
|
||||
|
||||
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
||||
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
|
||||
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms " + "(" + this.InputPulses.length + ")","10px Console");
|
||||
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
||||
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
||||
}
|
||||
}
|
||||
let ElementCatalog_DELAY = new ElementCatalog_Element("Delay","The delay element will delay any input signal for the given period then output it for the same length of time it came in. Inputs will be buffered and played back out at the period rate.","__|\\",DelayElement,[]);
|
||||
ElementReferenceTable.push(ElementCatalog_DELAY);
|
||||
ElementCategory_Timing.addElement(ElementCatalog_DELAY);
|
||||
@ -1,526 +0,0 @@
|
||||
function addElement(RestoreData = null,refClass,props) {
|
||||
let newElement = new refClass(logicEngine.ActiveContainer,RestoreData,logicEngine,...props);
|
||||
|
||||
if (!RestoreData) {
|
||||
let x = Math.round(logicEngine.Canvas.width / 2) - (newElement.Width/2);
|
||||
let y = Math.round(logicEngine.Canvas.height / 2) - (newElement.Height/2);
|
||||
x -= logicEngine.Panning.OffsetX;
|
||||
y -= logicEngine.Panning.OffsetY;
|
||||
x = logicEngine.Settings.GridSize * Math.round(x/logicEngine.Settings.GridSize);
|
||||
y = logicEngine.Settings.GridSize * Math.round(y/logicEngine.Settings.GridSize);
|
||||
let width = newElement.Width;
|
||||
let height = newElement.Height;
|
||||
|
||||
let noclearspot = true;
|
||||
let findattempt = 0;
|
||||
while (noclearspot && findattempt < 100) {
|
||||
findattempt++;
|
||||
if (!logicEngine.ActiveContainer.checkOverlayBounds(x, y, width, height)) {
|
||||
noclearspot = false;
|
||||
} else {
|
||||
x += Math.floor(Math.random() * (width - (width - (width * 2)))) + (width - (width * 2));
|
||||
y += Math.floor(Math.random() * (height - (height - (height * 2)))) + (height - (height * 2));
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x + width > logicEngine.Canvas.width) x = logicEngine.Canvas.width - width;
|
||||
if (y + height > logicEngine.Canvas.height) y = logicEngine.Canvas.height - height;
|
||||
x = logicEngine.Settings.GridSize * Math.round(x/logicEngine.Settings.GridSize);
|
||||
y = logicEngine.Settings.GridSize * Math.round(y/logicEngine.Settings.GridSize);
|
||||
}
|
||||
}
|
||||
newElement.X = x;
|
||||
newElement.Y = y;
|
||||
}
|
||||
logicEngine.ActiveContainer.AddElement(newElement);
|
||||
logicEngine.ActiveContainer.Select(newElement);
|
||||
|
||||
logicEngine.MovingElement = new Array(logicEngine.ActiveContainer.Selected.length);
|
||||
|
||||
for (let b = 0; b < logicEngine.ActiveContainer.Selected.length; b++) {
|
||||
logicEngine.ActiveContainer.Selected[b].X = (logicEngine.Mouse.x - logicEngine.Panning.OffsetX) - (logicEngine.ActiveContainer.Selected[b].Width/2);
|
||||
logicEngine.ActiveContainer.Selected[b].Y = (logicEngine.Mouse.y - logicEngine.Panning.OffsetY) - (logicEngine.ActiveContainer.Selected[b].Height/2);
|
||||
logicEngine.MovingElement[b] = {
|
||||
StartX: logicEngine.ActiveContainer.Selected[b].X,
|
||||
StartY: logicEngine.ActiveContainer.Selected[b].Y
|
||||
};
|
||||
logicEngine.MovingElementMouseStartX = logicEngine.Mouse.x;
|
||||
logicEngine.MovingElementMouseStartY = logicEngine.Mouse.y;
|
||||
}
|
||||
let rect = logicEngine.Canvas.getBoundingClientRect();
|
||||
let realMouseX = logicEngine.Mouse.x + rect.left;
|
||||
let realMouseY = logicEngine.Mouse.y + rect.top;
|
||||
|
||||
logicEngine.MouseDown = true;
|
||||
logicEngine.DrawOffCanvasElement({clientX: realMouseX, clientY: realMouseY});
|
||||
disableSelectedMenus(false);
|
||||
return newElement;
|
||||
}
|
||||
|
||||
function createIC(container,name,description) {
|
||||
let newContainer = false;
|
||||
if (container instanceof elementContainer) newContainer = loadContainer(JSON.parse(JSON.stringify(container)));
|
||||
if (typeof container == 'string' || container instanceof String) newContainer = loadContainer(JSON.parse(container));
|
||||
if (!newContainer) return false;
|
||||
|
||||
let ICSettings = {
|
||||
Name: name.replace('^[a-zA-Z0-9_]+$',''),
|
||||
Description: description,
|
||||
Container: JSON.stringify(container),
|
||||
};
|
||||
|
||||
let outputCount = 0;
|
||||
for (let a = 0; a < newContainer.Elements.length; a++) {
|
||||
if (newContainer.Elements[a] instanceof ICOutput) {
|
||||
outputCount++;
|
||||
}
|
||||
}
|
||||
if (outputCount == 0) return false;
|
||||
let ElementCatalog_IC = new ElementCatalog_Element(ICSettings.Name,ICSettings.Description,"≡█≡",ICElement,[ICSettings]);
|
||||
ElementReferenceTable.push(ElementCatalog_IC);
|
||||
ElementCategory_ICs.addElement(ElementCatalog_IC);
|
||||
BuildToolbox();
|
||||
return ElementCatalog_IC;
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
let d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
let expires = "expires="+ d.toUTCString();
|
||||
document.cookie = cname + '="' + cvalue + '";' + expires;
|
||||
localStorage.setItem(cname,cvalue);
|
||||
}
|
||||
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for(var i = 0; i <ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return localStorage.getItem(cname);
|
||||
}
|
||||
|
||||
function CheckForWelcomeCookie() {
|
||||
if (getCookie("hidewelcomescreen")) {
|
||||
let WelcomeScreen = document.getElementById("WelcomeWindow");
|
||||
let DarkOverlay = document.getElementById("darkout-overlay");
|
||||
WelcomeScreen.style.display = "none";
|
||||
DarkOverlay.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function getMousePos(canvas, evt) {
|
||||
let rect = canvas.getBoundingClientRect();
|
||||
return {
|
||||
x: evt.clientX - rect.left,
|
||||
y: evt.clientY - rect.top
|
||||
};
|
||||
}
|
||||
|
||||
function averageArray(arr) {
|
||||
return arr.reduce((a, b) => (a + b)) / arr.length;
|
||||
}
|
||||
|
||||
function length2D(x1,y1,x2,y2) {
|
||||
let xDist = x1 - x2;
|
||||
let yDist = y1 - y2;
|
||||
return Math.sqrt(xDist*xDist + yDist * yDist);
|
||||
}
|
||||
|
||||
function MatchesPress(binding,evt) {
|
||||
let retval = true;
|
||||
if (binding?.Key == evt.key.toLowerCase()) {
|
||||
if (binding?.Alt) {
|
||||
if (!evt.altKey) retval = false;
|
||||
} else {
|
||||
if (evt.altKey) retval = false;
|
||||
}
|
||||
if (binding?.Ctrl) {
|
||||
if (!evt.ctrlKey) retval = false;
|
||||
} else {
|
||||
if (evt.ctrlKey) retval = false;
|
||||
}
|
||||
if (binding?.Shift) {
|
||||
if (!evt.shiftKey) retval = false;
|
||||
} else {
|
||||
if (evt.shiftKey) retval = false;
|
||||
}
|
||||
if (binding?.Meta) {
|
||||
if (!evt.metaKey) retval = false;
|
||||
} else {
|
||||
if (evt.metaKey) retval = false;
|
||||
}
|
||||
} else {
|
||||
retval = false;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
function KeybindToString(binding) {
|
||||
let kbs = "";
|
||||
if (binding?.Meta) kbs += "Meta+";
|
||||
if (binding?.Ctrl) kbs += "Ctrl+";
|
||||
if (binding?.Alt) kbs += "Alt+";
|
||||
if (binding?.Shift) kbs += "Shift+";
|
||||
kbs += binding?.Key.toUpperCase();
|
||||
return kbs;
|
||||
}
|
||||
|
||||
function GetCopyObject() {
|
||||
if (logicEngine?.ActiveContainer?.Selected?.length > 0) {
|
||||
let copyObject = {Paste: true,LogicParts: Version};
|
||||
copyObject.Elements = logicEngine.ActiveContainer.Selected;
|
||||
let copyObjectString = JSON.stringify(copyObject);
|
||||
copyObject = JSON.parse(copyObjectString);
|
||||
for (let a = 0; a < copyObject.Elements.length; a++) {
|
||||
console.log("Adaptering X: " + copyObject.Elements[a].X + " to " + (copyObject.Elements[a].X + logicEngine.Panning.OffsetX));
|
||||
console.log("Adaptering Y: " + copyObject.Elements[a].Y + " to " + (copyObject.Elements[a].Y + logicEngine.Panning.OffsetY));
|
||||
copyObject.Elements[a].X = copyObject.Elements[a].X + logicEngine.Panning.OffsetX;
|
||||
copyObject.Elements[a].Y = copyObject.Elements[a].Y + logicEngine.Panning.OffsetY;
|
||||
}
|
||||
return copyObject;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function BuildToolbox() {
|
||||
let toolbox = document.getElementById("inner-left-menu");
|
||||
toolbox.innerHTML = "";
|
||||
|
||||
|
||||
for (let a = 0; a < elementCatalog.Categories.length; a++) {
|
||||
let div_CategoryHeader = document.createElement("div");
|
||||
div_CategoryHeader.id = "TB_CATH_" + elementCatalog.Categories[a].Name;
|
||||
div_CategoryHeader.className ="TOOLBOX_CATEGORY_HEADER";
|
||||
div_CategoryHeader.innerHTML = '<h2>' + elementCatalog.Categories[a].Name + '</h2>';
|
||||
let div_Category = document.createElement("div");
|
||||
div_Category.id = "TB_CATB_" + elementCatalog.Categories[a].Name;
|
||||
div_Category.className = "TOOLBOX_CATEGORY_BODY";
|
||||
div_Category.innerHTML = "";
|
||||
for (let b = 0; b < elementCatalog.Categories[a].Elements.length; b++) {
|
||||
let btn_Element = document.createElement("input");
|
||||
btn_Element.type = "button";
|
||||
btn_Element.id = "btn_" + elementCatalog.Categories[a].Elements[b].Name;
|
||||
btn_Element.title = elementCatalog.Categories[a].Elements[b].Description;
|
||||
btn_Element.value = elementCatalog.Categories[a].Elements[b].Icon + " " + elementCatalog.Categories[a].Elements[b].Name;
|
||||
btn_Element.style.width = "100px";
|
||||
btn_Element.style.textAlign = "left";
|
||||
|
||||
btn_Element.addEventListener('click', function(evt) {
|
||||
addElement(null,elementCatalog.Categories[a].Elements[b].Class,elementCatalog.Categories[a].Elements[b].Args);
|
||||
}, false);
|
||||
|
||||
div_Category.append(btn_Element);
|
||||
// div_Category.append(document.createElement("br"));
|
||||
}
|
||||
toolbox.append(div_CategoryHeader);
|
||||
toolbox.append(div_Category);
|
||||
}
|
||||
}
|
||||
|
||||
function getElementInfo(element) {
|
||||
for (let a = 0; a < ElementReferenceTable.length; a++) {
|
||||
if (ElementReferenceTable[a].Name == element) return ElementReferenceTable[a];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isVersionNewer(version1,version2,orEqual = true) {
|
||||
let v1 = version1.split(".");
|
||||
let v2 = version1.split(".");
|
||||
if (v1.length != 3) return false;
|
||||
if (v2.length != 3) return false;
|
||||
for (let a = 0; a < 3; a++) {
|
||||
v1[a] = parseInt(v1[a]);
|
||||
v2[a] = parseInt(v2[a]);
|
||||
}
|
||||
if (v1[0] > v2[0]) return true;
|
||||
if (v1[0] == v2[0] && v1[1] > v2[1]) return true;
|
||||
if (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] > v2[2]) return true;
|
||||
if ((v1[0] == v2[0] && v1[1] == v2[1]) && (orEqual && v1[2] == v2[2])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function disableSelectedMenus(bool) {
|
||||
disableSelectedRCMs(bool);
|
||||
disableSelectedTFMs(bool);
|
||||
}
|
||||
|
||||
function ShowHelp() {
|
||||
let helpWindow = document.getElementById("HelpWindow");
|
||||
helpWindow.style.display = "block";
|
||||
helpWindow.style.left = Math.round((window.innerWidth / 2) - (helpWindow.clientWidth/2)) + "px";
|
||||
helpWindow.style.top = Math.round((window.innerHeight / 2) - (helpWindow.clientHeight/2)) + "px";
|
||||
}
|
||||
|
||||
function HideHelp() {
|
||||
let helpWindow = document.getElementById("HelpWindow");
|
||||
helpWindow.style.display = "none";
|
||||
}
|
||||
|
||||
function HidePropertiesWindow() {
|
||||
let propwin = document.getElementById("PropertiesBox");
|
||||
propwin.style.display = "none";
|
||||
}
|
||||
|
||||
function SaveSettings() {
|
||||
localStorage.setItem("LogicEngineSettings",JSON.stringify(logicEngine.Settings));
|
||||
console.log("Settings Saved");
|
||||
}
|
||||
|
||||
function createSaveState(container = false) {
|
||||
let saveState = {
|
||||
Name: "LogicDesign",
|
||||
Version: Version,
|
||||
Timestamp: Date.now(),
|
||||
PanX: logicEngine.Panning.OffsetX,
|
||||
PanY: logicEngine.Panning.OffsetY,
|
||||
Elements: new Array()
|
||||
};
|
||||
if (container.Elements.length > 0) saveState.Elements = container.Elements;
|
||||
let saveCompressed = document.getElementById("saveCompressed");
|
||||
if (saveCompressed.checked) {
|
||||
saveState.Elements = LZString.compressToUTF16(JSON.stringify(saveState.Elements));
|
||||
saveState.Compressed = true;
|
||||
} else {
|
||||
saveState.Compressed = false;
|
||||
}
|
||||
return saveState;
|
||||
}
|
||||
|
||||
function download(filename, savestate) {
|
||||
let text = JSON.stringify(savestate);
|
||||
let element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
||||
element.setAttribute('download', filename);
|
||||
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
function getPastedElementDesignator(map,element) {
|
||||
for (let a = 0; a < map?.length; a++) {
|
||||
if (map[a].Old == element) return map[a].New;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadActiveContainer(Elements) {
|
||||
|
||||
let elementConnections = new Array();
|
||||
let icelementConnections = new Array();
|
||||
let designatorMap = new Array();
|
||||
let selectedObjects = new Array();
|
||||
let lowestX = 9999999999;
|
||||
let lowestY = 9999999999;
|
||||
let highestX = 0;
|
||||
let highestY = 0;
|
||||
|
||||
for (let a = 0; a < Elements.length; a++) {
|
||||
if (Elements[a].IsIC) {
|
||||
let classRef = getElementInfo(Elements[a].Name).Class;
|
||||
if (!classRef) {
|
||||
// We need to add this IC to the toolbox
|
||||
let newIC = createIC(JSON.stringify(Elements[a].ICBlueprint),Elements[a].Name,Elements[a].Description);
|
||||
//console.log("NewIC:" + newIC);
|
||||
if (!newIC) return false;
|
||||
}
|
||||
}
|
||||
let classRef = getElementInfo(Elements[a].Name).Class;
|
||||
|
||||
let newElement = new classRef(logicEngine.ActiveContainer,Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]);
|
||||
if (newElement) selectedObjects.push(newElement);
|
||||
|
||||
logicEngine.ActiveContainer.AddElement(newElement);
|
||||
newElement.X -= logicEngine.Panning.OffsetX;
|
||||
newElement.Y -= logicEngine.Panning.OffsetY;
|
||||
|
||||
if (newElement.X < lowestX) lowestX = newElement.X;
|
||||
if (newElement.X > highestX) highestX = newElement.X;
|
||||
if (newElement.Y < lowestY) lowestY = newElement.Y;
|
||||
if (newElement.Y > highestY) highestY = newElement.Y;
|
||||
|
||||
designatorMap.push({Old: Elements[a].Designator, New: newElement.Designator});
|
||||
|
||||
if (Elements[a].Outputs) {
|
||||
if (Elements[a].Outputs.length > 0) {
|
||||
for (let b=0; b < Elements[a].Outputs.length; b++) {
|
||||
elementConnections.push({
|
||||
FromElement: newElement,
|
||||
Input: Elements[a].Outputs[b].Input,
|
||||
Output: Elements[a].Outputs[b].Output,
|
||||
ToElement: Elements[a].Outputs[b].Element
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Elements[a].ICOutputs) {
|
||||
if (Elements[a].ICOutputs.length > 0) {
|
||||
for (let b=0; b < Elements[a].ICOutputs.length; b++) {
|
||||
icelementConnections.push({
|
||||
Element: newElement,
|
||||
FromContainer: newElement.Container,
|
||||
FromElement: Elements[a].ICOutputs[b].fromElement,
|
||||
Input: Elements[a].ICOutputs[b].Input,
|
||||
ToElement: Elements[a].ICOutputs[b].toElement
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logicEngine.ActiveContainer.Selected = selectedObjects;
|
||||
logicEngine.MovingElement = new Array(logicEngine.ActiveContainer.Selected.length);
|
||||
|
||||
for (let b = 0; b < logicEngine.ActiveContainer.Selected.length; b++) {
|
||||
logicEngine.ActiveContainer.Selected[b].X = (logicEngine.ActiveContainer.Selected[b].X - lowestX) + (logicEngine.Mouse.x - logicEngine.Panning.OffsetX) - ((highestX-lowestX)/2);
|
||||
logicEngine.ActiveContainer.Selected[b].Y = (logicEngine.ActiveContainer.Selected[b].Y - lowestY) + (logicEngine.Mouse.y - logicEngine.Panning.OffsetY) - ((highestY-lowestY)/2);
|
||||
logicEngine.MovingElement[b] = {
|
||||
StartX: logicEngine.ActiveContainer.Selected[b].X,
|
||||
StartY: logicEngine.ActiveContainer.Selected[b].Y
|
||||
};
|
||||
logicEngine.MovingElementMouseStartX = logicEngine.Mouse.x;
|
||||
logicEngine.MovingElementMouseStartY = logicEngine.Mouse.y;
|
||||
}
|
||||
logicEngine.MouseDown = true;
|
||||
|
||||
for (let a = 0; a < elementConnections.length; a++) {
|
||||
let toElement = logicEngine.ActiveContainer.HasElement(getPastedElementDesignator(designatorMap,elementConnections[a].ToElement));
|
||||
if (toElement) {
|
||||
if (elementConnections[a].FromContainer) {
|
||||
let fromElement = elementConnections[a].FromContainer.HasElement(elementConnections[a].fromElement);
|
||||
let newConnection = new ContainerConnection(elementConnections[a].FromContainer,logicEngine.ActiveContainer,fromElement,toElement,elementConnections[a].Input);
|
||||
elementConnections[a].Element.Outputs.push(newConnection);
|
||||
|
||||
} else {
|
||||
let newConnection = new ElementConnection(logicEngine.ActiveContainer, toElement, elementConnections[a].Input, elementConnections[a].Output);
|
||||
if (newConnection) {
|
||||
elementConnections[a].FromElement.addConnection(logicEngine.ActiveContainer,toElement,elementConnections[a].Input, elementConnections[a].Output)
|
||||
} else {
|
||||
console.log("We dont have a new connection!!!");
|
||||
console.log(toElement);
|
||||
console.log(elementConnections[a]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function loadContainer(Elements) {
|
||||
let newContainer = new elementContainer();
|
||||
let elementConnections = new Array();
|
||||
let icelementConnections = new Array();
|
||||
for (let a = 0; a < Elements.length; a++) {
|
||||
if (Elements[a].IsIC) {
|
||||
let classRef = getElementInfo(Elements[a].Name).Class;
|
||||
if (!classRef) {
|
||||
// We need to add this IC to the toolbox
|
||||
let newIC = createIC(JSON.stringify(Elements[a].ICBlueprint),Elements[a].Name,Elements[a].Description);
|
||||
//console.log("NewIC:" + newIC);
|
||||
if (!newIC) return false;
|
||||
}
|
||||
}
|
||||
let classRef = getElementInfo(Elements[a].Name).Class;
|
||||
|
||||
let newElement = new classRef(newContainer,Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]);
|
||||
|
||||
newContainer.AddElement(newElement);
|
||||
newElement.Designator = Elements[a].Designator;
|
||||
if (Elements[a].Outputs) {
|
||||
if (Elements[a].Outputs.length > 0) {
|
||||
for (let b=0; b < Elements[a].Outputs.length; b++) {
|
||||
elementConnections.push({
|
||||
FromElement: newElement,
|
||||
Input: Elements[a].Outputs[b].Input,
|
||||
Output: Elements[a].Outputs[b].Output,
|
||||
ToElement: Elements[a].Outputs[b].Element
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Elements[a].ICOutputs) {
|
||||
if (Elements[a].ICOutputs.length > 0) {
|
||||
for (let b=0; b < Elements[a].ICOutputs.length; b++) {
|
||||
icelementConnections.push({
|
||||
Element: newElement,
|
||||
FromContainer: newElement.Container,
|
||||
FromElement: Elements[a].ICOutputs[b].fromElement,
|
||||
Input: Elements[a].ICOutputs[b].Input,
|
||||
ToElement: Elements[a].ICOutputs[b].toElement
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we need to make all of the connections
|
||||
for (let a = 0; a < elementConnections.length; a++) {
|
||||
let toElement = newContainer.HasElement(elementConnections[a].ToElement);
|
||||
if (toElement) {
|
||||
if (elementConnections[a].FromContainer) {
|
||||
let fromElement = elementConnections[a].FromContainer.HasElement(elementConnections[a].fromElement);
|
||||
let newConnection = new ContainerConnection(elementConnections[a].FromContainer,newContainer,fromElement,toElement,elementConnections[a].Input);
|
||||
elementConnections[a].Element.Outputs.push(newConnection);
|
||||
|
||||
} else {
|
||||
let newConnection = new ElementConnection(newContainer, toElement, elementConnections[a].Input, elementConnections[a].Output);
|
||||
//elementConnections[a].FromElement.addConnection(newContainer,toElement,elementConnections[a].Input,elementConnections[a].Output)
|
||||
if (newConnection) {
|
||||
//elementConnections[a].FromElement.OutputConnections.push(newConnection);
|
||||
elementConnections[a].FromElement.addConnection(newContainer,toElement,elementConnections[a].Input, elementConnections[a].Output)
|
||||
} else {
|
||||
console.log("We dont have a new connection!!!");
|
||||
console.log(toElement);
|
||||
console.log(elementConnections[a]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
for (let a = 0; a < icelementConnections.length; a++) {
|
||||
let fromElement = icelementConnections[a].FromContainer.HasElement(icelementConnections[a].FromElement);
|
||||
let toElement = newContainer.HasElement(icelementConnections[a].ToElement);
|
||||
if (toElement) {
|
||||
icelementConnections[a].Element.addConnection(newContainer, toElement, icelementConnections[a].Input);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return newContainer;
|
||||
}
|
||||
|
||||
function loadsave(savedata) {
|
||||
if (!savedata) return 0;
|
||||
if (!savedata.Version) return -1; // TODO: Let the person know invalid save file
|
||||
if (!isVersionNewer(savedata.Version,"0.3.0")) return -2; // TODO: Let the person know the version is too old
|
||||
if (savedata?.Compressed) savedata.Elements = JSON.parse(LZString.decompressFromUTF16(savedata.Elements));
|
||||
let newContainer = loadContainer(savedata.Elements);
|
||||
if (!newContainer) return -3;
|
||||
let saveName = document.getElementById("saveName");
|
||||
saveName.value = savedata.Name;
|
||||
logicEngine.ActiveContainer = newContainer;
|
||||
logicEngine.Panning.OffsetX = 0;
|
||||
logicEngine.Panning.OffsetY = 0;
|
||||
logicEngine.Ctx.setTransform(1,0,0,1,0,0);
|
||||
|
||||
if (savedata.PanX) {
|
||||
logicEngine.Panning.OffsetX = savedata.PanX;
|
||||
logicEngine.Panning.OffsetY = savedata.PanY;
|
||||
logicEngine.Ctx.translate(logicEngine.Panning.OffsetX,logicEngine.Panning.OffsetY);
|
||||
}
|
||||
logicEngine.RedrawStatics();
|
||||
return true;
|
||||
}
|
||||
1807
js/logicengine.js
1807
js/logicengine.js
File diff suppressed because it is too large
Load Diff
119
js/main.js
119
js/main.js
@ -2,23 +2,122 @@
|
||||
MatCat BrowserLogic Simulator
|
||||
*/
|
||||
|
||||
let Version = "0.4.16";
|
||||
|
||||
let Version = "0.2.10";
|
||||
let spanVersion = document.getElementById("version");
|
||||
spanVersion.innerText = Version;
|
||||
|
||||
// get the canvas and get the engine object going
|
||||
let lCanvasElement = document.getElementById("LogicPlane");
|
||||
let logicEngine = new LogicEngine(lCanvasElement);
|
||||
|
||||
// Get the game Tick going, set to 1ms but most browsers will min to 4ms
|
||||
setInterval(logicEngine.Scheduler.Tick.bind(logicEngine.Scheduler), 1);
|
||||
// Get the game Tick going, this will be 4ms for now which is the fastest that is supported
|
||||
// by the HTML5 spec!
|
||||
setInterval(logicEngine.Scheduler.Tick.bind(logicEngine.Scheduler), 4);
|
||||
|
||||
// Sadly this doesn't work well inside of the class so we will do it here real fast
|
||||
window.addEventListener('resize', function(evt) {
|
||||
logicEngine.Resize(evt);
|
||||
}, false);
|
||||
|
||||
window.addEventListener('keydown', function(evt) {
|
||||
logicEngine.Key_Press(evt);
|
||||
}, false);
|
||||
|
||||
lCanvasElement.addEventListener('mousedown', function(evt) {
|
||||
logicEngine.Mouse_Down(evt);
|
||||
}, false);
|
||||
|
||||
lCanvasElement.addEventListener('mouseup', function(evt) {
|
||||
logicEngine.Mouse_Up(evt);
|
||||
}, false);
|
||||
|
||||
window.addEventListener('mousemove', function(evt) {
|
||||
logicEngine.Mouse_Move(evt);
|
||||
}, false);
|
||||
|
||||
// Get the engine going
|
||||
logicEngine.StartEngine();
|
||||
RegisterEvents();
|
||||
TopMenuListeners();
|
||||
RightClickMenuListeners();
|
||||
BuildToolbox();
|
||||
BuildTopMenu();
|
||||
|
||||
// Setup interface buttons
|
||||
let btn_CloseWelcome = document.getElementById("btn_CloseWelcome");
|
||||
btn_CloseWelcome.addEventListener('click', function(evt) {
|
||||
let WelcomeScreen = document.getElementById("WelcomeWindow");
|
||||
let DarkOverlay = document.getElementById("darkout-overlay");
|
||||
WelcomeScreen.style.display = "none";
|
||||
DarkOverlay.style.display = "none";
|
||||
let chk_dontDisplayWelcome = document.getElementById("chk_dontDisplayWelcome");
|
||||
if (chk_dontDisplayWelcome.checked) {
|
||||
setCookie("hidewelcomescreen","true",3600);
|
||||
}
|
||||
}, false);
|
||||
|
||||
let btn_Delete = document.getElementById("btn_Delete");
|
||||
btn_Delete.addEventListener('click', function(evt) {
|
||||
logicEngine.Key_Press({key: "Delete"});
|
||||
}, false);
|
||||
|
||||
let btn_AddAND = document.getElementById("btn_AddAND");
|
||||
btn_AddAND.addEventListener('click', function(evt) {
|
||||
addElement(LogicAND, [2]);
|
||||
}, false);
|
||||
|
||||
let btn_AddNAND = document.getElementById("btn_AddNAND");
|
||||
btn_AddNAND.addEventListener('click', function(evt) {
|
||||
addElement(LogicNAND, [2]);
|
||||
}, false);
|
||||
|
||||
let btn_AddOR = document.getElementById("btn_AddOR");
|
||||
btn_AddOR.addEventListener('click', function(evt) {
|
||||
addElement(LogicOR, [2]);
|
||||
}, false);
|
||||
|
||||
let btn_AddNOR = document.getElementById("btn_AddNOR");
|
||||
btn_AddNOR.addEventListener('click', function(evt) {
|
||||
addElement(LogicNOR, [2]);
|
||||
}, false);
|
||||
|
||||
let btn_AddXOR = document.getElementById("btn_AddXOR");
|
||||
btn_AddXOR.addEventListener('click', function(evt) {
|
||||
addElement(LogicXOR,[]);
|
||||
}, false);
|
||||
|
||||
let btn_AddXNOR = document.getElementById("btn_AddXNOR");
|
||||
btn_AddXNOR.addEventListener('click', function(evt) {
|
||||
addElement(LogicXNOR,[]);
|
||||
}, false);
|
||||
|
||||
btn_AddNOT.addEventListener('click', function(evt) {
|
||||
addElement(LogicNOT,[]);
|
||||
}, false);
|
||||
|
||||
let btn_AddSWITCH = document.getElementById("btn_AddSWITCH");
|
||||
btn_AddSWITCH.addEventListener('click', function(evt) {
|
||||
addElement(InputSwitch,[]);
|
||||
}, false);
|
||||
|
||||
let btn_AddBTN = document.getElementById("btn_AddBTN");
|
||||
btn_AddBTN.addEventListener('click', function(evt) {
|
||||
addElement(InputButton,[]);
|
||||
}, false);
|
||||
|
||||
let btn_AddCLK = document.getElementById("btn_AddCLK");
|
||||
btn_AddCLK.addEventListener('click', function(evt) {
|
||||
let clk = addElement(ClockElement,[]);
|
||||
logicEngine.Scheduler.addTask(clk.Task);
|
||||
}, false);
|
||||
|
||||
let btn_AddPulse = document.getElementById("btn_AddPULSE");
|
||||
btn_AddPulse.addEventListener('click', function(evt) {
|
||||
let pulse = addElement(PulseElement,[]);
|
||||
logicEngine.Scheduler.addTask(pulse.Task);
|
||||
}, false);
|
||||
|
||||
function CheckForWelcomeCookie() {
|
||||
if (getCookie("hidewelcomescreen")) {
|
||||
let WelcomeScreen = document.getElementById("WelcomeWindow");
|
||||
let DarkOverlay = document.getElementById("darkout-overlay");
|
||||
WelcomeScreen.style.display = "none";
|
||||
DarkOverlay.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
CheckForWelcomeCookie();
|
||||
|
||||
212
js/mainevents.js
212
js/mainevents.js
@ -1,212 +0,0 @@
|
||||
function RegisterEvents() {
|
||||
|
||||
/*
|
||||
DISPLAY AND RESIZING
|
||||
*/
|
||||
window.addEventListener('resize', function (evt) {
|
||||
logicEngine.Resize(evt);
|
||||
}, false);
|
||||
|
||||
|
||||
/*
|
||||
SYSTEM EVENTS
|
||||
*/
|
||||
document.addEventListener('copy', function (evt) {
|
||||
evt.preventDefault();
|
||||
let copyObject = GetCopyObject();
|
||||
if (!copyObject) return;
|
||||
|
||||
evt.clipboardData.setData('logicparts/json', JSON.stringify(copyObject));
|
||||
evt.clipboardData.setData('text', JSON.stringify(copyObject));
|
||||
});
|
||||
|
||||
document.addEventListener('cut', function (evt) {
|
||||
evt.preventDefault();
|
||||
let copyObject = GetCopyObject();
|
||||
if (!copyObject) return;
|
||||
|
||||
evt.clipboardData.setData('logicparts/json', JSON.stringify(copyObject));
|
||||
evt.clipboardData.setData('text', JSON.stringify(copyObject));
|
||||
|
||||
logicEngine.Key_Press({ctrlKey: false, key: "Delete"});
|
||||
disableSelectedMenus(true);
|
||||
});
|
||||
|
||||
document.addEventListener('paste', function (evt) {
|
||||
evt.preventDefault();
|
||||
if (evt.clipboardData.getData('logicparts/json')) {
|
||||
//console.log(evt.clipboardData.getData('logicparts/json'));
|
||||
loadActiveContainer(JSON.parse(evt.clipboardData.getData('logicparts/json')).Elements);
|
||||
} else {
|
||||
// Lets see if they pasted a proper LogicParts format atleast
|
||||
if (evt.clipboardData.getData('text')) {
|
||||
let jsonObj = JSON.parse(evt.clipboardData.getData('text'));
|
||||
if (jsonObj?.LogicParts && jsonObj?.Paste) {
|
||||
console.log("We got a paste of a general text object that is valid");
|
||||
loadActiveContainer(jsonObj.Elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
KEYBOARD EVENTS
|
||||
*/
|
||||
window.addEventListener('keydown', function (evt) {
|
||||
logicEngine.Key_Press(evt);
|
||||
}, false);
|
||||
|
||||
window.addEventListener('keyup', function (evt) {
|
||||
logicEngine.Key_Up(evt);
|
||||
}, false);
|
||||
|
||||
/*
|
||||
MOUSE EVENTS
|
||||
*/
|
||||
window.addEventListener('mouseup', function (evt) {
|
||||
let rcm = document.getElementById("RightClickMenu");
|
||||
rcm.style.display = "none";
|
||||
let tfms = document.getElementsByClassName("top-menu-div");
|
||||
for (let a = 0; a < tfms.length; a++) {
|
||||
tfms[a].style.display = "none";
|
||||
}
|
||||
}, false);
|
||||
|
||||
lCanvasElement.addEventListener('mousedown', function (evt) {
|
||||
logicEngine.Mouse_Down(evt);
|
||||
}, false);
|
||||
|
||||
lCanvasElement.addEventListener('mouseup', function (evt) {
|
||||
logicEngine.Mouse_Up(evt);
|
||||
}, false);
|
||||
|
||||
window.addEventListener('mousemove', function (evt) {
|
||||
logicEngine.Mouse_Move(evt);
|
||||
}, false);
|
||||
|
||||
|
||||
/*
|
||||
WINDOW CONTROLS
|
||||
*/
|
||||
let btn_CloseWelcome = document.getElementById("btn_CloseWelcome");
|
||||
btn_CloseWelcome.addEventListener('click', function (evt) {
|
||||
let WelcomeScreen = document.getElementById("WelcomeWindow");
|
||||
let DarkOverlay = document.getElementById("darkout-overlay");
|
||||
WelcomeScreen.style.display = "none";
|
||||
DarkOverlay.style.display = "none";
|
||||
let chk_dontDisplayWelcome = document.getElementById("chk_dontDisplayWelcome");
|
||||
if (chk_dontDisplayWelcome.checked) {
|
||||
setCookie("hidewelcomescreen", "true", 3600);
|
||||
}
|
||||
}, false);
|
||||
|
||||
let btn_CreateIC_Cancel = document.getElementById("btn_CreateIC_Cancel");
|
||||
btn_CreateIC_Cancel.addEventListener('click', function (evt) {
|
||||
let CreateICBox = document.getElementById("CreateICBox");
|
||||
CreateICBox.style.display = "none";
|
||||
});
|
||||
|
||||
let btn_CreateIC_Create = document.getElementById("btn_CreateIC_Create");
|
||||
btn_CreateIC_Create.addEventListener('click', function (evt) {
|
||||
let CreateICBox = document.getElementById("CreateICBox");
|
||||
let ICName = document.getElementById("ICName");
|
||||
let ICDescription = document.getElementById("ICDescription");
|
||||
createIC(logicEngine.ActiveContainer, ICName.value, ICDescription.value);
|
||||
CreateICBox.style.display = "none";
|
||||
});
|
||||
|
||||
let ICName = document.getElementById("ICName");
|
||||
let ICDescription = document.getElementById("ICDescription");
|
||||
|
||||
ICName.addEventListener('input', function (evt) {
|
||||
btn_CreateIC_Create.disabled = true;
|
||||
if (ICName.value.length > 0 && ICDescription.value.length > 0) btn_CreateIC_Create.disabled = false;
|
||||
});
|
||||
|
||||
ICDescription.addEventListener('input', function (evt) {
|
||||
btn_CreateIC_Create.disabled = true;
|
||||
if (ICName.value.length > 0 && ICDescription.value.length > 0) btn_CreateIC_Create.disabled = false;
|
||||
});
|
||||
|
||||
let btn_SaveDesign = document.getElementById("btn_SaveDesign");
|
||||
btn_SaveDesign.addEventListener('click', function (evt) {
|
||||
let DesignName = document.getElementById("saveName");
|
||||
let savestate = createSaveState(logicEngine.ActiveContainer);
|
||||
savestate.Name = DesignName.value;
|
||||
let saveWindow = document.getElementById("SaveWindow");
|
||||
saveWindow.style.display = "none";
|
||||
download(savestate.Name + ".LogicParts", savestate);
|
||||
});
|
||||
|
||||
let btn_Save_Cancel = document.getElementById("btn_CancelSave");
|
||||
btn_Save_Cancel.addEventListener('click', function (evt) {
|
||||
let saveWindow = document.getElementById("SaveWindow");
|
||||
saveWindow.style.display = "none";
|
||||
});
|
||||
|
||||
let file_Load = document.getElementById("file_Load");
|
||||
let tfm_Open = document.getElementById("tfm_Open");
|
||||
|
||||
file_Load.addEventListener('change', function (evt) {
|
||||
let fread = new FileReader();
|
||||
fread.onload = (function (theFile) {
|
||||
return function (e) {
|
||||
try {
|
||||
let restoredata = JSON.parse(e.target.result);
|
||||
|
||||
loadresult = loadsave(restoredata);
|
||||
if (!loadresult) {
|
||||
switch (loadresult) {
|
||||
case -1:
|
||||
alert("Invalid LogicParts file!");
|
||||
break;
|
||||
case -2:
|
||||
alert("The version of MatCat BrowserLogic cannot open this save version!");
|
||||
break;
|
||||
default:
|
||||
alert("Bad save file!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
alert("Bad file! " + ex);
|
||||
console.log(ex);
|
||||
}
|
||||
}
|
||||
})(evt.target.files[0]);
|
||||
fread.readAsText(evt.target.files[0]);
|
||||
}, false);
|
||||
|
||||
|
||||
let in_GridSize = document.getElementById("in_GridSize");
|
||||
in_GridSize.addEventListener("change", function (evt) {
|
||||
logicEngine.Settings.GridSize = parseInt(in_GridSize.value);
|
||||
logicEngine.Resize("");
|
||||
SaveSettings();
|
||||
//setTimeout(function(){hideMenus()},10);
|
||||
});
|
||||
|
||||
let helpWindow = document.getElementById("HelpWindow");
|
||||
let helpWindow_Close = helpWindow.getElementsByClassName("CloseWindow")[0];
|
||||
helpWindow_Close.addEventListener("click", function (evt) {
|
||||
HideHelp();
|
||||
});
|
||||
|
||||
|
||||
let leftmenu = document.getElementById("left-menu");
|
||||
let propwin = document.getElementById("PropertiesBox");
|
||||
lCanvasElement.addEventListener("contextmenu", function (evt) {
|
||||
showRCM(evt)
|
||||
});
|
||||
leftmenu.addEventListener("contextmenu", function (evt) {
|
||||
showRCM(evt)
|
||||
});
|
||||
propwin.addEventListener("contextmenu", function (evt) {
|
||||
showRCM(evt)
|
||||
});
|
||||
|
||||
let topbar = document.getElementById("top-bar");
|
||||
topbar.addEventListener("contextmenu", function (evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
function disableSelectedRCMs(bool) {
|
||||
let rcm_Delete = document.getElementById("rcm_Delete");
|
||||
let rcm_Disconnect = document.getElementById("rcm_Disconnect");
|
||||
|
||||
if (bool) {
|
||||
rcm_Delete.classList.add("disabled");
|
||||
rcm_Disconnect.classList.add("disabled");
|
||||
} else {
|
||||
rcm_Delete.classList.remove("disabled");
|
||||
rcm_Disconnect.classList.remove("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
function showRCM(evt) {
|
||||
evt.preventDefault();
|
||||
let rcm = document.getElementById("RightClickMenu");
|
||||
rcm.style.left = (evt.clientX-40) + "px";
|
||||
rcm.style.top = (evt.clientY-25) + "px";
|
||||
rcm.style.display = "block";
|
||||
}
|
||||
|
||||
function RightClickMenuListeners() {
|
||||
let rcm_CreateIC = document.getElementById("rcm_CreateIC");
|
||||
rcm_CreateIC.addEventListener('click', function (evt) {
|
||||
if (!this.classList.contains("disabled")) {
|
||||
let CreateICBox = document.getElementById("CreateICBox");
|
||||
CreateICBox.style.display = "block";
|
||||
}
|
||||
});
|
||||
|
||||
let rcm_New = document.getElementById("rcm_New");
|
||||
rcm_New.addEventListener('click', function (evt) {
|
||||
logicEngine.ActiveContainer = new elementContainer();
|
||||
logicEngine.Ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
logicEngine.Panning.OffsetX = 0;
|
||||
logicEngine.Panning.OffsetY = 0;
|
||||
HidePropertiesWindow();
|
||||
disableSelectedMenus(true);
|
||||
|
||||
});
|
||||
|
||||
let rcm_Delete = document.getElementById("rcm_Delete");
|
||||
rcm_Delete.addEventListener('click', function (evt) {
|
||||
logicEngine.Key_Press({ctrlKey: false, key: "Delete"});
|
||||
HidePropertiesWindow();
|
||||
disableSelectedMenus(true);
|
||||
});
|
||||
|
||||
let rcm_Disconect = document.getElementById("rcm_Disconnect");
|
||||
rcm_Disconect.addEventListener('click', function (evt) {
|
||||
for (let a = 0; a < logicEngine.ActiveContainer.Selected.length; a++) {
|
||||
logicEngine.ActiveContainer.Selected[a].Disconnect();
|
||||
}
|
||||
logicEngine.ActiveContainer.Disconnect(logicEngine.ActiveContainer.Selected);
|
||||
});
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
class Task {
|
||||
constructor(taskname,taskdescription,tasktype,tasktime,callback,deleteonrun = false) {
|
||||
// tasktype: 0: interval, 1: fixed time
|
||||
this.Name = taskname;
|
||||
this.Description = taskdescription;
|
||||
this.Type = tasktype;
|
||||
this.Enabled = true;
|
||||
this.Time = tasktime;
|
||||
this.LastCall = Date.now();
|
||||
this.CallCount = 0;
|
||||
this.DeleteOnRun = false;
|
||||
if (deleteonrun) this.DeleteOnRun = true;
|
||||
this.Callback = callback;
|
||||
if (!(tasktype >= 0 && tasktype <= 1)) this.Type = 0;
|
||||
}
|
||||
CheckTime() {
|
||||
let time = this.Time;
|
||||
if (this.Type == 0) time = this.LastCall + this.Time;
|
||||
if (this.Enabled && (Date.now() >= time)) {
|
||||
this.LastCall = Date.now();
|
||||
this.CallCount++;
|
||||
if (this.Type == 1 || this.DeleteOnRun == true) this.Enabled = false;
|
||||
this.Callback();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleEngine {
|
||||
constructor() {
|
||||
this.Tasks = new Array();
|
||||
}
|
||||
|
||||
addTask(task) {
|
||||
this.Tasks.push(task);
|
||||
}
|
||||
|
||||
deleteTask(task) {
|
||||
for (let a = 0; a < this.Tasks.length; a++) {
|
||||
if (this.Tasks[a] == task) {
|
||||
this.Tasks.splice(a,1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Tick() {
|
||||
for (let a = 0; a < this.Tasks.length; a++) {
|
||||
this.Tasks[a].CheckTime();
|
||||
if (!this.Tasks[a].Enabled && this.Tasks[a].DeleteOnRun) {
|
||||
this.Tasks.splice(a,1);
|
||||
a--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,304 +0,0 @@
|
||||
function BuildTopMenu() {
|
||||
// Make sure settings are set correct
|
||||
let tfm_ShowConns = document.getElementById("tfm_ShowConnections");
|
||||
let tfm_ConnLayer = document.getElementById("tfm_ConnectionLayer");
|
||||
|
||||
if (logicEngine.Settings.HideConnections) {
|
||||
let sgSpan = tfm_ShowConns.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
} else {
|
||||
let sgSpan = tfm_ShowConns.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
}
|
||||
if (!logicEngine.Settings.TopConnections) {
|
||||
tfm_ConnLayer.innerHTML = '<span class="blankbox"> </span>Connections Above';
|
||||
} else {
|
||||
tfm_ConnLayer.innerHTML = '<span class="blankbox"> </span>Connections Below';
|
||||
}
|
||||
if (!logicEngine.Settings.ShowFPS) {
|
||||
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
} else {
|
||||
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
}
|
||||
if (!logicEngine.Settings.ShowGrid) {
|
||||
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
} else {
|
||||
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
}
|
||||
if (!logicEngine.Settings.SnapGrid) {
|
||||
let sgSpan = tfm_SnapGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
} else {
|
||||
let sgSpan = tfm_SnapGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
}
|
||||
in_GridSize.value = logicEngine.Settings.GridSize;
|
||||
}
|
||||
|
||||
function disableSelectedTFMs(bool) {
|
||||
let tfm_Delete = document.getElementById("tfm_Delete");
|
||||
let tfm_Disconnect = document.getElementById("tfm_Disconnect");
|
||||
|
||||
if (bool) {
|
||||
tfm_Delete.classList.add("disabled");
|
||||
tfm_Disconnect.classList.add("disabled");
|
||||
} else {
|
||||
tfm_Delete.classList.remove("disabled");
|
||||
tfm_Disconnect.classList.remove("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
function hideMenus() {
|
||||
let tds = document.getElementsByClassName("top-menu-div");
|
||||
for (let a = 0; a < tds.length; a++) {
|
||||
tds[a].setAttribute('style','display: none;');
|
||||
}
|
||||
}
|
||||
|
||||
function TopMenuListeners() {
|
||||
/*
|
||||
All the listeners for the top menu
|
||||
*/
|
||||
let tfm_CreateIC = document.getElementById("tfm_CreateIC");
|
||||
tfm_CreateIC.addEventListener('click', function (evt) {
|
||||
if (!tfm_CreateIC.classList.contains("disabled")) {
|
||||
let CreateICBox = document.getElementById("CreateICBox");
|
||||
CreateICBox.style.display = "block";
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
let tfm_New = document.getElementById("tfm_New");
|
||||
tfm_New.addEventListener('click', function (evt) {
|
||||
logicEngine.ActiveContainer = new elementContainer();
|
||||
logicEngine.Ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
logicEngine.Panning.OffsetX = 0;
|
||||
logicEngine.Panning.OffsetY = 0;
|
||||
HidePropertiesWindow();
|
||||
disableSelectedMenus(true);
|
||||
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Cut = document.getElementById("tfm_Cut");
|
||||
tfm_Cut.addEventListener('click', function (evt) {
|
||||
document.execCommand('cut');
|
||||
disableSelectedMenus(true);
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Copy = document.getElementById("tfm_Copy");
|
||||
tfm_Copy.addEventListener('click', function (evt) {
|
||||
document.execCommand('copy');
|
||||
disableSelectedMenus(true);
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Paste = document.getElementById("tfm_Paste");
|
||||
tfm_Paste.addEventListener('click', function (evt) {
|
||||
document.execCommand('paste');
|
||||
disableSelectedMenus(true);
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
|
||||
let tfm_Delete = document.getElementById("tfm_Delete");
|
||||
tfm_Delete.addEventListener('click', function (evt) {
|
||||
logicEngine.Key_Press({ctrlKey: false, key: "Delete"});
|
||||
HidePropertiesWindow();
|
||||
disableSelectedMenus(true);
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Disconect = document.getElementById("tfm_Disconnect");
|
||||
tfm_Disconect.addEventListener('click', function (evt) {
|
||||
for (let a = 0; a < logicEngine.ActiveContainer.Selected.length; a++) {
|
||||
logicEngine.ActiveContainer.Selected[a]?.Disconnect();
|
||||
}
|
||||
logicEngine.ActiveContainer.Disconnect(logicEngine.ActiveContainer.Selected);
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_SelectAll = document.getElementById("tfm_SelectAll");
|
||||
tfm_SelectAll.addEventListener('click', function (evt) {
|
||||
let elements = new Array();
|
||||
for (let a = 0; a < logicEngine.ActiveContainer.Elements?.length; a++) {
|
||||
elements.push(logicEngine.ActiveContainer.Elements[a]);
|
||||
}
|
||||
logicEngine.ActiveContainer.Selected = elements;
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Save = document.getElementById("tfm_Save");
|
||||
tfm_Save.addEventListener('click', function (evt) {
|
||||
let saveWindow = document.getElementById("SaveWindow");
|
||||
saveWindow.style.display = "block";
|
||||
saveWindow.style.left = Math.round((window.innerWidth / 2) - (saveWindow.clientWidth / 2)) + "px";
|
||||
saveWindow.style.top = Math.round((window.innerHeight / 2) - (saveWindow.clientHeight / 2)) + "px";
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
tfm_Open.addEventListener('click', function (evt) {
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
file_Load.click();
|
||||
});
|
||||
|
||||
/*
|
||||
Generate the top menu item listeners
|
||||
*/
|
||||
let tfms = document.getElementsByClassName("top-menu-item");
|
||||
for (let a = 0; a < tfms.length; a++) {
|
||||
tfms[a].addEventListener("click", function (evt) {
|
||||
let tfm = document.getElementById(tfms[a].id + "Menu");
|
||||
let tfm_rect = tfms[a].getBoundingClientRect();
|
||||
tfm.style.left = (tfm_rect.left) + "px";
|
||||
tfm.style.top = (tfm_rect.top + tfm_rect.height) + "px";
|
||||
tfm.style.display = "block";
|
||||
});
|
||||
let tfm = document.getElementById(tfms[a].id + "Menu");
|
||||
if (!tfm.classList.contains("keybinding-complete")) {
|
||||
tfm.classList.add("keybinding-complete");
|
||||
let menuItems = tfm?.getElementsByTagName("li");
|
||||
for (let b = 0; b < menuItems?.length; b++) {
|
||||
if (menuItems[b]?.getAttribute("value")) menuItems[b].innerHTML = '<span class="menuitem-label">' + menuItems[b].innerHTML + '</span><span class="menuitem-shortcut">' + (KeybindToString(logicEngine.Settings.Keybindings[menuItems[b].getAttribute("value")])) + '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tfm_About = document.getElementById("tfm_About");
|
||||
tfm_About.addEventListener("click", function (evt) {
|
||||
let WelcomeScreen = document.getElementById("WelcomeWindow");
|
||||
WelcomeScreen.style.display = "block";
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_ShowConns = document.getElementById("tfm_ShowConnections");
|
||||
tfm_ShowConns.addEventListener("click", function (evt) {
|
||||
if (logicEngine.Settings.HideConnections) {
|
||||
let sgSpan = tfm_ShowConns.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
logicEngine.Settings.HideConnections = false;
|
||||
} else {
|
||||
let sgSpan = tfm_ShowConns.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
logicEngine.Settings.HideConnections = true;
|
||||
}
|
||||
SaveSettings();
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_ConnLayer = document.getElementById("tfm_ConnectionLayer");
|
||||
tfm_ConnLayer.addEventListener("click", function (evt) {
|
||||
if (logicEngine.Settings.TopConnections) {
|
||||
tfm_ConnLayer.innerText = "Connections Above";
|
||||
logicEngine.Settings.TopConnections = false;
|
||||
} else {
|
||||
tfm_ConnLayer.innerText = "Connections Below";
|
||||
logicEngine.Settings.TopConnections = true;
|
||||
}
|
||||
SaveSettings();
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_ShowFPS = document.getElementById("tfm_ShowFPS");
|
||||
tfm_ShowFPS.addEventListener("click", function (evt) {
|
||||
if (logicEngine.Settings.ShowFPS) {
|
||||
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
logicEngine.Settings.ShowFPS = false;
|
||||
} else {
|
||||
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
logicEngine.Settings.ShowFPS = true;
|
||||
}
|
||||
SaveSettings();
|
||||
logicEngine.Resize("");
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Pan2Center = document.getElementById("tfm_Pan2Center");
|
||||
tfm_Pan2Center.addEventListener("click", function (evt) {
|
||||
logicEngine.Ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
logicEngine.Panning.OffsetX = 0;
|
||||
logicEngine.Panning.OffsetY = 0;
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_ShowGrid = document.getElementById("tfm_ShowGrid");
|
||||
tfm_ShowGrid.addEventListener("click", function (evt) {
|
||||
if (logicEngine.Settings.ShowGrid) {
|
||||
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
logicEngine.Settings.ShowGrid = false;
|
||||
} else {
|
||||
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
logicEngine.Settings.ShowGrid = true;
|
||||
}
|
||||
SaveSettings();
|
||||
logicEngine.Resize("");
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_SnapGrid = document.getElementById("tfm_SnapGrid");
|
||||
tfm_SnapGrid.addEventListener("click", function (evt) {
|
||||
if (logicEngine.Settings.SnapGrid) {
|
||||
let sgSpan = tfm_SnapGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerHTML = " ";
|
||||
logicEngine.Settings.SnapGrid = false;
|
||||
} else {
|
||||
let sgSpan = tfm_SnapGrid.getElementsByClassName("checkbox")[0];
|
||||
sgSpan.innerText = "✓";
|
||||
logicEngine.Settings.SnapGrid = true;
|
||||
}
|
||||
SaveSettings();
|
||||
logicEngine.Resize("");
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
});
|
||||
|
||||
let tfm_Help = document.getElementById("tfm_Help");
|
||||
tfm_Help.addEventListener("click", function (evt) {
|
||||
setTimeout(function () {
|
||||
hideMenus()
|
||||
}, 10);
|
||||
ShowHelp();
|
||||
});
|
||||
}
|
||||
1
js/vendor/lz-string.min.js
vendored
1
js/vendor/lz-string.min.js
vendored
@ -1 +0,0 @@
|
||||
var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString);
|
||||
3
js/vendor/modernizr-3.11.2.min.js
vendored
Normal file
3
js/vendor/modernizr-3.11.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user