Compare commits

..

No commits in common. "master" and "0.3.10" have entirely different histories.

20 changed files with 2317 additions and 4612 deletions

103
README.md
View File

@ -1,116 +1,17 @@
# 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

View File

@ -12,10 +12,8 @@
========================================================================== */
body {
background-color: #54545d;
overflow: hidden;
}
html {
color: #ddd;
font-size: 1em;
@ -267,126 +265,55 @@ textarea {
}
#left-menu {
width: 212px;
width: 200px;
max-width: 200px;
height: 99vh;
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;
height: 99%;
overflow: auto;
}
#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;
height: 50px;
overflow: none;
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 {
#LeftOf-SiteTitle {
display: inline-block;
width: 200px;
height: 100%;
font-size: 0.5em;
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 */
#SiteTitle {
display: inline-block;
height: 100%;
width: 50%;
margin: 0px;
padding-top: 2px;
}
.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 */
.TOOLBOX_CATEGORY_HEADER {
margin: 0px;
padding: 0px;
text-align: center;
background-color: #222;
}
.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;
.TOOLBOX_CATEGORY_HEADER h2 {
margin: 0px;
padding: 5px;
}
#darkout-overlay {
@ -439,7 +366,7 @@ textarea {
#WelcomeWindow label {
font-size: 0.5em;
}
#PropertiesBox, #CreateICBox, #SaveWindow, #HelpWindow {
#PropertiesBox, #CreateICBox {
display: none;
position: absolute;
right: 20px;
@ -450,43 +377,6 @@ textarea {
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;
@ -497,135 +387,16 @@ textarea {
border: 3px solid red;
}
#SaveWindow {
width: 350px;
height: 140px;
}
#SaveWindowContent {
margin: auto;
width: 90%;
margin-top: 10px;
}
#PropertiesBoxTitle, #CreateICBoxTitle, #SaveWindowTitle, #HelpWindowTitle {
#PropertiesBoxTitle, #CreateICBoxTitle {
text-align: center;
background-color: #222222;
font-size: 1.5em;
}
#PropertiesBoxContent, #CreateICBoxContent, #SaveWindowContent {
#PropertiesBoxContent, #CreateICBoxContent {
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 */

View File

@ -26,86 +26,26 @@
<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">&nbsp;</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">&nbsp;</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">&nbsp;</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 id="LeftOf-SiteTitle">
<input type="button" id="btn_New" value="New"/>&nbsp;&nbsp;&nbsp;
<input type="button" id="btn_Save" value="Save"/>&nbsp;&nbsp;&nbsp;
<input type="button" id="btn_Load" value="Load"/>
<input type="file" id="file_Load" accept=".LogicParts" style="display: none;" />
</div>
<div id ="SiteTitle">
MatCat BrowserLogic <span id="version"> </span>
</div>
</div>
<div id="left-menu">
<div id="inner-left-menu">
LOADING
</div>
<input type="button" id="btn_CreateIC" value="Create IC" style="position: absolute; bottom: 30px; left: 50px;">
</div>
<canvas id="GridPlane" width="400" height="300" style="position: absolute; top: 50px; left 202px;"></canvas>
<canvas id="LogicPlane" width="400" height="300" style="margin: 0px; padding: 0px; position: absolute; top: 50px; left: 202px;"></canvas>
@ -128,82 +68,15 @@
<center><input type="button" id="btn_CreateIC_Create" value="Create IC" disabled>&nbsp;&nbsp;&nbsp;<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">&nbsp;&nbsp;&nbsp;<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/elements.js"></script>
<script src="js/logicengine.js"></script>
<script src="js/mainevents.js"></script>
<script src="js/main.js"></script>
</body>
</html>

View File

