├── LICENSE.txt ├── README.md ├── dev_plan.md ├── examples ├── binary_adder.net ├── binary_scaler.net ├── chained_scaler.net ├── memory_gate.net └── unary_scaler.net ├── img └── mcp.gif ├── index.html ├── neural_nets.js └── papers ├── In a Frog’s Eye.pdf ├── kleene.pdf ├── l2_JB.pdf ├── letvin_ieee_1959.pdf ├── mcp.pdf ├── minsky_1.gif ├── minsky_2.gif ├── pitts1947.pdf └── smalheiser_pitts_2000.pdf /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [neural-nets-sim](https://justinmeiners.github.io/neural-nets-sim) 2 | 3 | Created by: [Ryan Pendleton](https://github.com/rpendleton) & [Justin Meiners](https://github.com/justinmeiners) 4 | 5 | ![neural nets 1](papers/minsky_1.gif) 6 | 7 | ![neural nets 2](papers/minsky_2.gif) 8 | 9 | Pages from [Computation: finite and infinite machines](https://dl.acm.org/citation.cfm?id=1095587) by Marvin Minsky. 10 | 11 | # License 12 | 13 | This code is licensed under [GPL v2](LICENSE.txt). 14 | 15 | # Neural Net List 16 | 17 | This is a list where you can contribute your own neural nets. Simply host them some place that is publically accessible (Like a Gist) and add a link to it: 18 | 19 | ``` 20 | https://justinmeiners.github.io/neural-nets-sim/?d=https://yourlinkhere 21 | ``` 22 | 23 | - [Gist Sample](https://justinmeiners.github.io/neural-nets-sim/?d=https://gist.githubusercontent.com/justinmeiners/8f02ad348f577eb0fc29d64fccde94a3/raw/b1804996b41ab811c1976dde77f9af2dbf86bbbf/sample_net.net) Shows you how to host a Neural Net in a Gist. By [justinmeiners](https://github.com/justinmeiners). 24 | -------------------------------------------------------------------------------- /dev_plan.md: -------------------------------------------------------------------------------- 1 | ## Simulation 2 | 3 | ### Data Structures 4 | 5 | ***Cell*** 6 | - input fibers[] 7 | - input fiber types[] (whether inhibitatory or not) 8 | - output fiber 9 | - threshold (N) 10 | - firing (boolean) 11 | 12 | ***Branch*** 13 | - (no fire delay) 14 | - input fiber 15 | - output fibers[] 16 | 17 | ***Fiber*** 18 | - input 19 | - output 20 | 21 | ## State 22 | - which cells were firing 23 | - does not depend on wiring, or anything else 24 | 25 | ### Algorithms 26 | 27 | ``` 28 | propogate: state -> state 29 | for each cell that is firing: 30 | mark all connected fibers as firing 31 | stop firing 32 | 33 | for each cell 34 | look at all connected fibers 35 | if inhibitory 36 | stay quite 37 | if above threshold 38 | then fire 39 | ``` 40 | 41 | ## Display 42 | 43 | ## Data Structures 44 | 45 | CellView 46 | - x, y 47 | - angle 48 | - size 49 | 50 | FiberView 51 | - path points 52 | 53 | BranchView 54 | - x, y 55 | 56 | ## Algorithms 57 | 58 | ``` 59 | render: state -> state -> View -> Canvas 60 | 61 | 62 | ``` 63 | 64 | ## Interaction 65 | 66 | Selection 67 | - drag rectangle to select cells and branches 68 | 69 | Create Cell/Branch 70 | - Right click with no selection, and pick "Create Cell/Branch" 71 | 72 | Delete Cell/branch 73 | - Right click with selection, and delete 74 | 75 | Create a fiber 76 | - each cell and branch shows the output slots 77 | - the user clicks with their mouse on the area around the output, 78 | and starts dragging to create a fiber. When they are over an input 79 | area they release the mouse to create a connection. 80 | 81 | Delete a fiber 82 | - 83 | 84 | 85 | Rotate a cell 86 | 87 | 88 | Adjust cell threshold 89 | 90 | ## Tools 91 | 92 | 93 | Select Mode 94 | - click on nodes to move around 95 | - type number to change threshold 96 | 97 | Cell Mode 98 | - click to make nodes 99 | - right click to delete nodes 100 | 101 | Branch Mode 102 | - click to make branches 103 | - right click to delete branches 104 | 105 | Wiring Mode 106 | - click and drag to make wires 107 | - right click to delete wires 108 | 109 | 110 | -------------------------------------------------------------------------------- /examples/binary_adder.net: -------------------------------------------------------------------------------- 1 | +AICABEAGgALAC0BewABAAAAAgAGAAAACQAAAAEAAwAnAbgAAgAAAAIABwAAAAoAAAAEAAIAFQAWABcAJAH+AAMAAAACAAgAAAALAAAAAQAEAIUBfAABAAAAAgACAAEAAwAAAAEAAQCiAbwAAQAAAAEABAAAAAEAAAA5Ao8AAQAAAAIAAAAAAAEAAAAAAJcAYgAAAAAAAAADAAYABwAIAJEAegEAAAAAAAADAAkACgALAEwC3AEBAAAAAQAFAAAAAACUAKEAAQAAAAAAAwAMAA4ADwCPAL4BAQAAAAAAAwANABAAEQBSAWABAQAAAAMADAAAABEAAAAVAAAAAQATAFIBmgECAAAAAwAOAAAAEAAAABYAAAACABIABQBRAd0BAwAAAAMADQAAAA8AAAAXAAAAAQAYALUBXwEBAAAAAgASAAEAEwAAAAEAFABHAgEBAQAAAAIAFAAAABkAAAAAAL8BmAEBAAAAAQAYAAAAAQAZAAQABQADAAUAAQADAAAAAwACAAQADAAIAAYAAAAGAAEABgACAAcAAAAHAAEABwACAAkACwAKAA0ACQAMAAkADQAKAAwACgALAAwADgALAA4ADgAPAAEACwABAAwAAQANAA0AEAAQAA8AuQEUABcAkwJIAAYARACkAAcAlgLdAQUAeAAlABEAjgAJAhEARQB5AQcARADDAQcARwBiAAcAmAKLAAcAmgL+AAcAQgBpAG4AYQByAHkAIABBAGQAZABlAHIAIAAoADIAIABkAGkAZwBpAHQAcwApAE8AdQB0AHAAdQB0AEQAaQBnAGkAdAAgADIAQwBhAHIAcgB5AEkAbgBwAHUAdAA6ACAAMQBzAHQAIABOAHUAbQBiAGUAcgBJAG4AcAB1AHQAOgAgADIAbgBkACAATgB1AG0AYgBlAHIARABpAGcAaQB0ACAAMQBEAGkAZwBpAHQAIAAyAEQAaQBnAGkAdAAgADEARABpAGcAaQB0ACAAMQBEAGkAZwBpAHQAIAAyAA== 2 | -------------------------------------------------------------------------------- /examples/binary_scaler.net: -------------------------------------------------------------------------------- 1 | YAECAAYADgAEAFkBsAABAAAABQAAAAAAAgAAAAUAAQAHAAAACwABAAIAAAAGAMYB6AACAAAABQABAAAAAwABAAYAAAAIAAAADAABAAQAAwAEAAUACgB+ATMBAgAAAAMABAAAAAkAAAANAAEAAgABAAIAsQCrAAAAAAAAAAMABwAIAAkAVQLwAAEAAAABAAoAAAAAALgAZAEBAAAAAAADAAsADAANAAAAAAACAAEAAgAAAAEAAQABAAIAAQAAAAAAAQADAAAAAwABAAMAAgABAAQABQAAAAUAAQAFAAIATgKsAAYApQB8AAUArAAyAQQAewE4ACYATwB1AHQAcAB1AHQASQBuAHAAdQB0AFMAdABvAHAAQgBpAG4AYQByAHkAIABTAGMAYQBsAGUAcgA6ACAATwBuAGUAIABwAHUAbABzAGUAIABmAG8AcgAgAGUAdgBlAHIAeQAgAHQAdwBvAA== 2 | -------------------------------------------------------------------------------- /examples/chained_scaler.net: -------------------------------------------------------------------------------- 1 | HgMCABIALAAEAOcAdAABAAAABQAAAAAAAgAAAAUAAQAHAAAACwABAAIAAAAGACwBnwACAAAABQABAAAAAwABAAYAAAAIAAAADAABAAQAAwAEAAUADwDwANQAAgAAAAMABAAAAAkAAAAKAAEAAgABAAIAiACMAAEAAAABAA0AAAADAAcACAAJAIgAxAABAAAAAQAOAAAAAwALAAwACgA2AIEAAAAAAAAAAQANAFIAXAEBAAAAAAADAA4AHAAmABwBGQEBAAAAAQAPAAAAAwASABMAFAB/AfEAAQAAAAUAEAAAABIAAAAWAAAAGAABABkAAQACABAAFQDNAScBAgAAAAQAEQABABMAAAAVAAAAGgABAAQAEQAXABgAHwB9AVcBAgAAAAMAFAAAABcAAAAbAAEAAQAWAPgAYwEBAAAAAQAcAAAAAwAZABoAGwDeAaABAQAAAAUAHQAAACEAAAAlAAEAJwABACoAAAACAB0AIAA1AtUBAgAAAAQAHgABACAAAAAjAAAAKAABAAQAHgAkACUAKwB5AcYBAQAAAAEAHwAAAAMAIQAiACMA5AEFAgIAAAADACIAAAAkAAAAKQABAAEAKgB2Af4BAQAAAAEAJgAAAAMAJwAoACkAwwIvAQEAAAABACsAAAAAAAAAAAACAAEAAgAAAAEAAQABAAIAAQAAAAAAAQADAAAAAwABAAMAAgAEAAIABAAAAAQAAQAFAAMABgAEAAEABwAIAAgACQAJAAcACAAHAAkABwAKAAgACQAKAAgACQAKAAkACAALAAgACwAJAAsACgAGAAsADAAMAA0ADQAJAA4ADAANAA4ADAAOAA8ADgANAA0ADwANAAwABgAQABAADAAQAA0AEAAPAA8ADAANABEAkwEbACcANwBTAAUAwgL+AAYAUACEAQQAUwBlAHYAZQByAGEAbAAgAGIAaQBuAGEAcgB5ACAAcwBjAGEAbABhAHIAcwAgAGMAaABhAGkAbgBlAGQAIAB0AG8AZwBlAHQAaABlAHIASQBuAHAAdQB0AE8AdQB0AHAAdQB0AFMAdABvAHAA 2 | -------------------------------------------------------------------------------- /examples/memory_gate.net: -------------------------------------------------------------------------------- 1 | PgECAAYABgAGAGwBpAAAAAAAAAABAAEAcAH3AAEAAAADAAAAAAAEAAAABQABAAIAAAACAMIB0gACAAAAAgABAAAAAgAAAAEAAwA3As0AAQAAAAEAAwAAAAAA1AAEAQEAAAAAAAEABADeAG4BAQAAAAAAAQAFAAEAAQAAAAIAAQACAAIAAwAEAAEABQABAF0ApgAAALgA2wEAALIAsgEQAIsBLAAhALIAxgAPAJgCygAGAEkAbgBwAHUAdAA6ACAAdAB1AHIAbgAgAG8AZgBmAC4ATQBlAG0AbwByAHkAIABjAGUAbABsADoAIABzAHQAbwByAGUAcwAgAGEAIABzAGkAbgBnAGwAZQAgAGIAaQB0AC4ASQBuAHAAdQB0ADoAIAB0AHUAcgBuACAAbwBuAC4ATwB1AHQAcAB1AHQA 2 | -------------------------------------------------------------------------------- /examples/unary_scaler.net: -------------------------------------------------------------------------------- 1 | 1AECAAcAEwAEAP4AsQABAAAAAwAAAAAACAABAAkAAAACAAAADgBhAbgAAgAAAAQAAQAAAAcAAQAKAAAADgAAAAIAAQAPAL0BuQACAAAABAACAAAABgABAAsAAAAPAAAAAgACABAAFwLJAAIAAAAEAAMAAAAFAAEADAAAABAAAAACAAMAEQC1AXEBAgAAAAMABAABABEAAAASAAAABgAEAAUABgAHAAgADQBrACQBAAAAAAAABQAJAAoACwAMABIAiwIpAQEAAAABAA0AAAAAAAAAAAABAAEAAgACAAMAAwAEAAQABAADAAQAAgAEAAEABAAAAAUAAAAFAAEABQACAAUAAwAEAAYAAAABAAEAAgACAAMAAwAEAAUABACAAUoAIQBvAOsABQCLAv0ABgBCAdABJABVAG4AYQByAHkAIABTAGMAYQBsAGEAcgA6ACAAMQAgAHAAdQBsAHMAZQAgAGYAbwByACAAZQB2AGUAcgB5ACAANgBJAG4AcAB1AHQATwB1AHQAcAB1AHQAVAByAHkAIAB0AGgAZQAgAHMAdABlAHAAIAB0AG8AbwBsACAAdABvACAAcwBlAGUAIABlAGEAYwBoACAAcAB1AGwAcwBlAC4A 2 | -------------------------------------------------------------------------------- /img/mcp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/img/mcp.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | McCulloch & Pitts Neural Net Simulator 5 | 6 | 72 | 73 | 74 | 75 |
76 |

McCulloch & Pitts Neural Net Simulator

77 | 78 | 79 |
80 |
    81 |
  • New Neuron
  • 82 |
  • New Label
  • 83 |
84 |
85 | 86 |
87 |
    88 |
  • Delete
  • 89 |
  • Flip
  • 90 |
91 |
92 | 93 |
94 |
    95 |
  • Delete
  • 96 |
  • Edit
  • 97 |
98 |
99 | 100 |
101 |
    102 |
  • Delete
  • 103 |
  • Toggle
  • 104 |
105 |
106 | 107 |
108 | 109 | 110 | 111 | 112 |
113 | 114 |
115 | 116 | 117 | Use copy and paste here 118 | 119 |
120 | 121 |

How does this work?

122 | 123 |

124 | Each neuron has input fibers and output fibers which connect it to other neurons. 125 | A neuron fires after it receives a number of signals from its input fibers, greater or equal to its threshold (the number written on it.) 126 | Fibers which have a circle at the end are called inhibitory fibers. If a neuron receives an inhibitory signal, it will not fire, regardless of how many signals it receives. 127 |

128 | 129 | 130 |

Examples

131 | 132 | 151 | 152 | You can share your own neural nets too! Copy the saved text to an online location (like a Gist) 153 | and append it to the end of the URL like so: ?d={YOUR URL}. 154 | 155 | Contribute yours to the list! 156 | 157 |

What are McCulloch & Pitts Neural Nets?

158 | 159 |

160 | McCulloch and 161 | Pitts 162 | were researchers in neuroscience who became pioneers in artificial intelligence. 163 | In 1943 they created a computation model of how neurons fire in our brains. 164 | This project is a simulator of that model. 165 |

166 | 167 | 168 | neuron 169 | 170 |

171 | Even though these neural nets are inspired by biology, they aren't an accurate representation of their physical properties. They are a simplification for computational purposes. 172 | Marvin Minsky says: 173 |

174 | 175 |
176 | It should be understood clearly that neither McCulloch, Pitts, nor the present writer considers these devices and machines to serve as accurate physiological models of nerve cells and tissues. They were not designed with that purpose in mind. They are designed for the representation and analysis of the logic of situations that arise in any discrete process, be it in brain, computer, or anywhere else. In theories which are more seriously intended to be brain models, the "neurons" have to be much more complicated. The real biological neuron is much more complex than our simple 177 | logical units for the evolution of nerve cells has led to very intricate and specialized organs. 178 |
179 | 180 |

181 | Their work later became important in the Theory of Computation. In 1951, Kleene proved that the 182 | patterns which are recognizable by the neural nets are precisely the Regular Expressions. This is commonly known as Kleene's Theorem. Finite state machines and neural nets are computationally equivalent. 183 |

184 | 185 |

186 | If you want to learn more, I recommend Marvin Minsky's book: Computation: Finite & Infinite. 187 | Also see Turing's diagrams 188 | which use similar nets. 189 |

190 |
191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /neural_nets.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Created by Justin Meiners 3 | // LICENSE GPL v2.0 4 | 5 | // SIMULATION 6 | // ------------------------ 7 | 8 | var INPUT_EXCITE = 0; 9 | var INPUT_INHIBIT = 1; 10 | 11 | function Cell(i) { 12 | this.index = i; 13 | this.inputs = []; 14 | this.inputTypes = []; 15 | this.outputs = []; 16 | this.threshold = 1; 17 | } 18 | 19 | function Label(i) { 20 | this.text = ""; 21 | this.index = i; 22 | } 23 | 24 | function Fiber(i) { 25 | this.index = i; 26 | this.from = null; 27 | this.to = null; 28 | } 29 | 30 | function Branch(i) { 31 | this.index = i; 32 | this.input = null; 33 | this.outputs = []; 34 | } 35 | 36 | function Net() { 37 | this.cells = []; 38 | this.fibers = []; 39 | this.branches = []; 40 | this.labels = []; 41 | } 42 | 43 | function visitFibers(fiber, f) { 44 | var b; 45 | var i; 46 | // apply function 47 | f(fiber); 48 | 49 | // if its connected to a branch 50 | // we need to visit the other fibers 51 | if (fiber.to instanceof Branch) { 52 | b = fiber.to; 53 | for (i = 0; i < b.outputs.length; ++i) { 54 | visitFibers(b.outputs[i], f); 55 | } 56 | } 57 | } 58 | 59 | function sendSignals(net, state) { 60 | var i, j; 61 | var firing; 62 | var cell; 63 | var fiber; 64 | var signals = []; 65 | 66 | var fiberVisitor = function(fiber) { 67 | signals[fiber.index] = true; 68 | }; 69 | 70 | // send signals from cells to fibers 71 | for (i = 0; i < net.cells.length; ++i) { 72 | firing = state[i]; 73 | if (firing) { 74 | cell = net.cells[i]; 75 | for (j = 0; j < cell.outputs.length; ++j) { 76 | fiber = cell.outputs[j]; 77 | visitFibers(fiber, fiberVisitor); 78 | } 79 | } 80 | } 81 | 82 | return signals; 83 | } 84 | 85 | function applySignals(net, state, signals) { 86 | var i, j; 87 | var newState = []; 88 | var cell; 89 | var inputFiber; 90 | var inputType; 91 | var activated; 92 | var inhibited; 93 | 94 | // mark which cells are firing 95 | for (i = 0; i < net.cells.length; ++i) { 96 | cell = net.cells[i]; 97 | 98 | // count up activated 99 | // and check for inhibit 100 | activated = 0; 101 | inhibited = false; 102 | 103 | for (j = 0; j < cell.inputs.length; ++j) { 104 | inputFiber = cell.inputs[j]; 105 | inputType = cell.inputTypes[j]; 106 | 107 | if (signals[inputFiber.index]) { 108 | if (inputType === INPUT_INHIBIT) { 109 | inhibited = true; 110 | break; 111 | } else { 112 | ++activated; 113 | } 114 | } 115 | } 116 | // ignore inhibited cells 117 | if (!inhibited && activated >= cell.threshold) { 118 | // fire this cell 119 | newState[i] = true; 120 | } 121 | } 122 | 123 | return newState; 124 | } 125 | 126 | // VIEW 127 | // ------------------------ 128 | 129 | function Vec(x, y) { 130 | this.x = x; 131 | this.y = y; 132 | } 133 | 134 | 135 | Vec.prototype.lenSqr = function() { 136 | return this.x * this.x + this.y * this.y; 137 | }; 138 | 139 | Vec.prototype.len = function() { 140 | return Math.sqrt(this.lenSqr()); 141 | }; 142 | 143 | Vec.prototype.inBounds = function(min, max) { 144 | return this.x >= min.x && this.y >= min.y && 145 | this.x <= max.x && this.y <= max.y; 146 | }; 147 | 148 | Vec.prototype.inCircle = function(o, r) { 149 | return Vec.distSqr(this, o) < r * r; 150 | }; 151 | 152 | Vec.prototype.add = function(b) { 153 | this.x += b.x; 154 | this.y += b.y; 155 | return this; 156 | }; 157 | 158 | Vec.add = function(a, b) { 159 | return new Vec(a.x + b.x, a.y + b.y); 160 | }; 161 | 162 | Vec.sub = function(a, b) { 163 | return new Vec(a.x - b.x, a.y - b.y); 164 | }; 165 | 166 | Vec.scale = function(a, s) { 167 | return new Vec(a.x * s, a.y * s); 168 | }; 169 | 170 | Vec.distSqr = function(a, b) { 171 | return Vec.sub(a, b).lenSqr(); 172 | }; 173 | 174 | Vec.dist = function(a, b) { 175 | return Math.sqrt(Vec.distSqr(a, b)); 176 | }; 177 | 178 | Vec.min = function(a, b) { 179 | return new Vec(Math.min(a.x, b.x), Math.min(a.y, b.y)); 180 | }; 181 | 182 | Vec.max = function(a, b) { 183 | return new Vec(Math.max(a.x, b.x), Math.max(a.y, b.y)); 184 | }; 185 | 186 | Vec.dot = function(a, b) { 187 | return a.x * b.x + a.y * b.y; 188 | }; 189 | 190 | Vec.bezier = function(t, p1, cp1, cp2, p2) { 191 | var tInv = 1.0 - t; 192 | var a = Vec.scale(p1, tInv * tInv * tInv); 193 | var b = Vec.scale(cp1, 3.0 * tInv * tInv * t); 194 | var c = Vec.scale(cp2, 3.0 * tInv * t * t); 195 | var d = Vec.scale(p2, t * t * t); 196 | return Vec.add(a, Vec.add(b, Vec.add(c, d))); 197 | }; 198 | 199 | var ANGLE_EAST = 0; 200 | var ANGLE_WEST = 1; 201 | 202 | Vec.fromAngle = function(angle) { 203 | if (angle === ANGLE_EAST) { 204 | return new Vec(1.0, 0.0); 205 | } else if (angle === ANGLE_WEST) { 206 | return new Vec(-1.0, 0.0); 207 | } else { 208 | return new Vec(0.0, 0.0); 209 | } 210 | }; 211 | 212 | function CellView(i) { 213 | Cell.call(this, i); 214 | this.pos = new Vec(0, 0); 215 | this.angle = ANGLE_EAST; 216 | } 217 | 218 | CellView.radius = 15.0; 219 | 220 | CellView.prototype = Object.create(Cell.prototype); 221 | CellView.prototype.constructor = CellView; 222 | 223 | CellView.prototype.radius = CellView.radius; 224 | CellView.prototype.connectorPadding = 11.0; 225 | 226 | CellView.prototype.hits = function(p) { 227 | return p.inCircle(this.pos, this.radius); 228 | }; 229 | 230 | CellView.prototype.hitsConnectors = function(p) { 231 | var OUTSIDE_PADDING = 6; 232 | return p.inCircle(this.pos, this.radius + this.connectorPadding + OUTSIDE_PADDING); 233 | }; 234 | 235 | CellView.prototype.isPositionOnOutputSide = function(mousePos) { 236 | var dir = Vec.sub(mousePos, this.pos); 237 | return Vec.dot(dir, Vec.fromAngle(this.angle)) > 0.0; 238 | } 239 | 240 | function LabelView(i) { 241 | Label.call(this, i); 242 | this.pos = new Vec(0,0); 243 | } 244 | LabelView.prototype.bounds = function(fontsize) { 245 | var width = this.text.length * fontsize; 246 | var height = fontsize; 247 | var pad = 2; 248 | 249 | return { 250 | min: new Vec(this.pos.x - width / 2.0 - pad, this.pos.y - height / 2.0 - pad), 251 | max: new Vec(this.pos.x + width / 2.0 + pad, this.pos.y + height / 2.0 + pad) 252 | } 253 | }; 254 | 255 | LabelView.prototype.hits = function(mousePos, fontsize) { 256 | var bounds = this.bounds(fontsize); 257 | return mousePos.inBounds(bounds.min, bounds.max); 258 | } 259 | 260 | function FiberView(i) { 261 | Fiber.call(this, i); 262 | 263 | // don't set this. 264 | // Its driven by the inputTypes 265 | // on cell 266 | this.outputIndex = -1; 267 | 268 | // cache of 269 | // bezier curve points 270 | // [p0, cp0, cp1, p1] 271 | this.bezierPoints = new Array(4); 272 | } 273 | 274 | FiberView.prototype = Object.create(Fiber.prototype); 275 | 276 | // ATTEMPT 1: minimize distance between curve and point 277 | // I did the math for this one. 278 | // It is relatively easy to get the derivative of 279 | // D(t) = || B(t) - q ||^2 280 | // however actually minimizing that 281 | // requires solving a cubic polynomial 282 | // and I don't want to mess around with Newton's method. 283 | 284 | // ATTEMPT 2: Use isPointInStroke 285 | // Maintaining the ctx state is awkward. 286 | // It also doesn't seem to account for lineWidth 287 | // correctly so its not very usable 288 | 289 | // ATTEMPT 3: Chop the bezier curve into line segments 290 | // and minimize the distance along each line. 291 | 292 | FiberView.prototype.hits = function(q) { 293 | var p = this.bezierPoints; 294 | 295 | // early rejection 296 | // with a bounding box 297 | var min = p.reduce(Vec.min); 298 | var max = p.reduce(Vec.max); 299 | 300 | if (!q.inBounds(min, max)) { 301 | return false; 302 | } 303 | 304 | // number of segments 305 | var N = 50; 306 | 307 | // collision radius 308 | // around path 309 | var r = 9.0; 310 | // cache this 311 | var rSqr = r * r; 312 | 313 | var x0 = p[0]; 314 | var x1; 315 | var t; 316 | 317 | // l: vector in direction of line segment 318 | // d: delta from x0 to q 319 | // c: component of delta on l 320 | var l; 321 | var d; 322 | var c; 323 | 324 | for (var i = 1; i <= N; ++i) { 325 | t = i / N; 326 | 327 | // line segment from x0 to x1 along the path 328 | x1 = Vec.bezier(t, p[0], p[1], p[2], p[3]); 329 | 330 | l = Vec.sub(x1, x0); 331 | d = Vec.sub(q, x0); 332 | 333 | // project delta onto seg 334 | // two Inv sqrt :( 335 | c = Vec.scale(l, Vec.dot(l, d) / (l.len() * d.len())); 336 | 337 | // subtract projection 338 | // check if below distance 339 | if (Vec.sub(d, c).lenSqr() < rSqr) { 340 | return true; 341 | } 342 | // save previous point 343 | x0 = x1; 344 | } 345 | 346 | return false; 347 | }; 348 | 349 | function BranchView() { 350 | Branch.call(this); 351 | this.pos = new Vec(0, 0); 352 | } 353 | 354 | BranchView.prototype = Object.create(Branch.prototype); 355 | BranchView.prototype.constructor = BranchView; 356 | 357 | BranchView.prototype.radius = 5.0; 358 | 359 | function NetView() { 360 | Net.call(this); 361 | 362 | // since the NetView 363 | // is the visual component 364 | // we will store the state here 365 | // even though the underlying 366 | // structure is immutable over time 367 | this.state = []; 368 | this.signals = []; 369 | 370 | this.time = 0; 371 | } 372 | 373 | NetView.prototype = Object.create(Net.prototype); 374 | NetView.prototype.constructor = NetView; 375 | 376 | NetView.prototype.step = function() { 377 | var newState = applySignals(this, this.state, this.signals); 378 | var nextSignals = sendSignals(this, newState, this.signals); 379 | 380 | this.state = newState; 381 | this.signals = nextSignals; 382 | 383 | ++this.time; 384 | }; 385 | 386 | NetView.prototype.restart = function() { 387 | this.state = []; 388 | this.signals = []; 389 | this.time = 0; 390 | }; 391 | 392 | NetView.prototype.addTextLabel = function() { 393 | var label = new LabelView(this.labels.length); 394 | this.labels.push(label); 395 | return label; 396 | }; 397 | 398 | NetView.prototype.removeLabel = function(toDelete) { 399 | var last = this.labels.pop(); 400 | if (last !== toDelete) { 401 | this.labels[toDelete.index] = last; 402 | last.index = toDelete.index; 403 | } 404 | }; 405 | 406 | 407 | NetView.prototype.addCell = function() { 408 | var cell = new CellView(this.cells.length); 409 | this.cells.push(cell); 410 | return cell; 411 | }; 412 | 413 | NetView.prototype.removeCell = function(toDelete) { 414 | // detach all fibers 415 | // 416 | // slicing is to make sure 417 | // the array isn't modified underneath us 418 | toDelete.inputs.slice().forEach(this.removeFiber.bind(this)); 419 | toDelete.outputs.slice().forEach(this.removeFiber.bind(this)); 420 | 421 | // algorithm Ryan thought up 422 | // 1. when you delete an item 423 | // there is one unused index. 424 | // (at the slot you deleted.) 425 | // 2. set the last cells index 426 | // to the available one. 427 | // 3. Shrink the array length by one 428 | var last = this.cells.pop(); 429 | if (last !== toDelete) { 430 | this.cells[toDelete.index] = last; 431 | last.index = toDelete.index; 432 | } 433 | // now toDelete is no good 434 | }; 435 | 436 | NetView.prototype.addFiber = function(from, to) { 437 | var fiber = new FiberView(this.fibers.length); 438 | fiber.from = from; 439 | fiber.to = to; 440 | this.fibers.push(fiber); 441 | return fiber; 442 | }; 443 | 444 | NetView.prototype.removeFiber = function(toDelete) { 445 | var i; 446 | // remove from "from" outputs 447 | i = toDelete.from.outputs.indexOf(toDelete); 448 | toDelete.from.outputs.splice(i, 1); 449 | 450 | // remove from "to" inputs 451 | i = toDelete.to.inputs.indexOf(toDelete); 452 | toDelete.to.inputs.splice(i, 1); 453 | toDelete.to.inputTypes.splice(i, 1); 454 | 455 | // see removeCell 456 | var last = this.fibers.pop(); 457 | if (last !== toDelete) { 458 | this.fibers[toDelete.index] = last; 459 | last.index = toDelete.index; 460 | } 461 | }; 462 | 463 | NetView.prototype.addBranch = function() { 464 | var branch = new BranchView(); 465 | this.branches.push(branch); 466 | return branch; 467 | }; 468 | 469 | // SERIALIZATION 470 | // ===================== 471 | 472 | var SERIALIZATION_VERSION = 2; 473 | var SERIALIZATION_SUCCESS = true; 474 | var SERIALIZATION_INVALID_BASE64 = -1; 475 | var SERIALIZATION_INVALID_LENGTH = -2; 476 | var SERIALIZATION_INVALID_VERSION = -3; 477 | 478 | NetView.prototype.save = function() { 479 | var d = []; 480 | var i, j; 481 | 482 | function write(val) { 483 | d.push(val); 484 | } 485 | 486 | // add a placeholder for the data length 487 | write(0); 488 | write(SERIALIZATION_VERSION); 489 | 490 | write(this.cells.length); 491 | write(this.fibers.length); 492 | write(this.labels.length); 493 | 494 | for (i = 0; i < this.cells.length; ++i) { 495 | var cell = this.cells[i]; 496 | 497 | write(cell.pos.x); 498 | write(cell.pos.y); 499 | write(cell.threshold); 500 | write(cell.angle); 501 | 502 | write(cell.inputs.length); 503 | for (j = 0; j < cell.inputs.length; ++j) { 504 | write(cell.inputs[j].index); 505 | write(cell.inputTypes[j] === INPUT_INHIBIT ? INPUT_INHIBIT : INPUT_EXCITE); 506 | } 507 | 508 | write(cell.outputs.length); 509 | for (j = 0; j < cell.outputs.length; ++j) { 510 | write(cell.outputs[j].index); 511 | } 512 | } 513 | 514 | for (i = 0; i < this.fibers.length; ++i) { 515 | var fiber = this.fibers[i]; 516 | 517 | write(fiber.from.index); 518 | write(fiber.to.index); 519 | } 520 | 521 | for (i = 0; i < this.labels.length; ++i) { 522 | var label = this.labels[i]; 523 | write(label.pos.x); 524 | write(label.pos.y); 525 | write(label.text.length); 526 | } 527 | 528 | this.labels.forEach(function(l) { 529 | for (i = 0; i < l.text.length; ++i) { 530 | write(l.text.charCodeAt(i)); 531 | } 532 | }); 533 | 534 | // prefix the data with the number of bytes so that load can detect malformed data 535 | d[0] = d.length * 2; 536 | 537 | var arr16 = new Uint16Array(d); 538 | var arr8 = new Uint8Array(arr16.buffer); 539 | var str = String.fromCharCode.apply(null, arr8); 540 | 541 | return btoa(str); 542 | }; 543 | 544 | NetView.prototype.load = function(base64) { 545 | var str; 546 | var i, j; 547 | var cell; 548 | var fiber; 549 | var label; 550 | 551 | try { 552 | str = atob(base64); 553 | } catch (e) { 554 | return SERIALIZATION_INVALID_BASE64; 555 | } 556 | 557 | var arr8 = new Uint8Array(str.length); 558 | 559 | for (i = 0; i < str.length; ++i) { 560 | arr8[i] = str.charCodeAt(i); 561 | } 562 | 563 | var d = new Uint16Array(arr8.buffer); 564 | var cursor = -1; 565 | 566 | function read() { 567 | return d[++cursor]; 568 | } 569 | 570 | function readBlock(length) { 571 | var x = d.slice(cursor + 1, cursor + 1 + length); 572 | cursor += length; 573 | return x; 574 | } 575 | 576 | if (read() !== d.length * 2) { 577 | return SERIALIZATION_INVALID_LENGTH; 578 | } 579 | 580 | var version = read(); 581 | if (version < 1 || version > SERIALIZATION_VERSION) { 582 | return SERIALIZATION_INVALID_VERSION; 583 | } 584 | 585 | this.cells = new Array(read()); 586 | this.fibers = new Array(read()); 587 | 588 | for (i = 0; i < this.cells.length; ++i) { 589 | this.cells[i] = new CellView(i); 590 | } 591 | 592 | for (i = 0; i < this.fibers.length; ++i) { 593 | this.fibers[i] = new FiberView(i); 594 | } 595 | 596 | if (version > 1) { 597 | this.labels = new Array(read()); 598 | for (i = 0; i < this.labels.length; ++i) { 599 | this.labels[i] = new LabelView(i); 600 | } 601 | } 602 | 603 | for (i = 0; i < this.cells.length; ++i) { 604 | cell = this.cells[i]; 605 | 606 | cell.pos.x = read(); 607 | cell.pos.y = read(); 608 | cell.threshold = read(); 609 | cell.angle = read(); 610 | 611 | cell.inputs = new Array(read()); 612 | for (j = 0; j < cell.inputs.length; ++j) { 613 | cell.inputs[j] = this.fibers[read()]; 614 | cell.inputTypes[j] = read(); 615 | } 616 | 617 | cell.outputs = new Array(read()); 618 | for (j = 0; j < cell.outputs.length; ++j) { 619 | cell.outputs[j] = this.fibers[read()]; 620 | } 621 | } 622 | 623 | for (i = 0; i < this.fibers.length; ++i) { 624 | fiber = this.fibers[i]; 625 | fiber.from = this.cells[read()]; 626 | fiber.to = this.cells[read()]; 627 | } 628 | 629 | if (version > 1) { 630 | var textLengths = []; 631 | for (i = 0; i < this.labels.length; ++i) { 632 | label = this.labels[i]; 633 | label.pos.x = read(); 634 | label.pos.y = read(); 635 | textLengths.push(read()); 636 | } 637 | 638 | for (i = 0; i < textLengths.length; ++i) { 639 | var characters = readBlock(textLengths[i]); 640 | this.labels[i].text = String.fromCharCode.apply(null, characters); 641 | } 642 | } 643 | 644 | return SERIALIZATION_SUCCESS; 645 | }; 646 | 647 | function messageForSerializationError(error) { 648 | switch (error) { 649 | case SERIALIZATION_SUCCESS: 650 | return null; 651 | 652 | case SERIALIZATION_INVALID_BASE64: 653 | return 'Could not load net. Failed to base64 decode.'; 654 | 655 | case SERIALIZATION_INVALID_LENGTH: 656 | return 'Could not load net. Incorrect checksum or length.'; 657 | 658 | case SERIALIZATION_INVALID_VERSION: 659 | return 'Could not load net. Unsupported serialization version.'; 660 | 661 | default: 662 | return 'Could not load net. Unexpected serialization error.'; 663 | } 664 | } 665 | 666 | // TOOLS 667 | // ===================== 668 | 669 | function SelectTool(sim, e) { 670 | this.dragInitial = sim.mousePos; 671 | this.sim = sim; 672 | } 673 | 674 | SelectTool.prototype.mouseUp = function(e) { 675 | var min = Vec.min(this.dragInitial, this.sim.mousePos); 676 | var max = Vec.max(this.dragInitial, this.sim.mousePos); 677 | 678 | var all = this.sim.net.cells.concat(this.sim.net.labels); 679 | 680 | this.sim.selection = all.filter(function (obj) { 681 | return obj.pos.inBounds(min, max); 682 | }); 683 | }; 684 | 685 | function MoveTool(sim, e) { 686 | this.sim = sim; 687 | this.initial = this.sim.mousePos; 688 | this.previous = this.sim.mousePos; 689 | } 690 | 691 | MoveTool.prototype.mouseMove = function(e) { 692 | var i; 693 | var object; 694 | var delta = Vec.sub(this.sim.mousePos, this.previous); 695 | 696 | for (i = 0; i < this.sim.selection.length; ++i) { 697 | object = this.sim.selection[i]; 698 | object.pos.add(delta); 699 | } 700 | 701 | this.previous = this.sim.mousePos; 702 | }; 703 | 704 | MoveTool.prototype.mouseUp = function(e) { 705 | 706 | }; 707 | 708 | MoveTool.prototype.cancel = function() { 709 | var i; 710 | var object; 711 | var invDelta = Vec.sub(this.initial, this.sim.mousePos); 712 | 713 | for (i = 0; i < this.sim.selection.length; ++i) { 714 | object = this.sim.selection[i]; 715 | object.pos.add(invDelta); 716 | } 717 | }; 718 | 719 | function CreateTool(sim, e) { 720 | var menu = document.getElementById('new-menu'); 721 | 722 | this.menu = menu; 723 | this.menu.style.left = e.pageX + 'px'; 724 | this.menu.style.top = e.pageY + 'px'; 725 | this.menu.classList.add('active'); 726 | 727 | this.menu.onclick = function(e) { 728 | var action; 729 | var added; 730 | var canvasLoc = sim.mousePos; 731 | if (e.target.matches('li')) { 732 | action = e.target.getAttribute('data-action'); 733 | 734 | if (action === 'new-cell') { 735 | added = sim.net.addCell(); 736 | added.pos = canvasLoc; 737 | } else if (action === 'new-branch') { 738 | added = sim.net.addBranch(); 739 | added.pos = canvasLoc; 740 | } else if (action == 'new-label') { 741 | added = sim.net.addTextLabel(); 742 | added.pos = canvasLoc; 743 | sim.editLabelText(added); 744 | } 745 | menu.classList.remove('active'); 746 | } 747 | }; 748 | } 749 | 750 | CreateTool.prototype.mouseUp = function(e) { 751 | this.cancel(); 752 | }; 753 | 754 | CreateTool.prototype.cancel = function() { 755 | this.menu.classList.remove('active'); 756 | }; 757 | 758 | function EditTextTool(sim, text, callback) { 759 | this.input = EditTextTool.createTextInputElement(sim); 760 | this.input.value = text; 761 | 762 | this.input.focus(); 763 | this.input.select(); 764 | 765 | this.input.addEventListener("keyup", (function(e) { 766 | if (e.key === 'Enter' || e.key === 'Return') { 767 | this.input.blur(); 768 | callback(e.target.value); 769 | } else if (e.key == 'Escape') { 770 | this.input.blur(); 771 | } 772 | }).bind(this)); 773 | 774 | this.input.addEventListener("focusout", this.cancel.bind(this)); 775 | } 776 | 777 | EditTextTool.prototype.cancel = function() { 778 | this.input.remove(); 779 | }; 780 | 781 | EditTextTool.createTextInputElement = function(sim){ 782 | var dom = document.createElement("INPUT"); 783 | dom.setAttribute("type", "text"); 784 | dom.style.position = "absolute"; 785 | var rect = sim.canvas.getBoundingClientRect(); 786 | dom.style.top = (sim.mousePos.y + rect.top).toString() + "px"; 787 | dom.style.left = (sim.mousePos.x + rect.left).toString() + "px"; 788 | document.body.appendChild(dom); 789 | return dom; 790 | }; 791 | 792 | function EditLabelTool(sim, e, label){ 793 | var menu = document.getElementById('label-menu'); 794 | 795 | this.menu = menu; 796 | this.menu.style.left = e.pageX + 'px'; 797 | this.menu.style.top = e.pageY + 'px'; 798 | this.menu.classList.add('active'); 799 | 800 | this.menu.onclick = function(e) { 801 | var action; 802 | if(e.target.matches('li')){ 803 | action = e.target.getAttribute('data-action'); 804 | 805 | if (action === 'edit'){ 806 | sim.editLabelText(label); 807 | } else if (action === 'delete') { 808 | sim.net.removeLabel(label); 809 | } 810 | } 811 | menu.classList.remove('active'); 812 | } 813 | 814 | this.input = null; 815 | } 816 | 817 | EditLabelTool.prototype.mouseUp = function(e) { 818 | this.cancel(); 819 | }; 820 | 821 | EditLabelTool.prototype.cancel = function() { 822 | this.menu.classList.remove('active'); 823 | 824 | }; 825 | 826 | function EditCellTool(sim, e, obj) { 827 | var menu = document.getElementById('cell-menu'); 828 | 829 | this.obj = obj; 830 | 831 | this.menu = menu; 832 | this.menu.style.left = e.pageX + 'px'; 833 | this.menu.style.top = e.pageY + 'px'; 834 | this.menu.classList.add('active'); 835 | 836 | this.menu.onclick = function(e) { 837 | var action; 838 | if (e.target.matches('li')) { 839 | action = e.target.getAttribute('data-action'); 840 | 841 | if (action === 'delete') { 842 | // delete selected cells 843 | sim.deleteSelection(); 844 | } else if (action === 'flip') { 845 | 846 | sim.selection.forEach(function(fiber) { 847 | if (fiber.angle === ANGLE_EAST) { 848 | fiber.angle = ANGLE_WEST; 849 | } else { 850 | fiber.angle = ANGLE_EAST; 851 | } 852 | }); 853 | } 854 | } 855 | 856 | menu.classList.remove('active'); 857 | }; 858 | } 859 | 860 | EditCellTool.prototype.mouseUp = function(e) { 861 | this.cancel(); 862 | }; 863 | 864 | EditCellTool.prototype.cancel = function() { 865 | this.menu.classList.remove('active'); 866 | }; 867 | 868 | function EditFiberTool(sim, e, obj) { 869 | var inputType; 870 | var menu = document.getElementById('fiber-menu'); 871 | 872 | this.obj = obj; 873 | 874 | this.menu = menu; 875 | this.menu.style.left = e.pageX + 'px'; 876 | this.menu.style.top = e.pageY + 'px'; 877 | this.menu.classList.add('active'); 878 | 879 | this.menu.onclick = function(e) { 880 | var action; 881 | if (e.target.matches('li')) { 882 | action = e.target.getAttribute('data-action'); 883 | 884 | if (action === 'delete') { 885 | sim.net.removeFiber(obj); 886 | } else if (action === 'toggle') { 887 | // find index in cell 888 | if (obj.to.inputTypes[obj.outputIndex] === INPUT_INHIBIT) { 889 | inputType = INPUT_EXCITE; 890 | } else { 891 | inputType = INPUT_INHIBIT; 892 | } 893 | 894 | obj.to.inputTypes[obj.outputIndex] = inputType; 895 | } 896 | } 897 | 898 | menu.classList.remove('active'); 899 | }; 900 | } 901 | 902 | EditFiberTool.prototype.mouseUp = function(e) { 903 | this.cancel(); 904 | }; 905 | 906 | EditFiberTool.prototype.cancel = function() { 907 | this.menu.classList.remove('active'); 908 | }; 909 | 910 | function FiberTool(sim, e, cell) { 911 | this.sim = sim; 912 | this.initial = this.sim.mousePos; 913 | 914 | var dir = Vec.sub(this.sim.mousePos, cell.pos); 915 | 916 | if (cell.isPositionOnOutputSide(this.sim.mousePos)) { 917 | this.from = cell; 918 | } else { 919 | this.to = cell; 920 | } 921 | } 922 | 923 | FiberTool.prototype.mouseUp = function(e) { 924 | var mousePos = this.sim.mousePos; 925 | 926 | var hit = this.sim.net.cells.find(function (cell) { 927 | return cell.hitsConnectors(mousePos); 928 | }); 929 | 930 | if (!hit) { 931 | // didn't click anything 932 | return; 933 | } 934 | 935 | if (this.from) { 936 | this.to = hit; 937 | } else { 938 | this.from = hit; 939 | } 940 | 941 | if (this.from === this.to) { 942 | // connecting to ourselves 943 | 944 | // we need some distance to make sure this isn't 945 | // an accidental click 946 | if (Vec.sub(this.sim.mousePos, this.initial).lenSqr() < 947 | CellView.radius * CellView.radius) { 948 | return; 949 | } 950 | } 951 | 952 | var f = this.sim.net.addFiber(this.from, this.to); 953 | this.from.outputs.push(f); 954 | this.to.inputs.push(f); 955 | this.to.inputTypes.push(INPUT_EXCITE); 956 | }; 957 | 958 | 959 | 960 | // WINDOW AND CONTEXT 961 | // ------------------------- 962 | 963 | function getMousePos(canvas, e) { 964 | var rect = canvas.getBoundingClientRect(); 965 | return new Vec(e.clientX - rect.left, e.clientY - rect.top); 966 | } 967 | 968 | var DefaultNet = "dgICAAgACAAGAEoB6QAAAAAAAQAFAAEAAwAAAAEABgASAmsAAQAAAAEAAAAAAAEAAgDqAbIAAQAAAAEAAQAAAAEAAwB6AtwAAwAAAAMAAgAAAAMAAAAHAAAAAQAEAAABLAEBAAEAAQAEAAAAAQAFAAcC8AABAAAAAQAGAAAAAQAHALwBLQIAAAAAAAAAAFgCFQIBAAAAAAAAAAAAAQAAAAIAAQADAAIAAwADAAQABAAAAAAABQAFAAMAAgIzACsA7gBlACQAxQDfAA4AfgGtAS0A8gHoASUA1ABOAR0AUwBlAGwAZQBjAHQAIABhAG4AZAAgAGQAcgBhAGcAIABuAGUAdQByAG8AbgBzACAAdwBpAHQAaAAgAHQAaABlACAAbABlAGYAdAAgAG0AbwB1AHMAZQBTAGUAdAAgAHQAaAByAGUAcwBoAG8AbABkACAAdwBpAHQAaAAgAG4AdQBtAGIAZQByACAAawBlAHkAcwAgACgAMAAtADkAKQAwACAAYQBsAHcAYQB5AHMAIABmAGkAcgBlAHMAUgBpAGcAaAB0ACAAYwBsAGkAYwBrACAAbwBuACAAZgBpAGIAZQByAHMAIABhAG4AZAAgAG4AZQB1AHIAbwBuAHMAIABmAG8AcgAgAG8AcAB0AGkAbwBuAHMAQwBsAGkAYwBrACAAYQBuAGQAIABkAHIAYQBnACAAZgBpAGIAZQByAHMAIABiAGUAdAB3AGUAZQBuACAAbgBlAHUAcgBvAG4AcwB1AG4AbABlAHMAcwAgAGkAdAAgAHIAZQBjAGUAaQB2AGUAcwAgAGEAbgAgAGkAbgBoAGkAYgBpAHQA"; 969 | 970 | function Sim() { 971 | this.selection = []; 972 | this.play = true; 973 | 974 | this.mousePos = new Vec(0, 0); 975 | this.fontsize = 14.0; 976 | 977 | this.playBtn = document.getElementById('play-btn'); 978 | this.playBtn.onclick = this.togglePlay.bind(this); 979 | 980 | this.stepBtn = document.getElementById('step-btn'); 981 | this.stepBtn.onclick = this.step.bind(this); 982 | 983 | this.restartBtn = document.getElementById('restart-btn'); 984 | this.restartBtn.onclick = this.restart.bind(this); 985 | 986 | this.timeDisplay = document.getElementById('time'); 987 | this.storageInput = document.getElementById('storage-input'); 988 | 989 | this.loadBtn = document.getElementById('load-btn'); 990 | this.loadBtn.onclick = this.load.bind(this); 991 | 992 | this.saveBtn = document.getElementById('save-btn'); 993 | this.saveBtn.onclick = this.save.bind(this); 994 | 995 | this.canvas = document.getElementById('main-canvas'); 996 | this.ctx = this.canvas.getContext('2d', { alpha: false }); 997 | 998 | this.canvas.onmousedown = this.mouseDown.bind(this); 999 | this.canvas.onmouseup = this.mouseUp.bind(this); 1000 | this.canvas.onmousemove = this.mouseMove.bind(this); 1001 | 1002 | this.canvas.onselectstart = function() { 1003 | return false; 1004 | }; 1005 | 1006 | window.addEventListener('keydown', (function(e) { 1007 | var num; 1008 | 1009 | if (document.activeElement !== document.body || e.metaKey) { 1010 | // do nothing in case the text field is focused or the user is holding a modifier 1011 | } else if (e.key === 's') { 1012 | // step hotkey 1013 | this.step(); 1014 | e.preventDefault(); 1015 | } else if (e.key === ' ') { 1016 | // play/pause hotkey 1017 | this.togglePlay(); 1018 | e.preventDefault(); 1019 | } else if (e.key.localeCompare('0') >= 0 && e.key.localeCompare('9') <= 0) { 1020 | // numbers for threshold 1021 | num = parseInt(e.key); 1022 | 1023 | this.selection.forEach(function(obj) { 1024 | if (obj instanceof CellView) { 1025 | obj.threshold = num; 1026 | } 1027 | }); 1028 | 1029 | e.preventDefault(); 1030 | } else if (event.key === 'Delete' || 1031 | event.key === 'Backspace') { 1032 | // delete selected cells 1033 | this.deleteSelection(); 1034 | e.preventDefault(); 1035 | } else if (event.key === 'Escape') { 1036 | if (this.tool && this.tool.cancel) { 1037 | this.tool.cancel(); 1038 | delete this.tool; 1039 | } 1040 | e.preventDefault(); 1041 | } 1042 | }).bind(this)); 1043 | 1044 | this.canvas.oncontextmenu = (function(e) { 1045 | e.preventDefault(); 1046 | 1047 | if (this.tool && this.tool.cancel) { 1048 | this.tool.cancel(); 1049 | delete this.tool; 1050 | } 1051 | 1052 | var mousePos = getMousePos(this.canvas, e); 1053 | 1054 | var hit = this.net.cells.find(function (cell) { 1055 | return cell.hits(mousePos); 1056 | }); 1057 | 1058 | if (hit) { 1059 | this.tool = new EditCellTool(this, e, hit); 1060 | } else { 1061 | var fontsize = this.fontsize; 1062 | hit = this.net.labels.find(function (label) { 1063 | return label.hits(mousePos, fontsize); 1064 | }); 1065 | 1066 | if (hit) { 1067 | this.tool = new EditLabelTool(this, e, hit); 1068 | } else { 1069 | hit = this.net.fibers.find(function (fiber) { 1070 | return fiber.hits(mousePos); 1071 | }); 1072 | if (hit) { 1073 | this.tool = new EditFiberTool(this, e, hit); 1074 | } else { 1075 | this.tool = new CreateTool(this, e, hit); 1076 | } 1077 | } 1078 | } 1079 | }).bind(this); 1080 | 1081 | // create a new net 1082 | this.net = new NetView(); 1083 | 1084 | // handle download 1085 | // if they provided one 1086 | var urlParams = new URLSearchParams(window.location.search); 1087 | var downloadUrl = urlParams.get('d'); 1088 | 1089 | if (downloadUrl && downloadUrl.length > 0) { 1090 | this.download(downloadUrl); 1091 | } else { 1092 | this.net.load(DefaultNet); 1093 | } 1094 | }; 1095 | 1096 | Sim.prototype.togglePlay = function() { 1097 | this.play = !this.play; 1098 | 1099 | if (this.play) { 1100 | this.playBtn.innerText = 'Pause'; 1101 | } else { 1102 | this.playBtn.innerText = 'Play'; 1103 | } 1104 | }; 1105 | 1106 | Sim.prototype.restart = function() { 1107 | this.selection = []; 1108 | this.net.restart(); 1109 | this.timeDisplay.innerText = String(this.net.time); 1110 | }; 1111 | 1112 | Sim.prototype.download = function(url) { 1113 | var req = new XMLHttpRequest(); 1114 | req.open('GET', url); 1115 | req.send(); 1116 | 1117 | req.onerror = function(err) { 1118 | alert('Could not download net. Unknown XHR network error.'); 1119 | }; 1120 | 1121 | req.onload = (function() { 1122 | var ret; 1123 | 1124 | if ( req.status >= 200 && req.status < 300) { 1125 | ret = this.net.load(req.responseText.trim()); 1126 | 1127 | if (ret !== SERIALIZATION_SUCCESS) { 1128 | alert(messageForSerializationError(ret)); 1129 | } 1130 | } else { 1131 | alert('Could not download net. Error status: ' + req.status); 1132 | } 1133 | }).bind(this); 1134 | }; 1135 | 1136 | Sim.prototype.load = function() { 1137 | var ret = this.net.load(this.storageInput.value); 1138 | 1139 | if (ret === SERIALIZATION_SUCCESS) { 1140 | this.restart(); 1141 | } else { 1142 | alert(messageForSerializationError(ret)); 1143 | } 1144 | }; 1145 | 1146 | Sim.prototype.save = function() { 1147 | this.storageInput.value = this.net.save(); 1148 | this.storageInput.select(); 1149 | }; 1150 | 1151 | Sim.prototype.deleteSelection = function() { 1152 | var net = this.net; 1153 | this.selection.forEach(function(obj) { 1154 | if (obj instanceof CellView) { 1155 | net.removeCell(obj); 1156 | } else if (obj instanceof LabelView) { 1157 | net.removeLabel(obj); 1158 | } 1159 | }); 1160 | 1161 | // clear selection 1162 | this.selection = []; 1163 | }; 1164 | 1165 | Sim.prototype.step = function() { 1166 | this.net.step(); 1167 | this.timeDisplay.innerText = String(this.net.time); 1168 | }; 1169 | 1170 | Sim.prototype.mouseDown = function(e) { 1171 | var mousePos = getMousePos(this.canvas, e); 1172 | this.mousePos = mousePos; 1173 | 1174 | if (this.tool && this.tool.cancel) { 1175 | this.tool.cancel(); 1176 | delete this.tool; 1177 | } 1178 | 1179 | var hit = this.net.cells.find(function (cell) { 1180 | return cell.hits(mousePos); 1181 | }); 1182 | 1183 | var connectHit = this.net.cells.find(function (cell) { 1184 | return cell.hitsConnectors(mousePos); 1185 | }); 1186 | 1187 | var fontSize = this.fontsize; 1188 | var labelHit = this.net.labels.find(function (label) { 1189 | return label.hits(mousePos,fontSize); 1190 | }); 1191 | 1192 | if (hit) { 1193 | var index = this.selection.indexOf(hit); 1194 | 1195 | if (index === -1) { 1196 | this.selection = [hit]; 1197 | } 1198 | 1199 | this.tool = new MoveTool(this, e); 1200 | } else if (labelHit) { 1201 | var index = this.selection.indexOf(labelHit); 1202 | 1203 | if (index === -1) { 1204 | this.selection = [labelHit]; 1205 | } 1206 | 1207 | this.tool = new MoveTool(this, e); 1208 | 1209 | } else if (connectHit) { 1210 | this.tool = new FiberTool(this, e, connectHit); 1211 | } else { 1212 | this.tool = new SelectTool(this, e); 1213 | } 1214 | }; 1215 | 1216 | Sim.prototype.editLabelText = function(label) { 1217 | if (this.tool && this.tool.cancel) { 1218 | this.tool.cancel(); 1219 | delete this.tool; 1220 | } 1221 | var net = this.net; 1222 | this.tool = new EditTextTool(this, label.text, function(val) { 1223 | if (!val) { 1224 | net.removeLabel(label); 1225 | } else { 1226 | label.text = val; 1227 | } 1228 | }); 1229 | }; 1230 | 1231 | Sim.prototype.mouseMove = function(e) { 1232 | this.mousePos = getMousePos(this.canvas, e); 1233 | 1234 | if (this.tool && 1235 | this.tool.mouseMove) { 1236 | this.tool.mouseMove(e); 1237 | } 1238 | }; 1239 | 1240 | Sim.prototype.mouseUp = function(e) { 1241 | this.mousePos = getMousePos(this.canvas, e); 1242 | 1243 | if (this.tool && 1244 | this.tool.mouseUp) { 1245 | this.tool.mouseUp(e); 1246 | } 1247 | 1248 | delete this.tool; 1249 | }; 1250 | 1251 | var gSim = new Sim(); 1252 | 1253 | setInterval(function() { 1254 | drawSim(gSim.ctx, gSim.canvas, gSim); 1255 | }, 16); 1256 | 1257 | setInterval(function() { 1258 | if (gSim.play) { 1259 | gSim.step(); 1260 | } 1261 | }, 500); 1262 | 1263 | // RENDERER 1264 | // -------------------------- 1265 | // 1266 | function drawSim(ctx, canvas, sim) { 1267 | clearCanvas(ctx, canvas); 1268 | 1269 | drawFibers(ctx, sim.net); 1270 | drawCells(ctx, sim.net, sim.fontsize); 1271 | 1272 | var drawingFiberToInput = false; 1273 | var drawingFiberToOutput = false; 1274 | 1275 | if (sim.tool) { 1276 | if (sim.tool instanceof FiberTool) { 1277 | drawPartialFiber(ctx, sim.tool, sim.mousePos); 1278 | drawingFiberToInput = !!sim.tool.from; 1279 | drawingFiberToOutput = !!sim.tool.to; 1280 | } else if (gSim.tool instanceof SelectTool) { 1281 | drawSelectBox(ctx, sim.tool, sim.mousePos); 1282 | } 1283 | } 1284 | 1285 | drawHoverRing(ctx, sim.net, sim.mousePos, drawingFiberToInput, drawingFiberToOutput); 1286 | drawTextLabels(ctx, sim.net, sim.selection, sim.fontsize); 1287 | } 1288 | 1289 | function clearCanvas(ctx, canvas) { 1290 | ctx.fillStyle = '#EFF0F1'; 1291 | ctx.beginPath(); 1292 | ctx.rect(0, 0, canvas.width, canvas.height); 1293 | ctx.closePath(); 1294 | return ctx.fill(); 1295 | } 1296 | 1297 | function drawHoverRing(ctx, net, mousePos, drawingFiberToInput, drawingFiberToOutput) { 1298 | var INACTIVE_COLOR = '#B0B0B0'; 1299 | var HIGHLIGHT_COLOR = '#0000FF'; 1300 | 1301 | var i; 1302 | var cell; 1303 | var highlightInput; 1304 | var highlightOutput; 1305 | var radius; 1306 | var angleOffset; 1307 | 1308 | for (i = 0; i < net.cells.length; ++i) { 1309 | cell = net.cells[i]; 1310 | 1311 | if (cell.hitsConnectors(mousePos)) { 1312 | // If the user is using the fiber tool to create a new fiber, we 1313 | // want to highlight the portion of the hover ring that the user can 1314 | // connect the fiber to. Otherwise, we want highlight the portion 1315 | // of the hover ring that the user is hovering over. 1316 | if (drawingFiberToInput || drawingFiberToOutput) { 1317 | highlightOutput = drawingFiberToOutput; 1318 | highlightInput = drawingFiberToInput; 1319 | } else if (cell.hits(mousePos)) { 1320 | highlightOutput = false; 1321 | highlightInput = false; 1322 | } else { 1323 | highlightOutput = cell.isPositionOnOutputSide(mousePos); 1324 | highlightInput = !highlightOutput; 1325 | } 1326 | 1327 | radius = cell.radius + cell.connectorPadding; 1328 | ctx.lineWidth = 1; 1329 | ctx.setLineDash([4]); 1330 | 1331 | angleOffset = cell.angle !== ANGLE_EAST ? 0 : Math.PI * 1; 1332 | 1333 | ctx.beginPath(); 1334 | ctx.strokeStyle = highlightOutput ? HIGHLIGHT_COLOR : INACTIVE_COLOR; 1335 | ctx.arc(cell.pos.x, cell.pos.y, radius, Math.PI * 0.5 + angleOffset, Math.PI * 1.5 + angleOffset, false); 1336 | ctx.stroke(); 1337 | 1338 | ctx.beginPath(); 1339 | ctx.strokeStyle = highlightInput ? HIGHLIGHT_COLOR : INACTIVE_COLOR; 1340 | ctx.arc(cell.pos.x, cell.pos.y, radius, Math.PI * -0.5 + angleOffset, Math.PI * -1.5 + angleOffset, false); 1341 | ctx.stroke(); 1342 | 1343 | ctx.lineWidth = 1; 1344 | ctx.setLineDash([]); 1345 | } 1346 | } 1347 | } 1348 | 1349 | function drawSelectBox(ctx, selectTool, mousePos) { 1350 | ctx.strokeStyle = '#009900'; 1351 | ctx.lineWidth = 1; 1352 | ctx.setLineDash([4]); 1353 | 1354 | var p = []; 1355 | p[0] = selectTool.dragInitial; 1356 | p[1] = mousePos; 1357 | 1358 | ctx.beginPath(); 1359 | ctx.rect(p[0].x, p[0].y, p[1].x - p[0].x, p[1].y - p[0].y); 1360 | ctx.stroke(); 1361 | 1362 | ctx.setLineDash([]); 1363 | } 1364 | 1365 | function drawTextLabels(ctx, net, selection, fontsize){ 1366 | ctx.font = fontsize.toString() + 'pt monospace'; 1367 | ctx.textAlign = 'center'; 1368 | ctx.textBaseline = 'middle'; 1369 | 1370 | var marked = {}; 1371 | 1372 | for (i = 0; i < selection.length; ++i) { 1373 | if (selection[i] instanceof LabelView) { 1374 | marked[selection[i].index] = true; 1375 | } 1376 | } 1377 | 1378 | 1379 | 1380 | /* 1381 | var bounds = sel[i].bounds(fontsize); 1382 | 1383 | ctx.beginPath(); 1384 | ctx.moveTo(bounds.min.x, bounds.max.y); 1385 | ctx.lineTo(bounds.max.x, bounds.max.y); 1386 | ctx.stroke(); 1387 | */ 1388 | 1389 | var label; 1390 | var i; 1391 | for (i = 0; i < net.labels.length; i++){ 1392 | label = net.labels[i]; 1393 | 1394 | if (marked[i]) { 1395 | ctx.fillStyle = '#009900'; 1396 | } else { 1397 | ctx.fillStyle = '#000000'; 1398 | } 1399 | ctx.fillText(net.labels[i].text, label.pos.x, label.pos.y); 1400 | } 1401 | } 1402 | 1403 | function drawCells(ctx, net, labelFontSize) { 1404 | // draw the front of the cells 1405 | // quite 1406 | ctx.fillStyle = '#444444'; 1407 | ctx.strokeStyle = '#000000'; 1408 | drawCellFronts(ctx, net, false); 1409 | 1410 | // firing 1411 | ctx.fillStyle = '#FF0000'; 1412 | drawCellFronts(ctx, net, true); 1413 | 1414 | // draw the back of the cells 1415 | // firing and quiet 1416 | ctx.fillStyle = '#FFFFFF'; 1417 | drawCellBacks(ctx, net); 1418 | 1419 | ctx.strokeStyle = '#FFFFFF'; 1420 | drawCellArrows(ctx, net); 1421 | 1422 | // draw the selected cells 1423 | ctx.strokeStyle = '#009900'; 1424 | drawCellSelection(ctx, net, gSim.selection, labelFontSize); 1425 | 1426 | // draw the text 1427 | drawCellLabels(ctx, net); 1428 | } 1429 | 1430 | function drawCellLabels(ctx, net) { 1431 | var i; 1432 | var cell; 1433 | var dir; 1434 | var labelPos; 1435 | 1436 | ctx.fillStyle = '#000000'; 1437 | ctx.font = '14pt monospace'; 1438 | ctx.textAlign = 'center'; 1439 | ctx.textBaseline = 'middle'; 1440 | 1441 | for (i = 0; i < net.cells.length; ++i) { 1442 | cell = net.cells[i]; 1443 | dir = Vec.fromAngle(cell.angle); 1444 | 1445 | labelPos = Vec.sub(cell.pos, Vec.scale(dir, cell.radius * 0.5)); 1446 | ctx.fillText(String(cell.threshold), labelPos.x, labelPos.y); 1447 | } 1448 | } 1449 | 1450 | function drawCellFronts(ctx, net, active) { 1451 | var i; 1452 | var cell; 1453 | var cellFiring; 1454 | var clockwise; 1455 | 1456 | ctx.beginPath(); 1457 | 1458 | for (i = 0; i < net.cells.length; ++i) { 1459 | cell = net.cells[i]; 1460 | clockwise = (cell.angle === ANGLE_EAST); 1461 | cellFiring = net.state[i] ? true : false; 1462 | 1463 | if (cellFiring !== active) { 1464 | continue; 1465 | } 1466 | 1467 | ctx.moveTo(cell.pos.x, cell.pos.y + cell.radius); 1468 | ctx.arc(cell.pos.x, cell.pos.y, cell.radius, Math.PI * 0.5, Math.PI * 1.5, clockwise); 1469 | } 1470 | 1471 | ctx.fill(); 1472 | ctx.stroke(); 1473 | } 1474 | 1475 | // draws the path for half the cell 1476 | // ctx options are used to configure front or back style 1477 | function drawCellBacks(ctx, net) { 1478 | ctx.beginPath(); 1479 | net.cells.forEach(cell => { 1480 | var clockwise = cell.angle !== ANGLE_EAST; 1481 | ctx.moveTo(cell.pos.x, cell.pos.y + cell.radius); 1482 | ctx.arc(cell.pos.x, cell.pos.y, cell.radius, Math.PI * 0.5, Math.PI * 1.5, clockwise); 1483 | }); 1484 | ctx.fill(); 1485 | ctx.stroke(); 1486 | } 1487 | 1488 | function drawCellArrows(ctx, net) { 1489 | ctx.beginPath(); 1490 | net.cells.forEach(cell => { 1491 | var dir = Vec.fromAngle(cell.angle); 1492 | ctx.moveTo(cell.pos.x + dir.x * 4.0, cell.pos.y + cell.radius * 0.4); 1493 | ctx.lineTo(cell.pos.x + dir.x * cell.radius * 0.6, cell.pos.y); 1494 | ctx.lineTo(cell.pos.x + dir.x * 4.0, cell.pos.y - cell.radius * 0.4); 1495 | }); 1496 | ctx.stroke(); 1497 | } 1498 | 1499 | 1500 | function drawCellSelection(ctx, net, selection, fontsize) { 1501 | ctx.lineWidth = 2; 1502 | ctx.beginPath(); 1503 | selection.forEach(cell => { 1504 | if (cell instanceof CellView) { 1505 | ctx.moveTo(cell.pos.x + cell.radius, cell.pos.y); 1506 | ctx.arc(cell.pos.x, cell.pos.y, cell.radius, Math.PI * 2.0, 0.0, false); 1507 | } 1508 | }); 1509 | ctx.stroke(); 1510 | ctx.lineWidth = 1; 1511 | } 1512 | 1513 | /* 1514 | function drawBranches(ctx, net) { 1515 | var i; 1516 | var b; 1517 | ctx.strokeStyle = '#000000'; 1518 | ctx.fillStyle = '#000000'; 1519 | 1520 | for (i = 0; i < net.branches.length; ++i) { 1521 | b = net.branches[i]; 1522 | 1523 | ctx.beginPath(); 1524 | ctx.arc(b.pos.x, b.pos.y, 5.0, 0.0, Math.PI * 2.0, false); 1525 | ctx.fill(); 1526 | ctx.stroke(); 1527 | } 1528 | } 1529 | */ 1530 | 1531 | function stackedOffset(spread, j, n) { 1532 | return spread * (j - (n - 1) * 0.5); 1533 | } 1534 | 1535 | // returns a list of tuples 1536 | // [startPoint, endPoint, type] 1537 | function updateFiberPoints(net) { 1538 | var i, j, N; 1539 | var cell; 1540 | var f; 1541 | var fudge; 1542 | var yOffset; 1543 | var p; 1544 | var fromDir, toDir; 1545 | 1546 | for (i = 0; i < net.cells.length; ++i) { 1547 | cell = net.cells[i]; 1548 | N = cell.inputs.length; 1549 | 1550 | for (j = 0; j < N; ++j) { 1551 | f = cell.inputs[j]; 1552 | // cache this for rendering 1553 | f.outputIndex = j; 1554 | 1555 | // rotation vectors 1556 | fromDir = Vec.fromAngle(f.from.angle); 1557 | toDir = Vec.fromAngle(f.to.angle); 1558 | 1559 | yOffset = stackedOffset(7.0, j, N); 1560 | p = f.bezierPoints; 1561 | 1562 | p[0] = Vec.add(f.from.pos, Vec.scale(fromDir, f.from.radius)); 1563 | p[3] = Vec.add(Vec.add(f.to.pos, Vec.scale(toDir, -f.to.radius)), new Vec(0.0, yOffset)); 1564 | 1565 | // control points 1566 | if (f.from === f.to) { 1567 | // cell connected to itself 1568 | fudge = f.from.radius * 1.6; 1569 | p[1] = Vec.add(Vec.add(f.from.pos, Vec.scale(fromDir, fudge * 1.5)), new Vec(0.0, -fudge * 2.0)); 1570 | p[2] = Vec.add(Vec.add(f.to.pos, Vec.scale(toDir, -fudge * 3.0)), new Vec(0.0, -fudge)); 1571 | } else { 1572 | // normal fiber 1573 | fudge = Vec.dist(p[0], p[3]) * 0.5; 1574 | 1575 | p[1] = Vec.add(f.from.pos, Vec.scale(fromDir, fudge)); 1576 | p[2] = Vec.add(f.to.pos, Vec.scale(toDir, -fudge)); 1577 | } 1578 | } 1579 | } 1580 | } 1581 | 1582 | function drawFibers(ctx, net) { 1583 | updateFiberPoints(net); 1584 | 1585 | // draw quiet fibers 1586 | ctx.lineWidth = 1; 1587 | ctx.strokeStyle = '#000000'; 1588 | drawFiberPaths(ctx, net, false); 1589 | 1590 | // draw active fibers 1591 | ctx.lineWidth = 2; 1592 | ctx.strokeStyle = '#FF0000'; 1593 | drawFiberPaths(ctx, net, true); 1594 | 1595 | // draw quiet connectors 1596 | ctx.lineWidth = 1; 1597 | ctx.fillStyle = '#FFFFFF'; 1598 | ctx.strokeStyle = '#000000'; 1599 | drawConnectorPaths(ctx, net, false); 1600 | 1601 | // draw active connectors 1602 | ctx.lineWidth = 1; 1603 | ctx.strokeStyle = '#000000'; 1604 | ctx.fillStyle = '#FF0000'; 1605 | drawConnectorPaths(ctx, net, true); 1606 | } 1607 | 1608 | // draw "in progress" fibers for 1609 | // the fiber connecting tool 1610 | function drawPartialFiber(ctx, tool, mousePos) { 1611 | var p = []; 1612 | var dir; 1613 | 1614 | if (tool.from) { 1615 | dir = Vec.fromAngle(tool.from.angle); 1616 | p[0] = Vec.add(tool.from.pos, Vec.scale(dir, tool.from.radius)); 1617 | p[3] = mousePos; 1618 | } else { 1619 | dir = Vec.fromAngle(tool.to.angle); 1620 | p[0] = mousePos; 1621 | p[3] = Vec.add(tool.to.pos, Vec.scale(dir, -tool.to.radius)); 1622 | } 1623 | 1624 | var fudge = Vec.dist(p[0], p[3]) * 0.4; 1625 | p[1] = Vec.add(p[0], Vec.scale(dir, fudge)); 1626 | p[2] = Vec.add(p[3], Vec.scale(dir, -fudge)); 1627 | 1628 | ctx.strokeStyle = '#000000'; 1629 | ctx.lineWidth = 2; 1630 | ctx.setLineDash([2]); 1631 | 1632 | ctx.beginPath(); 1633 | ctx.moveTo(p[0].x, p[0].y); 1634 | ctx.bezierCurveTo(p[1].x, p[1].y, 1635 | p[2].x, p[2].y, 1636 | p[3].x, p[3].y); 1637 | ctx.stroke(); 1638 | ctx.lineWidth = 1; 1639 | ctx.setLineDash([]); 1640 | } 1641 | 1642 | function drawFiberPaths(ctx, net, active) { 1643 | var i; 1644 | var fiber; 1645 | var fiberActive; 1646 | var p; 1647 | 1648 | ctx.beginPath(); 1649 | for (i = 0; i < net.fibers.length; ++i) { 1650 | fiber = net.fibers[i]; 1651 | fiberActive = net.signals[fiber.index] ? true : false; 1652 | 1653 | if (fiberActive !== active) { 1654 | continue; 1655 | } 1656 | 1657 | p = fiber.bezierPoints; 1658 | ctx.moveTo(p[0].x, p[0].y); 1659 | ctx.bezierCurveTo(p[1].x, p[1].y, 1660 | p[2].x, p[2].y, 1661 | p[3].x, p[3].y); 1662 | } 1663 | ctx.stroke(); 1664 | } 1665 | 1666 | function drawConnectorPaths(ctx, net, active) { 1667 | var i; 1668 | var fiber; 1669 | var fiberActive; 1670 | var p; 1671 | var dir; 1672 | var outputType; 1673 | var radius = 4.0; 1674 | var origin; 1675 | 1676 | ctx.beginPath(); 1677 | 1678 | for (i = 0; i < net.fibers.length; ++i) { 1679 | fiber = net.fibers[i]; 1680 | fiberActive = net.signals[fiber.index] ? true : false; 1681 | p = fiber.bezierPoints; 1682 | 1683 | outputType = fiber.to.inputTypes[fiber.outputIndex]; 1684 | 1685 | if (outputType === INPUT_INHIBIT && fiberActive === active) { 1686 | dir = Vec.fromAngle(fiber.to.angle); 1687 | origin = Vec.add(p[3], Vec.scale(dir, -radius)); 1688 | 1689 | ctx.moveTo(origin.x + radius, origin.y); 1690 | ctx.arc(origin.x, origin.y, radius, 0.0, Math.PI * 2.0, false); 1691 | } 1692 | } 1693 | 1694 | ctx.fill(); 1695 | ctx.stroke(); 1696 | } 1697 | -------------------------------------------------------------------------------- /papers/In a Frog’s Eye.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/In a Frog’s Eye.pdf -------------------------------------------------------------------------------- /papers/kleene.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/kleene.pdf -------------------------------------------------------------------------------- /papers/l2_JB.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/l2_JB.pdf -------------------------------------------------------------------------------- /papers/letvin_ieee_1959.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/letvin_ieee_1959.pdf -------------------------------------------------------------------------------- /papers/mcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/mcp.pdf -------------------------------------------------------------------------------- /papers/minsky_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/minsky_1.gif -------------------------------------------------------------------------------- /papers/minsky_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/minsky_2.gif -------------------------------------------------------------------------------- /papers/pitts1947.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/pitts1947.pdf -------------------------------------------------------------------------------- /papers/smalheiser_pitts_2000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/neural-nets-sim/e355d6b28b2eeba4135ce6224cd2b604907e211d/papers/smalheiser_pitts_2000.pdf --------------------------------------------------------------------------------