@ -32,41 +32,25 @@ class CanvasTools {
ctx.restore();
}
drawTextCentered(ctx,x,y,x2,y2,text,fontStyle="24px Console",fontColor = "#555", Overline = false) {
drawTextCentered(ctx,x,y,x2,y2,text,fontStyle="24px Console",fontColor = "#555") {
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);
drawText(ctx,x,y,text,fontStyle="24px Console",fontColor = "#555") {
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 {
@ -93,39 +77,9 @@ class elementContainer {
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++) {
@ -150,40 +104,21 @@ class elementContainer {
} 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;
}
}
// Can pass object or Designator
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 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;
}
return false;
}
HasElement(element) {
@ -196,94 +131,59 @@ class elementContainer {
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++) {
ctx.save();
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();
}
if (this.Elements[a] == this.Selected) 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.font = "10px Console";
let x = this.Elements[a].X;
let y = this.Elements[a].Y + (this.Elements[a].Height - 12);
let x2 = this.Elements[a].Width;
let y2 = 10;
//this.Elements[a].drawTextCentered(ctx, x, y, x2, y2, this.Elements[a].Designator, ctx.font, "#000");
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";
}
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]);
// Not ideal to loop twice but we need the connections drawn all at once to prevent layer issues
this.Elements[a].drawConnections(ctx, settings);
}
this.Selected = selectedArray;
}
Select(element) {
this.Selected = new Array(element);
this.Selected = element;
let PropertiesBox = document.getElementById("PropertiesBox");
let PropertiesBoxTitle = document.getElementById("PropertiesBoxTitle");
let PropertiesBoxContent = document.getElementById("PropertiesBoxContent");
PropertiesBoxTitle.innerText = this.Selected[0].Designator + " Properties";
PropertiesBoxTitle.innerText = this.Selected.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;
for (let a = 0; a < this.Selected.Properties.length;a++) {
contentString += "<tr><td>" + this.Selected.Properties[a].Name + "</td><td>";
switch (this.Selected.Properties[a].Type) {
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) +");'>";
contentString += "<input type='number' id='prop_" + this.Selected.Properties[a].Name + "' min='" + this.Selected.Properties[a].Minimium + "' max='" + this.Selected.Properties[a].Maximium + "' value='" + this.Selected.Properties[a].CurrentValue + "' onchange='logicEngine.PropertyChange(" + String.fromCharCode(34) + this.Selected.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) + ');">';
contentString += '<input type="text" id="prop_' + this.Selected.Properties[a].Name + '" minlength="' + this.Selected.Properties[a].Minimium + '" maxlength="' + this.Selected.Properties[a].Maximium + '" value="' + this.Selected.Properties[a].CurrentValue + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected.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) + ');">';
contentString += '<input type="color" id="prop_' + this.Selected.Properties[a].Name + '" value="' + this.Selected.Properties[a].CurrentValue + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected.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 id="prop_' + this.Selected.Properties[a].Name + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected.Properties[a].Name + String.fromCharCode(39) + ');">';
for (let b = 0; b < this.Selected.Properties[a].Values.length; b++) {
contentString += '<option value="' + this.Selected.Properties[a].Values[b].Value + '" '+ ((this.Selected.Properties[a].Values[b].Value == this.Selected.Properties[a].CurrentValue) ? ' selected' : '') + '>' + this.Selected.Properties[a].Values[b].String + '</option>';
}
contentString += '</select>';
break;

2020
js/elements.js Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,20 +1,16 @@
function addElement(RestoreData = null,refClass,props) {
let newElement = new refClass(logicEngine.ActiveContainer,RestoreData,logicEngine,...props);
let newElement = new refClass(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;
let x = Math.round(logicEngine.Canvas.width / 2);
let y = Math.round(logicEngine.Canvas.height / 2);
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++;
while (noclearspot) {
if (!logicEngine.ActiveContainer.checkOverlayBounds(x, y, width, height)) {
noclearspot = false;
} else {
@ -26,6 +22,7 @@ function addElement(RestoreData = null,refClass,props) {
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;
@ -33,26 +30,6 @@ function addElement(RestoreData = null,refClass,props) {
}
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;
}
@ -133,64 +110,6 @@ function length2D(x1,y1,x2,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 = "";
@ -249,50 +168,14 @@ function isVersionNewer(version1,version2,orEqual = 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;
}
@ -310,116 +193,6 @@ function download(filename, savestate) {
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();
@ -430,13 +203,13 @@ function loadContainer(Elements) {
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);
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]);
let newElement = new classRef(Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]);
newContainer.AddElement(newElement);
newElement.Designator = Elements[a].Designator;
@ -468,6 +241,10 @@ function loadContainer(Elements) {
}
// Now we need to make all of the connections
for (let a = 0; a < elementConnections.length; a++) {
if (elementConnections[a].FromElement.Name == "1B_ADD") {
console.log("Making connection " + a);
console.log(elementConnections[a]);
}
let toElement = newContainer.HasElement(elementConnections[a].ToElement);
if (toElement) {
if (elementConnections[a].FromContainer) {
@ -481,6 +258,12 @@ function loadContainer(Elements) {
if (newConnection) {
//elementConnections[a].FromElement.OutputConnections.push(newConnection);
elementConnections[a].FromElement.addConnection(newContainer,toElement,elementConnections[a].Input, elementConnections[a].Output)
if (elementConnections[a].FromElement.Name == "1B_ADD") {
console.log("Making Connection");
console.log(newConnection);
console.log(elementConnections[a].FromElement);
console.log(elementConnections[a].FromElement.OutputConnections);
}
} else {
console.log("We dont have a new connection!!!");
console.log(toElement);
@ -506,21 +289,8 @@ 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;
}

View File

@ -1,9 +1,7 @@
class LogicEngineSettings {
constructor(restoresettings = false) {
constructor() {
this.ActiveConnectionColor = "#aabbaa";
this.ActiveConnectionHoverColor = "#ccffcc";
this.InactiveConnectionColor = "#bbaaaa";
this.InactiveConnectionHoverColor = "#ffcccc";
this.LinkWidth = "2";
this.LinkDash = [];
this.LinkingConnectionColor = "#aabbbb";
@ -12,370 +10,80 @@ class LogicEngineSettings {
this.ShadowColor = "#222";
this.InputCircleSize = 10;
this.OutputCircleSize = 10;
this.ShowGrid = true;
this.SnapGrid = true;
this.TopConnections = true;
this.HideConnections = false;
this.GridSize = 20;
this.ShowFPS = true;
this.Keybindings = {
FileNew: {Key: "n", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "New Design",
Description: "Start a new design",
Category: "File"},
FileOpen: {Key: "o", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Open Design",
Description: "Open a design",
Category: "File"},
FileSave: {Key: "s", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Save Design",
Description: "Save current design",
Category: "File"},
EditUndo: {Key: "z", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Undo",
Description: "Steps back through the last things you have done reversing them",
Category: "Edit"},
EditRedo: {Key: "y", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Redo",
Description: "Reverse any changes done via Redo",
Category: "Edit"},
DeleteElements: {Key: "delete",
Alt: false,
Shift: false,
Ctrl: false,
Meta: false,
Name: "Delete Element(s)",
Description: "Delete currently selected elements",
Category: "Edit"},
DisconnectElements: {Key: "d",
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Disconnect Element(s)",
Description: "Disconnect currently selected elements",
Category: "Edit"},
SelectAll: {Key: "a", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Select All",
Description: "Select all elments in the design",
Category: "Edit"},
ResetCanvas: {Key: "home", // lowercase
Alt: false,
Shift: false,
Ctrl: false,
Meta: false,
Name: "Reset View",
Description: "Resets the pan back to the default location",
Category: "View"},
ShowConnections: {Key: "c", // lowercase
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Toggle Connections",
Description: "Show / Hide connections",
Category: "View"},
ConnectionLayer: {Key: "c", // lowercase
Alt: false,
Shift: true,
Ctrl: true,
Meta: false,
Name: "Connection Layer",
Description: "Toggle connections above / below elements",
Category: "View"},
ShowGrid: {Key: "g", // lowercase
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Toggle Grid",
Description: "Show / Hide the grid",
Category: "View"},
SnapGrid: {Key: "g", // lowercase
Alt: true,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Snap to Grid",
Description: "Snap to grid or not",
Category: "View"},
GridPlus: {Key: "+", // lowercase
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Enlarge Grid",
Description: "Make the grid larger",
Category: "View"},
GridMinus: {Key: "-", // lowercase
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Shrink Grid",
Description: "Make the grid smaller",
Category: "View"},
ShowFPS: {Key: "f3", // lowercase
Alt: false,
Shift: false,
Ctrl: false,
Meta: false,
Name: "Toggle FPS",
Description: "Show / Hide FPS counter",
Category: "View"},
CreateIC: {Key: "i", // lowercase
Alt: false,
Shift: true,
Ctrl: false,
Meta: false,
Name: "Create IC",
Description: "Turns the current design into an IC",
Category: "Tools"},
Help: {Key: "f1", // lowercase
Alt: false,
Shift: false,
Ctrl: false,
Meta: false,
Name: "Help Window",
Description: "Opens help window",
Category: "Other"},
Cut: {Key: "x", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Cut",
Description: "Cuts selected elements to clipboard",
Category: "Invisible"},
Copy: {Key: "c", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Copy",
Description: "Copy selected elements to clipboard",
Category: "Invisible"},
Paste: {Key: "v", // lowercase
Alt: false,
Shift: false,
Ctrl: true,
Meta: false,
Name: "Paste",
Description: "pastes selected elements from clipboard",
Category: "Invisible"}
/*
keybind_name: {Key: "", // lowercase
Alt: false,
Shift: false,
Ctrl: false,
Meta: false,
Name: "",
Description: "",
Category: ""}
*/
};
if (restoresettings) {
let othis = this;
Object.keys(restoresettings).forEach(function(key) {
if (key != "Keybindings") othis[key] = restoresettings[key]; //TODO: Iterate keybindings as well
});
}
}
}
class LogicEngine {
Resize(evt) {
let leftmenu = document.getElementById("left-menu");
let topbar = document.getElementById("top-bar");
let lmrect = leftmenu.getBoundingClientRect();
let tbrect = topbar.getBoundingClientRect();
leftmenu.style.height = (window.innerHeight - (tbrect.height + 2)) + "px";
this.Canvas.width = window.innerWidth - lmrect.width;
this.Canvas.height = window.innerHeight - tbrect.height;
leftmenu.style.height = (window.innerHeight - 52) + "px";
this.Canvas.width = window.innerWidth - 205;
this.Canvas.height = window.innerHeight - 50;
this.Mouse = false;
let gridPlane = document.getElementById("GridPlane");
gridPlane.style.top = tbrect.height + "px";
gridPlane.style.left = lmrect.width + "px";
this.Canvas.style.top = tbrect.height + "px";
this.Canvas.style.left = lmrect.width + "px";
gridPlane.width = this.Canvas.width;
gridPlane.height = this.Canvas.height;
this.Ctx.setTransform(1,0,0,1,0,0);
this.Ctx.translate(this.Panning.OffsetX,this.Panning.OffsetY);
if (this.Settings.ShowGrid) {
let Ctx = gridPlane.getContext("2d");
Ctx.save();
let gridWidth = this.Settings.GridSize;
for (let x = gridWidth; x < (this.Canvas.width); x += gridWidth) {
Ctx.beginPath();
Ctx.moveTo(x, 0);
Ctx.lineTo(x, this.Canvas.height);
Ctx.strokeStyle = "#777";
Ctx.lineWidth = "1";
Ctx.stroke();
}
for (let y = gridWidth; y < (this.Canvas.height); y += gridWidth) {
Ctx.beginPath();
Ctx.moveTo(0, y);
Ctx.lineTo(this.Canvas.width, y);
Ctx.lineWidth = "1";
Ctx.strokeStyle = "#777";
Ctx.stroke();
}
Ctx.restore();
let Ctx = gridPlane.getContext("2d");
Ctx.save();
let gridWidth = this.Settings.GridSize;
for (let x = gridWidth;x < (this.Canvas.width); x+= gridWidth) {
Ctx.beginPath();
Ctx.moveTo(x,0);
Ctx.lineTo(x,this.Canvas.height);
Ctx.strokeStyle = "#777";
Ctx.lineWidth = "1";
Ctx.stroke();
}
for (let y = gridWidth;y < (this.Canvas.height); y+= gridWidth) {
Ctx.beginPath();
Ctx.moveTo(0,y);
Ctx.lineTo(this.Canvas.width,y);
Ctx.lineWidth = "1";
Ctx.strokeStyle = "#777";
Ctx.stroke();
}
Ctx.restore();
}
PropertyChange(property) {
if (this.ActiveContainer.Selected.length > 1) return false;
if (!this.ActiveContainer.Selected[0].getProperty(property)) return false;
if (!this.ActiveContainer.Selected.getProperty(property)) return false;
let propElement = document.getElementById("prop_" + property);
this.ActiveContainer.Selected[0].getProperty(property).Call(propElement.value);
this.ActiveContainer.Selected.getProperty(property).Call(propElement.value);
}
Mouse_Down(evt) {
if (evt.which === 1 && !this.MultiSelectStart.InProgress) {
let mousePos = getMousePos(this.Canvas, evt);
this.MouseDownTime = performance.now();
let element = this.ActiveContainer.checkMouseBounds(mousePos);
if (element) {
this.MouseDown = true;
if (this.ActiveContainer.isSelected(element)) {
} else {
this.ActiveContainer.Select(element);
}
this.MovingElement = new Array(this.ActiveContainer.Selected.length);
for (let a = 0; a < this.ActiveContainer.Selected.length; a++) {
this.MovingElement[a] = {
StartX: this.ActiveContainer.Selected[a].X,
StartY: this.ActiveContainer.Selected[a].Y
};
this.MovingElementMouseStartX = mousePos.x;
this.MovingElementMouseStartY = mousePos.y;
}
element.MouseDown(mousePos);
} else {
this.HideOffCanvasElement();
this.MouseDown = true;
this.ActiveLink = false;
if (this.ControlPressed) {
this.Panning.StartOffsetX = this.Panning.OffsetX;
this.Panning.StartOffsetY = this.Panning.OffsetY;
this.Panning.StartX = mousePos.x;
this.Panning.StartY = mousePos.y;
} else {
let cmPos = this.getCanvasMousePos(mousePos);
this.MultiSelectStart.InProgress = true;
this.MultiSelectStart.x = cmPos.x;
this.MultiSelectStart.y = cmPos.y;
this.ActiveContainer.Selected = false;
let PropertiesBox = document.getElementById("PropertiesBox");
PropertiesBox.style.display = "none";
}
}
if (this.ActiveContainer.Selected?.length > 0) {
disableSelectedMenus(false);
} else {
disableSelectedMenus(true);
}
let mousePos = getMousePos(this.Canvas, evt);
this.MouseDownTime = performance.now();
let element = this.ActiveContainer.checkMouseBounds(mousePos);
if (element) {
this.MouseDown = true;
this.ActiveContainer.Select(element);
this.MovingElement = element;
this.MovingElementStartX = element.X;
this.MovingElementStartY = element.Y;
this.MovingElementMouseStartX = mousePos.x;
this.MovingElementMouseStartY = mousePos.y;
element.MouseDown(mousePos);
} else {
this.ActiveLink = false;
}
}
Mouse_Up(evt) {
let mousePos = getMousePos(this.Canvas, evt);
if (this.MovingElement) {
let element = this.ActiveContainer.checkMouseBounds(mousePos);
if (element) element.MouseUp(mousePos);
}
if (this.MovingElement) this.MovingElement.MouseUp(mousePos);
if (this.MovingElement && (this.MovingElement.X == this.MovingElementStartX) && (this.MovingElement.Y == this.MovingElementStartY)) {
if ((performance.now() - this.MouseDownTime) < 3000) {
// Presume this was a click
let element = this.ActiveContainer.checkMouseBounds(mousePos);
if (element) element.MouseClick(mousePos);
this.MovingElement.MouseClick(mousePos);
}
//console.log("Mouse Up");
}
if (!this.MovingElement && evt.which === 1) {
this.ActiveContainer.Selected = false;
let PropertiesBox = document.getElementById("PropertiesBox");
PropertiesBox.style.display = "none";
this.HideOffCanvasElement();
}
if (!this.MovingElement) this.ActiveContainer.Selected = false;
this.MovingElement = false;
this.MouseDown = false;
this.HideOffCanvasElement();
if (this.MultiSelectStart.InProgress) {
this.MultiSelectStart.InProgress = false;
let cmStartPos = {x: this.MultiSelectStart.x, y: this.MultiSelectStart.y};
let cmEndPos = this.getCanvasMousePos(mousePos);
this.ActiveContainer.SelectWithin((cmStartPos.x>cmEndPos.x) ? cmEndPos.x : cmStartPos.x,
(cmStartPos.y>cmEndPos.y) ? cmEndPos.y : cmStartPos.y,
(cmStartPos.x>cmEndPos.x) ? cmStartPos.x : cmEndPos.x,
(cmStartPos.y>cmEndPos.y) ? cmStartPos.y : cmEndPos.y);
}
if (this.ActiveContainer.Selected?.length > 0) {
disableSelectedMenus(false);
} else {
disableSelectedMenus(true);
}
}
HideOffCanvasElement() {
let floatingCanvasDiv = document.getElementById("floatCanvas");
floatingCanvasDiv.style.display = "none";
}
DrawOffCanvasElement(evt) {
let rect = this.Canvas.getBoundingClientRect();
let floatingCanvasDiv = document.getElementById("floatCanvas");
let floatingCanvas = floatingCanvasDiv.getElementsByTagName("canvas")[0];
let floatingCtx = floatingCanvas.getContext("2d");
if ((evt.clientX >= (rect.left - (this.ActiveContainer.Selected[0].Width/2)))) {
floatingCanvasDiv.style.display = "none";
} else {
floatingCanvas.width = this.ActiveContainer.Selected[0].Width;
floatingCanvas.height = this.ActiveContainer.Selected[0].Height;
floatingCtx.clearRect(0, 0, floatingCanvas.width, floatingCanvas.height);
//console.log(this.ActiveContainer.Selected[0]);
floatingCtx.drawImage(this.ActiveContainer.Selected[0].StaticCanvas, 0, 0);
floatingCanvasDiv.style.left = (evt.clientX - (this.ActiveContainer.Selected[0].Width / 2)) + "px";
floatingCanvasDiv.style.top = (evt.clientY - (this.ActiveContainer.Selected[0].Height / 2)) + "px";
floatingCanvasDiv.style.display = "block";
}
}
Mouse_Move(evt) {
@ -385,44 +93,21 @@ class LogicEngine {
if(this.MouseDown) {
//console.log('Mouse at position: ' + mousePos.x + ',' + mousePos.y);
if (this.MovingElement) {
if (this.MovingElement.length == 1) this.DrawOffCanvasElement(evt);
if ((performance.now() - this.MouseDownTime) > 100) {
let xOffset = mousePos.x - this.MovingElementMouseStartX;
let yOffset = mousePos.y - this.MovingElementMouseStartY;
for (let a = 0; a < this.ActiveContainer.Selected.length; a++) {
let diffxOffset = this.MovingElementMouseStartX - this.MovingElement[a].StartX;
let diffyOffset = this.MovingElementMouseStartY - this.MovingElement[a].StartY;
let actualPosX = (this.MovingElementMouseStartX + xOffset) - diffxOffset;
let actualPosY = (this.MovingElementMouseStartY + yOffset) - diffyOffset;
if (!this.ControlPressed && this.Settings.SnapGrid) actualPosX = Math.round(actualPosX / this.Settings.GridSize) * this.Settings.GridSize;
if (!this.ControlPressed && this.Settings.SnapGrid) actualPosY = Math.round(actualPosY / this.Settings.GridSize) * this.Settings.GridSize;
this.ActiveContainer.Selected[a].X = actualPosX;
this.ActiveContainer.Selected[a].Y = actualPosY;
this.ActiveContainer.Selected[a].redraw = true;
}
}
} else {
if (this.ControlPressed) {
let distX = mousePos.x - this.Panning.StartX;
let distY = mousePos.y - this.Panning.StartY;
this.Panning.StartX += distX;
this.Panning.StartY += distY;
this.Panning.OffsetX += distX;
this.Panning.OffsetY += distY;
this.Ctx.translate(distX, distY);
let diffxOffset = this.MovingElementMouseStartX - this.MovingElementStartX;
let diffyOffset = this.MovingElementMouseStartY - this.MovingElementStartY;
let actualPosX = (this.MovingElementMouseStartX + xOffset) - diffxOffset;
let actualPosY = (this.MovingElementMouseStartY + yOffset) - diffyOffset;
if (!this.ControlPressed) actualPosX = Math.round(actualPosX/this.Settings.GridSize)*this.Settings.GridSize;
if (!this.ControlPressed) actualPosY = Math.round(actualPosY/this.Settings.GridSize)*this.Settings.GridSize;
this.MovingElement.X = actualPosX;
this.MovingElement.Y = actualPosY;
}
}
} else {
this.ActiveContainer.checkMouseBounds(mousePos);
if (!this.MovingElement) this.HideOffCanvasElement();
}
}
@ -438,7 +123,6 @@ class LogicEngine {
this.ControlPressed = true;
}
if (document.activeElement.tagName.toLowerCase() == "input") return; // Dont interupt a textbox
if (evt.key == "Escape") {
if (this.MovingElement && this.MouseDown) {
this.MovingElement.X = this.MovingElementStartX;
@ -446,95 +130,10 @@ class LogicEngine {
this.MovingElement = false;
}
}
if (MatchesPress(this.Settings.Keybindings.FileNew,evt)) {
evt.preventDefault();
tfm_New.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.FileOpen,evt)) {
evt.preventDefault();
alert("It is not possible to do a dialog box file open with javascript keyboard shortcuts, however this feature will work soon once server storage is implemented! Until then you will have to click on Open.");
}
if (MatchesPress(this.Settings.Keybindings.FileSave,evt)) {
evt.preventDefault();
tfm_Save.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.ResetCanvas,evt)) {
evt.preventDefault();
tfm_Pan2Center.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.ShowConnections,evt)) {
evt.preventDefault();
tfm_ShowConns.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.ConnectionLayer,evt)) {
evt.preventDefault();
tfm_ConnLayer.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.SelectAll,evt)) {
evt.preventDefault();
tfm_SelectAll.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.ShowGrid,evt)) {
evt.preventDefault();
tfm_ShowGrid.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.SnapGrid,evt)) {
evt.preventDefault();
tfm_SnapGrid.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.GridPlus,evt)) {
evt.preventDefault();
in_GridSize.value = logicEngine.Settings.GridSize + Math.ceil(logicEngine.Settings.GridSize * 0.05);
in_GridSize.dispatchEvent(new Event('change'));
}
if (MatchesPress(this.Settings.Keybindings.GridMinus,evt)) {
evt.preventDefault();
in_GridSize.value = ((logicEngine.Settings.GridSize - Math.ceil(logicEngine.Settings.GridSize * 0.05)) < 2) ? 2 : logicEngine.Settings.GridSize - Math.ceil(logicEngine.Settings.GridSize * 0.05) ;
in_GridSize.dispatchEvent(new Event('change'));
}
if (MatchesPress(this.Settings.Keybindings.ShowFPS,evt)) {
evt.preventDefault();
tfm_ShowFPS.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.CreateIC,evt)) {
evt.preventDefault();
tfm_CreateIC.click(evt);
}
if (MatchesPress(this.Settings.Keybindings.Help,evt)) {
evt.preventDefault();
ShowHelp();
}
if (MatchesPress(this.Settings.Keybindings.DeleteElements,evt)) {
if (this.ActiveContainer.Selected?.length > 0) {
if (evt.key == "Delete") {
if (this.ActiveContainer.Selected) {
this.ActiveContainer.DeleteElement(this.ActiveContainer.Selected);
this.ActiveContainer.Selected = false;
let PropertiesBox = document.getElementById("PropertiesBox");
PropertiesBox.style.display = "none";
}
}
if (MatchesPress(this.Settings.Keybindings.DisconnectElements,evt)) {
if (this.ActiveContainer.Selected?.length > 0) {
for (let a = 0; a < logicEngine.ActiveContainer.Selected.length; a++) {
logicEngine.ActiveContainer.Selected[a].Disconnect();
}
logicEngine.ActiveContainer.Disconnect(logicEngine.ActiveContainer.Selected);
}
}
}
@ -543,13 +142,7 @@ class LogicEngine {
this.Canvas = canvas;
this.Ctx = canvas.getContext("2d");
let restoresettings = false;
if (localStorage.getItem("LogicEngineSettings")) {
restoresettings = JSON.parse(localStorage.getItem("LogicEngineSettings"));
console.log("Restoring Settings");
}
this.Settings = new LogicEngineSettings(restoresettings);
this.Settings = new LogicEngineSettings;
this.FPSCounter = 0;
this.FPS = 0;
this.PotentialFPS = 0;
@ -560,6 +153,8 @@ class LogicEngine {
this.MouseDownTime = 0;
this.MovingElementContainer = false;
this.MovingElement = false;
this.MovingElementStartX = 0;
this.MovingElementStartY = 0;
this.MovingElementMouseStartX = 0;
this.MovingElementMouseStartY = 0;
this.ActiveContainer = new elementContainer();
@ -569,68 +164,42 @@ class LogicEngine {
this.RecursionError = false;
this.Canvas.setAttribute('tabindex','0');
this.ControlPressed = false;
this.Panning = {OffsetX: 0, OffsetY: 0,StartOffsetX: 0, StartOffsetY: 0, StartX: 0, StartY: 0};
this.MultiSelectStart = {
x: 0,
y: 0,
InProgress: false
};
}
Link(input = 0) {
if (this.ActiveLink) {
if ((this.ActiveContainer.Selected?.length == 1) && (this.ActiveContainer.Selected?.[0] != this.ActiveLink)) {
this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected[0],input,this.ActiveLink.OutputLink.Output);
if (this.ActiveContainer.Selected && (this.ActiveContainer.Selected != this.ActiveLink)) {
this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected,input,this.ActiveLink.OutputLink.Output);
this.ActiveLink = false;
} else {
this.ActiveLink = false;
}
} else {
if (this.ActiveContainer.Selected.length == 1) {
if (!this.ActiveContainer.Selected[0].NoOutput) this.ActiveLink = this.ActiveContainer.Selected[0];
if (this.ActiveContainer.Selected) {
this.ActiveLink = this.ActiveContainer.Selected;
}
}
}
getCanvasMousePos(mousePos) {
return {x: mousePos.x - this.Panning.OffsetX, y: mousePos.y - this.Panning.OffsetY};
}
RedrawStatics() {
for (let a = 0; a < this.ActiveContainer.Elements.length; a++) {
this.ActiveContainer.Elements[a].drawElement(0,0,this.ActiveContainer.Elements[a].StaticCtx);
}
}
DrawLoop() {
if (this.RecursionError) {
this.RecursionError = false;
alert("Recursion Error! Whatever you last did is causing an oscillating loop, please check your connections and try again!");
}
let startLoop = performance.now();
this.Ctx.clearRect(0- this.Panning.OffsetX,0- this.Panning.OffsetY,this.Canvas.width,this.Canvas.height);
let ct = new CanvasTools();
if (this.MultiSelectStart.InProgress) {
let cmPos = this.getCanvasMousePos(this.Mouse);
ct.drawBorderBox(this.Ctx,this.MultiSelectStart.x,this.MultiSelectStart.y,cmPos.x - this.MultiSelectStart.x,cmPos.y - this.MultiSelectStart.y,0,"rgba(100,200,255,0.25)","rgba(100,200,255,0.25)");
}
this.Ctx.clearRect(0,0,this.Canvas.width,this.Canvas.height);
this.ActiveContainer.DrawAll(this.Ctx,this.Settings);
let tfm_CreateIC = document.getElementById("tfm_CreateIC");
let rcm_CreateIC = document.getElementById("rcm_CreateIC");
tfm_CreateIC.classList.add("disabled");
rcm_CreateIC.classList.add("disabled");
if (this.ActiveContainer.ICOutputs > 0) tfm_CreateIC.classList.remove("disabled");
if (this.ActiveContainer.ICOutputs > 0) rcm_CreateIC.classList.remove("disabled");
let btn_CreateIC = document.getElementById("btn_CreateIC");
btn_CreateIC.disabled = true;
if (this.ActiveContainer.ICOutputs > 0) btn_CreateIC.disabled = false;
if (this.ActiveLink) {
let startX = this.ActiveLink.LinkOutLocation().x;
let startY = this.ActiveLink.LinkOutLocation().y;
let endX = this.Mouse.x - this.Panning.OffsetX;
let endY = this.Mouse.y - this.Panning.OffsetY;
let endX = this.Mouse.x;
let endY = this.Mouse.y;
let startMidX = startX + ((endX - startX)/2);
let startMidY = startY;
let midX = startMidX;
@ -638,7 +207,6 @@ class LogicEngine {
let endMidX = startMidX;
let endMidY = endY;
this.Ctx.save();
this.Ctx.strokeStyle = this.Settings.LinkingConnectionColor;
this.Ctx.lineWidth = this.Settings.LinkingWidth;
@ -650,18 +218,15 @@ class LogicEngine {
this.Ctx.stroke();
this.Ctx.restore();
}
let FPSOffset = 5 - this.Panning.OffsetX;
if (this.Settings.ShowFPS) {
ct.drawText(this.Ctx, FPSOffset, 15 - this.Panning.OffsetY, "FPS: " + this.FPS, "12px console", "#00ff00");
ct.drawText(this.Ctx, FPSOffset, 29 - this.Panning.OffsetY, "Draw Speed: " + Math.floor(this.frameTimeUS) + "uS", "12px console", "#00ff00");
ct.drawText(this.Ctx, FPSOffset, 43 - this.Panning.OffsetY, "Potential FPS: " + Math.floor(this.PotentialFPS), "12px console", "#00ff00");
}
let ct = new CanvasTools();
let FPSOffset = this.Canvas.width - 150;
ct.drawText(this.Ctx,FPSOffset,650,"FPS: " + this.FPS,"12px console", "#00ff00");
ct.drawText(this.Ctx,FPSOffset,670,"Potential FPS: " + this.PotentialFPS,"12px console", "#00ff00");
let timeCheck = performance.now();
this.FPSCounter++;
if (!(Math.round(timeCheck - this.LastFPSCheck) % 50)) {
let frameTimeUS = (performance.now() - startLoop) * 1000;
this.frameTimeUS = frameTimeUS;
let potentialFPS = 1000000 / frameTimeUS;
this.PotentialFPSAVGs[this.PotentialFPSAVGLoc] = Math.round(potentialFPS);
this.PotentialFPSAVGLoc++;
@ -682,6 +247,6 @@ class LogicEngine {
StartEngine() {
this.Resize("");
this.DrawLoop(); // Get the animation loop going
this.DrawLoop();
}
}

View File

@ -2,23 +2,138 @@
MatCat BrowserLogic Simulator
*/
let Version = "0.4.16";
let Version = "0.3.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);
window.addEventListener('resize', function(evt) {
logicEngine.Resize(evt);
}, false);
window.addEventListener('keydown', function(evt) {
logicEngine.Key_Press(evt);
}, false);
window.addEventListener('keyup', function(evt) {
logicEngine.Key_Up(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_CreateIC = document.getElementById("btn_CreateIC");
btn_CreateIC.addEventListener('click', function(evt) {
let CreateICBox = document.getElementById("CreateICBox");
CreateICBox.style.display = "block";
});
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_New = document.getElementById("btn_New");
btn_New.addEventListener('click', function(evt) {
logicEngine.ActiveContainer = new elementContainer();
});
let btn_Save = document.getElementById("btn_Save");
btn_Save.addEventListener('click', function(evt) {
download("mydeign.LogicParts",createSaveState(logicEngine.ActiveContainer));
});
let file_Load = document.getElementById("file_Load");
let btn_Load = document.getElementById("btn_Load");
btn_Load.addEventListener('click', function(evt) {
file_Load.click();
});
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);
console.log(restoredata);
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!");
console.log(ex);
}
}
})(evt.target.files[0]);
fread.readAsText(evt.target.files[0]);
}, false);
CheckForWelcomeCookie();

View File

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

View File

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

View File

@ -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 = "&nbsp;";
} else {
let sgSpan = tfm_ShowConns.getElementsByClassName("checkbox")[0];
sgSpan.innerText = "✓";
}
if (!logicEngine.Settings.TopConnections) {
tfm_ConnLayer.innerHTML = '<span class="blankbox">&nbsp;</span>Connections Above';
} else {
tfm_ConnLayer.innerHTML = '<span class="blankbox">&nbsp;</span>Connections Below';
}
if (!logicEngine.Settings.ShowFPS) {
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
sgSpan.innerHTML = "&nbsp;";
} else {
let sgSpan = tfm_ShowFPS.getElementsByClassName("checkbox")[0];
sgSpan.innerText = "✓";
}
if (!logicEngine.Settings.ShowGrid) {
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
sgSpan.innerHTML = "&nbsp;";
} else {
let sgSpan = tfm_ShowGrid.getElementsByClassName("checkbox")[0];
sgSpan.innerText = "✓";
}
if (!logicEngine.Settings.SnapGrid) {
let sgSpan = tfm_SnapGrid.getElementsByClassName("checkbox")[0];
sgSpan.innerHTML = "&nbsp;";
} 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 = "&nbsp;";
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 = "&nbsp;";
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 = "&nbsp;";
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 = "&nbsp;";
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();
});
}

View File

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

File diff suppressed because one or more lines are too long