├── .gitignore ├── README ├── examples ├── iccounter.txt └── twodigitcounter.txt ├── images ├── and.png ├── arrdown.png ├── arrup.png ├── btnsmallleft.png ├── btnsmallleftover.png ├── btnsmallmid.png ├── btnsmallmidover.png ├── btnsmallright.png ├── btnsmallrightover.png ├── buffer.png ├── clock.png ├── constoff.png ├── conston.png ├── decoder.png ├── delete.png ├── delic.png ├── dflipflop.png ├── encoder.png ├── input.png ├── move.png ├── nand.png ├── newfile.png ├── newic.png ├── nor.png ├── not.png ├── open.png ├── or.png ├── outoff.png ├── outon.png ├── output.png ├── pushswitchaclosed.png ├── pushswitchaopen.png ├── pushswitchbclosed.png ├── pushswitchbopen.png ├── save.png ├── select.png ├── sepend.png ├── sepmid.png ├── sevsega.png ├── sevsegb.png ├── sevsegbase.png ├── sevsegc.png ├── sevsegd.png ├── sevsegdecoder.png ├── sevsegdp.png ├── sevsege.png ├── sevsegf.png ├── sevsegg.png ├── switchclosed.png ├── switchopen.png ├── xnor.png └── xor.png ├── index.html └── scripts ├── button.js ├── environment.js ├── gate.js ├── lz-string-1.3.3.js ├── main.js ├── saving.js ├── toolbar.js ├── tools.js ├── wire.js └── wiregroup.js /.gitignore: -------------------------------------------------------------------------------- 1 | */Thumbs.db 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/README -------------------------------------------------------------------------------- /examples/iccounter.txt: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /examples/twodigitcounter.txt: -------------------------------------------------------------------------------- 1 | N4Ig5ghgLgpgziAXAbVFJIAiAxANgSwAc8B7QkAGhAA8kBmANgA4qBPegJipKWQDMIuODApQATgFcYAXSoATXgKEilw6QF8KaDDgLFcZSjSQAWBibaduiwcNGSZ8m8oqqZm7Yix4ipclVpEAFYGAAZLRDouEB4UN3spWRAFONsVNI0tEHQvXV8Df2NEBhCIqOtUl3FEp0q7N0zPbz0/I0CAdiDo9kjo2P40hMdk53qMj2ydH31DAKQmExYQHvKY0ZFq4ZSBlwaJnOb82aKAThMLZas1uo2HJO34vayDvJnCwIBGUJNwy96KnZ2Tb3daucbPKYtAptJAfD6MMp9UHA2qA9LKRqTXLTVpzRAfDirFZIlCbMEY1Fkp5NV64oqMJY9AlLfrxFEjG5DTEvHHQvFmC5Mjgs5F3VFsu77SFHd5IEK/IUiznsh6DYFS7FQ46BEoMCLMgESmoctFcjWHN4wxCdbqw4WGtVik1G9wQzUyq0LRl2pWmlWixLm2l807nfX265+p2qqqSt0Wumfb4Kn0O2PGmNAuM03na2HwvV/A2Rl0g5XZrEJkOfQm2/ER/pUjKUhxm+MAUQAdgBjEhyGBiK0EolIDg/NNjXaDBq1TsSXC4c1d3v9wd44d1n6/VnT3dTikcucL80AZRgADcYJ2z2BMDAVwOhxwToWeiYPoXG62yU307cpFys7zou8Znpe14wLe959o+67Pq+o6lJGP7fq2LpDIBh7AaeF5XjemD4HAhC4BA7DrowKaIAKAJlkeIFNGBeGQQRREkWRRQfCY7SbuYNFAce8YAAoSHAAAWJ4AO74FA3aiQAgkOFEREE7SfustHYfGADCJCdnAUAAJKdoQEjoHBL7KapAL+ogmzqEkUliPAvCgHAOpjlQcBvksMA6khMDeRMblIAwqxeaYPk6qsAWjkwTBBVF0ThYgwqRXa0U9KlCXzB+nneVQvnzIsBWBVkwWIEwHkgMlxUgIVFVVTFvShNlDUXMlUS/PVG4lZwLVlZ8XztfldWDQidWla5g0EnlEUFdN0RNQyrVwjN1UrMw83pYtG3xQNdqhL8NVpfWGURStHChEsx1bfWV29ZECwXfd630Ast09RNb0mK1XTXUyYajXKYQPZxP37cEXSzfigP1UEUNfVRUS/QjNXRTWZ1IxwrUMFVyVg7duqg+cOO1fjsM6rVTXnHtU0hWTb5UxjO2mIsK2Hf9sIU7CybE+DdP4qEYUAxc3VCyzwQEuzwtymt3W1g9QRSxDBKHdDBNA6dEtgxdeMix9HCNT0QQkyrhvDXK3Na4rpsCwSFmvSlos1g71O09VgQMtDjUu4WTVjq1XuOx8Vma0H/uHYHm2O5dXXMw9scXTL+Kh/LmOJyrBbq6nY1+5lkcq7zwc5zz46IxnAtnBbKd+4EVcJwXAtetntdFT5+f9QLNot7d3fl43HshUh+Ml8U/kd79IPF63wRTxHneD1RvHT7d1H9wv5V0N80Po/Q0dLdjENb9Xu+RNviMhwwgfnyPM/h0yqlR5zTu3ff51mw7+OXQbrsi0/6vf01vbPOB1/7B0AfVN+MN+aLyYLlYOVtm4X1trAsu5NnZFS6t5DegQFhHWwQbTGiwcGmGTkzUwy83atXOPgxCM9Pr+xCNQtBI16o0ITkwiGf1obkNnlg9+AsgjjWShwbihCJaiMPoI4RrDAhCJASlbirVuK0KoidPuVCIbcWfsfcRD1j7KIjDdTW2j9HfFaicQBHVxppwlnQRgFi1rGPqvXRGbMIaWNUbwzx+iHGFxes4pMZdNF21CPAjqCNbH6K6OzcJsiebwKWjEiGJhk4azYZQv+KTh5vkSfHNxH5qE5K5hgqi49TCFK0VY/WJjIlZK7oDGqlD8nU3MMoxpNT6rtFhm+NpHinGdLrj0kpFiZGmFdi0t8L5RmFnQbdE4NiplX0zjfQZ+ZFkjMzkY42EYomIz+itYscygHFiagclWSlHZr26iYI2FCYHlU4nre5H1bmi2wStN5PCJlcyIdMlWqSkqMxOpxP5yy7aXJqrw0FEsaYrUhW+U+YN+FY0+Sw0hi1PjsP2crO2XzHZK0xSUlFhK0XP24UAriEtzl4oZnvEFNNFbCnhVPDq+8sVU12p84pksiUwzETiqRi9OI8roJErF5TeUstUWKvlIqFGyulTvGxWLMn0D8Xi5e1j6HXKWZ8rVSzXkCupv8vFAqOoQKxcaj5FzWXEjjlza1fUlWO10ZSpgKKDEAo9Tvc+NyfVuL6XigNTTSkfHkQ9AUK0I1XOabCGNbsSH4hjTVH5ybhlUVNcKk21cTD+U+DmyNWbHmLGfnm+hha3GcODc/VKry4qRurcKhFsUGUNvLnFF1IjjVYt/qOJRALP6ZR7VzPtVF3F4qHazEF8NYUTuzajYFH1Z2K1xdmsZvLl11NlkKktzzZ4MrucESxgdF3A0Pe889p6gXzAdZEbdB6wHJQDZA/eTJzFH2jvjP1kzS7UINbe1earBYPPyc+u9DD32gfWbMnoL7c6gw/aE9FFUINFyakNCxHTAOa1cRhpDqDn7wbboh6D1oz2od7g+zDEMiaO2I2PBRNHBF2pw3DOeUHr3QwY1Aj19l1BAAAA== -------------------------------------------------------------------------------- /images/and.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/and.png -------------------------------------------------------------------------------- /images/arrdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/arrdown.png -------------------------------------------------------------------------------- /images/arrup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/arrup.png -------------------------------------------------------------------------------- /images/btnsmallleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallleft.png -------------------------------------------------------------------------------- /images/btnsmallleftover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallleftover.png -------------------------------------------------------------------------------- /images/btnsmallmid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallmid.png -------------------------------------------------------------------------------- /images/btnsmallmidover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallmidover.png -------------------------------------------------------------------------------- /images/btnsmallright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallright.png -------------------------------------------------------------------------------- /images/btnsmallrightover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/btnsmallrightover.png -------------------------------------------------------------------------------- /images/buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/buffer.png -------------------------------------------------------------------------------- /images/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/clock.png -------------------------------------------------------------------------------- /images/constoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/constoff.png -------------------------------------------------------------------------------- /images/conston.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/conston.png -------------------------------------------------------------------------------- /images/decoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/decoder.png -------------------------------------------------------------------------------- /images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/delete.png -------------------------------------------------------------------------------- /images/delic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/delic.png -------------------------------------------------------------------------------- /images/dflipflop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/dflipflop.png -------------------------------------------------------------------------------- /images/encoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/encoder.png -------------------------------------------------------------------------------- /images/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/input.png -------------------------------------------------------------------------------- /images/move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/move.png -------------------------------------------------------------------------------- /images/nand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/nand.png -------------------------------------------------------------------------------- /images/newfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/newfile.png -------------------------------------------------------------------------------- /images/newic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/newic.png -------------------------------------------------------------------------------- /images/nor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/nor.png -------------------------------------------------------------------------------- /images/not.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/not.png -------------------------------------------------------------------------------- /images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/open.png -------------------------------------------------------------------------------- /images/or.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/or.png -------------------------------------------------------------------------------- /images/outoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/outoff.png -------------------------------------------------------------------------------- /images/outon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/outon.png -------------------------------------------------------------------------------- /images/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/output.png -------------------------------------------------------------------------------- /images/pushswitchaclosed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/pushswitchaclosed.png -------------------------------------------------------------------------------- /images/pushswitchaopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/pushswitchaopen.png -------------------------------------------------------------------------------- /images/pushswitchbclosed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/pushswitchbclosed.png -------------------------------------------------------------------------------- /images/pushswitchbopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/pushswitchbopen.png -------------------------------------------------------------------------------- /images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/save.png -------------------------------------------------------------------------------- /images/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/select.png -------------------------------------------------------------------------------- /images/sepend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sepend.png -------------------------------------------------------------------------------- /images/sepmid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sepmid.png -------------------------------------------------------------------------------- /images/sevsega.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsega.png -------------------------------------------------------------------------------- /images/sevsegb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegb.png -------------------------------------------------------------------------------- /images/sevsegbase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegbase.png -------------------------------------------------------------------------------- /images/sevsegc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegc.png -------------------------------------------------------------------------------- /images/sevsegd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegd.png -------------------------------------------------------------------------------- /images/sevsegdecoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegdecoder.png -------------------------------------------------------------------------------- /images/sevsegdp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegdp.png -------------------------------------------------------------------------------- /images/sevsege.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsege.png -------------------------------------------------------------------------------- /images/sevsegf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegf.png -------------------------------------------------------------------------------- /images/sevsegg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/sevsegg.png -------------------------------------------------------------------------------- /images/switchclosed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/switchclosed.png -------------------------------------------------------------------------------- /images/switchopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/switchopen.png -------------------------------------------------------------------------------- /images/xnor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/xnor.png -------------------------------------------------------------------------------- /images/xor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metapyziks/Logic-Sim/9b30e553741f259808aa489cd32d91abddbe2377/images/xor.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LogicSim - Logic gate simulator 5 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Sorry! LogicSim requires a browser that supports HTML5 Canvas. 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /scripts/button.js: -------------------------------------------------------------------------------- 1 | Button = new Object(); 2 | Button.images = new Object(); 3 | Button.images.small = new Object(); 4 | Button.images.small.up = new Object(); 5 | Button.images.small.up.left = images.btnsmallleft; 6 | Button.images.small.up.mid = images.btnsmallmid; 7 | Button.images.small.up.right = images.btnsmallright; 8 | Button.images.small.over = new Object(); 9 | Button.images.small.over.left = images.btnsmallleftover; 10 | Button.images.small.over.mid = images.btnsmallmidover; 11 | Button.images.small.over.right = images.btnsmallrightover; 12 | 13 | Button.Base = function(x, y, width, height, contents) 14 | { 15 | this.isButton = true; 16 | 17 | this.x = x; 18 | this.y = y; 19 | this.width = width; 20 | this.height = height; 21 | 22 | this.text = (typeof(contents) == "string" ? contents : ""); 23 | this.image = (typeof(contents) == "string" ? null : contents); 24 | 25 | this.mouseOver = false; 26 | this.selected = false; 27 | 28 | this.isPositionOver = function(posX, posY) 29 | { 30 | return posX >= this.x && posY >= this.y 31 | && posX < this.x + this.width && posY < this.y + this.height; 32 | } 33 | 34 | this.mouseMove = function(mouseX, mouseY) 35 | { 36 | this.mouseOver = this.isPositionOver(mouseX, mouseY); 37 | } 38 | 39 | this.mouseDown = function(mouseX, mouseY) 40 | { 41 | 42 | } 43 | 44 | this.renderBack = function(context) 45 | { 46 | if (this.selected) 47 | { 48 | context.fillStyle = "#A0D1EF"; 49 | context.fillRect(-4, -4, this.width + 8, this.height + 8); 50 | } 51 | } 52 | 53 | this.render = function(context) 54 | { 55 | context.translate(this.x, this.y); 56 | 57 | this.renderBack(context); 58 | 59 | if (this.image) 60 | { 61 | context.drawImage(this.image, (this.width - this.image.width) / 2, 62 | (this.height - this.image.height) / 2); 63 | } 64 | else if (this.text) 65 | { 66 | context.fillStyle = "#FFFFFF"; 67 | context.font = "11px sans-serif"; 68 | context.textAlign = "center"; 69 | context.fillText(this.text, this.width / 2, 12); 70 | context.textAlign = "left"; 71 | } 72 | 73 | context.translate(-this.x, -this.y); 74 | } 75 | } 76 | 77 | Button.Tool = function(image, mouseDown) 78 | { 79 | this.__proto__ = new Button.Base(0, 0, image.width, image.height, image); 80 | this.mouseDown = mouseDown; 81 | } 82 | 83 | Button.Small = function(x, y, width, contents) 84 | { 85 | this.upImages = [Button.images.small.up.left, Button.images.small.up.mid, Button.images.small.up.right]; 86 | this.overImages = [Button.images.small.over.left, Button.images.small.over.mid, Button.images.small.over.right]; 87 | 88 | this.__proto__ = new Button.Base(x, y, width, 16, contents); 89 | 90 | this.renderBack = function(context) 91 | { 92 | var imgs = this.mouseOver ? this.overImages : this.upImages; 93 | 94 | context.drawImage(imgs[0], 0, 0); 95 | context.fillStyle = context.createPattern(imgs[1], "repeat-x"); 96 | context.fillRect(1, 0, this.width - 2, this.height); 97 | context.drawImage(imgs[2], this.width - 1, 0); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /scripts/environment.js: -------------------------------------------------------------------------------- 1 | function Environment() 2 | { 3 | this.gates = new Array(); 4 | this.wireGroups = new Array(); 5 | 6 | this.clear = function() 7 | { 8 | this.gates = new Array(); 9 | this.wireGroups = new Array(); 10 | } 11 | 12 | this.save = function() 13 | { 14 | var obj = { gates: [], wires: [] }; 15 | 16 | for (var i = 0; i < this.gates.length; ++i) 17 | { 18 | var gate = this.gates[i]; 19 | var gobj = { 20 | t: gate.type.ctorname, 21 | x: gate.x, 22 | y: gate.y, 23 | o: gate.getOutputs(), 24 | d: gate.saveData() 25 | }; 26 | 27 | if (gobj.t == "CustomIC") { 28 | gobj.i = logicSim.customGroup.items.indexOf(gate.type); 29 | gobj.e = gate.environment.save(); 30 | } 31 | 32 | obj.gates.push(gobj); 33 | } 34 | 35 | for (var i = 0; i < this.wireGroups.length; ++i) 36 | { 37 | var wires = this.wireGroups[i].getWires(); 38 | for (var j = 0; j < wires.length; ++j) 39 | { 40 | var wire = wires[j]; 41 | obj.wires.push({ 42 | sx: wire.start.x, 43 | sy: wire.start.y, 44 | ex: wire.end.x, 45 | ey: wire.end.y 46 | }); 47 | } 48 | } 49 | 50 | return obj; 51 | } 52 | 53 | this.load = function(obj, ics) 54 | { 55 | for (var i = 0; i < obj.gates.length; ++i) 56 | { 57 | var info = obj.gates[i]; 58 | var gate = null; 59 | 60 | if (info.t == "CustomIC") { 61 | gate = new Gate(ics[info.i], info.x, info.y); 62 | var env = new Environment(); 63 | env.load(info.e, ics); 64 | gate.environment = env; 65 | } else { 66 | var ctor = window[info.t]; 67 | gate = new Gate(new ctor(), info.x, info.y); 68 | } 69 | 70 | this.placeGate(gate); 71 | gate.setOutputs(info.o); 72 | gate.loadData(info.d); 73 | } 74 | 75 | for (var i = 0; i < obj.wires.length; ++i) 76 | { 77 | var info = obj.wires[i]; 78 | this.placeWire(new Pos(info.sx, info.sy), new Pos(info.ex, info.ey)); 79 | } 80 | } 81 | 82 | this.clone = function() 83 | { 84 | var env = new Environment(); 85 | 86 | for (var i = 0; i < this.gates.length; ++i) { 87 | env.placeGate(this.gates[i].clone()); 88 | } 89 | 90 | var wires = this.getAllWires(); 91 | for (var i = 0; i < wires.length; ++i) { 92 | env.placeWire(wires[i].start, wires[i].end); 93 | } 94 | 95 | return env; 96 | } 97 | 98 | var myIOSort = function (a, b) { 99 | if (a.y < b.y) return -1; 100 | if (a.y == b.y) return a.x < b.x ? -1 : a.x == b.x ? 0 : 1; 101 | return 1; 102 | } 103 | 104 | this.getInputs = function() 105 | { 106 | var inputs = new Array(); 107 | for (var i = 0; i < this.gates.length; ++i) { 108 | var gate = this.gates[i]; 109 | if (gate.type.ctorname == "ICInput") { 110 | inputs.push(gate); 111 | } 112 | } 113 | 114 | return inputs.sort(myIOSort); 115 | } 116 | 117 | this.getOutputs = function() 118 | { 119 | var outputs = new Array(); 120 | for (var i = 0; i < this.gates.length; ++i) { 121 | var gate = this.gates[i]; 122 | if (gate.type.ctorname == "ICOutput") { 123 | outputs.push(gate); 124 | } 125 | } 126 | 127 | return outputs.sort(myIOSort); 128 | } 129 | 130 | this.tryMerge = function(env, offset, selected, shallow) 131 | { 132 | if (offset == null) offset = new Pos(0, 0); 133 | if (selected == null) selected = false; 134 | if (shallow == null) shallow = false; 135 | 136 | for (var i = 0; i < env.gates.length; ++i) { 137 | var gate = env.gates[i].clone(shallow); 138 | gate.x += offset.x; 139 | gate.y += offset.y; 140 | gate.selected = selected; 141 | if (!this.canPlaceGate(gate)) return false; 142 | this.placeGate(gate); 143 | } 144 | 145 | var wires = env.getAllWires(); 146 | for (var i = 0; i < wires.length; ++i) { 147 | var wire = wires[i]; 148 | wire = new Wire(wire.start.add(offset), wire.end.add(offset)); 149 | if (!this.canPlaceWire(wire)) return false; 150 | this.placeWire(wire.start, wire.end, selected); 151 | } 152 | 153 | return true; 154 | } 155 | 156 | this.getAllWires = function() 157 | { 158 | var wires = []; 159 | for (var i = this.wireGroups.length - 1; i >= 0; i--) { 160 | wires.pushMany(this.wireGroups[i].getWires()); 161 | } 162 | 163 | return wires; 164 | } 165 | 166 | this.deselectAll = function() 167 | { 168 | for (var i = this.gates.length - 1; i >= 0; --i) { 169 | this.gates[i].selected = false; 170 | } 171 | 172 | var wires = this.getAllWires(); 173 | for (var i = wires.length - 1; i >= 0; --i) { 174 | wires[i].selected = false; 175 | } 176 | } 177 | 178 | this.canPlaceGate = function(gate) 179 | { 180 | var rect = gate.getRect(); 181 | 182 | if (rect.x < 256) return false; 183 | 184 | for (var i = 0; i < this.gates.length; ++i) { 185 | var other = this.gates[i].getRect(); 186 | 187 | if (rect.intersects(other)) return false; 188 | } 189 | 190 | var crossed = false; 191 | for (var i = 0; i < this.wireGroups.length; ++ i) { 192 | var group = this.wireGroups[i]; 193 | var wires = group.getWires(); 194 | 195 | for (var j = 0; j < wires.length; ++ j) { 196 | var wire = wires[j]; 197 | if (wire.start.x < rect.right && wire.end.x > rect.left 198 | && wire.start.y <= rect.bottom && wire.end.y >= rect.top) 199 | return false; 200 | } 201 | 202 | for (var j = 0; j < gate.outputs.length; ++ j) { 203 | var out = gate.outputs[j]; 204 | if (group.crossesPos(out.getPosition(gate.type, gate.x, gate.y))) { 205 | if (crossed || group.input != null) return false; 206 | 207 | crossed = true; 208 | } 209 | } 210 | } 211 | 212 | return true; 213 | } 214 | 215 | this.placeGate = function(gate) 216 | { 217 | gate.unlinkAll(); 218 | 219 | var r0 = gate.getRect(logicSim.getGridSize()); 220 | 221 | for (var i = 0; i < this.gates.length; ++ i) { 222 | var other = this.gates[i]; 223 | var r1 = other.getRect(logicSim.getGridSize()); 224 | 225 | if (r0.left == r1.right || r1.left == r0.right 226 | || r0.top == r1.bottom || r1.top == r0.bottom) { 227 | for (var j = 0; j < gate.inputs.length; ++ j) { 228 | var inp = gate.inputs[j]; 229 | for (var k = 0; k < other.outputs.length; ++ k) { 230 | var out = other.outputs[k]; 231 | 232 | if (inp.getPosition(gate.type, gate.x, gate.y).equals( 233 | out.getPosition(other.type, other.x, other.y))) { 234 | gate.linkInput(other, out, inp); 235 | } 236 | } 237 | } 238 | 239 | for (var j = 0; j < gate.outputs.length; ++ j) { 240 | var out = gate.outputs[j]; 241 | for (var k = 0; k < other.inputs.length; ++ k) { 242 | var inp = other.inputs[k]; 243 | 244 | if (out.getPosition(gate.type, gate.x, gate.y).equals( 245 | inp.getPosition(other.type, other.x, other.y))) { 246 | other.linkInput(gate, out, inp); 247 | } 248 | } 249 | } 250 | } 251 | } 252 | 253 | for (var i = 0; i < this.wireGroups.length; ++ i) { 254 | var group = this.wireGroups[i]; 255 | 256 | for (var j = 0; j < gate.inputs.length; ++ j) { 257 | var pos = gate.inputs[j].getPosition(gate.type, gate.x, gate.y); 258 | 259 | if (group.crossesPos(pos)) group.addOutput(gate, gate.inputs[j]); 260 | } 261 | 262 | for (var j = 0; j < gate.outputs.length; ++ j) { 263 | var pos = gate.outputs[j].getPosition(gate.type, gate.x, gate.y); 264 | 265 | if (group.crossesPos(pos)) group.setInput(gate, gate.outputs[j]); 266 | } 267 | } 268 | 269 | this.gates.push(gate); 270 | } 271 | 272 | this.removeGate = function(gate) 273 | { 274 | var index = this.gates.indexOf(gate); 275 | this.gates.splice(index, 1); 276 | 277 | for (var i = 0; i < this.gates.length; ++ i) { 278 | if (this.gates[i].isLinked(gate)) { 279 | this.gates[i].unlinkGate(gate); 280 | } 281 | 282 | if (gate.isLinked(this.gates[i])) { 283 | gate.unlinkGate(this.gates[i]); 284 | } 285 | } 286 | 287 | for (var i = 0; i < this.wireGroups.length; ++ i) { 288 | var group = this.wireGroups[i]; 289 | 290 | if (group.input != null && group.input.gate == gate) { 291 | group.input = null; 292 | } 293 | 294 | for (var j = group.outputs.length - 1; j >= 0; -- j) { 295 | if (group.outputs[j].gate == gate) { 296 | group.outputs.splice(j, 1); 297 | } 298 | } 299 | } 300 | } 301 | 302 | this.canPlaceWire = function(wire) 303 | { 304 | var input = null; 305 | 306 | if (wire.start.x < 256) return false; 307 | 308 | for (var i = 0; i < this.wireGroups.length; ++ i) { 309 | var group = this.wireGroups[i]; 310 | 311 | if (group.canAddWire(wire)) { 312 | if (wire.start.equals(wire.end)) return false; 313 | 314 | if (group.input != null) { 315 | if (input != null && !group.input.equals(input)) { 316 | return false; 317 | } 318 | 319 | input = group.input; 320 | } 321 | } 322 | } 323 | 324 | for (var i = 0; i < this.gates.length; ++ i) { 325 | var gate = this.gates[i]; 326 | var rect = gate.getRect(logicSim.getGridSize()); 327 | 328 | if (wire.start.x < rect.right && wire.end.x > rect.left 329 | && wire.start.y <= rect.bottom && wire.end.y >= rect.top) { 330 | return false; 331 | } 332 | 333 | if (wire.start.x == rect.right || rect.left == wire.end.x 334 | || wire.start.y == rect.bottom || rect.top == wire.end.y) { 335 | for (var j = 0; j < gate.outputs.length; ++ j) { 336 | var inp = new Link(gate, gate.outputs[j]); 337 | var pos = gate.outputs[j].getPosition(gate.type, gate.x, gate.y); 338 | 339 | if (wire.crossesPos(pos)) { 340 | if (input != null && !inp.equals(input)) return false; 341 | 342 | input = inp; 343 | } 344 | } 345 | } 346 | } 347 | 348 | return true; 349 | } 350 | 351 | this.placeWire = function(start, end, selected) 352 | { 353 | if (start.equals(end)) { 354 | return; 355 | } 356 | 357 | // Here we go... 358 | 359 | selected = selected != null ? true : false; 360 | var wire = new Wire(start, end); 361 | wire.selected = selected; 362 | 363 | var group = new WireGroup(); 364 | group.addWire(wire); 365 | 366 | // Check for gate input / output intersections 367 | for (var i = 0; i < this.gates.length; ++ i) { 368 | var gate = this.gates[i]; 369 | var rect = gate.getRect(logicSim.getGridSize()); 370 | 371 | if (wire.start.x == rect.right || rect.left == wire.end.x 372 | || wire.start.y == rect.bottom || rect.top == wire.end.y) { 373 | for (var j = 0; j < gate.inputs.length; ++ j) { 374 | var pos = gate.inputs[j].getPosition(gate.type, gate.x, gate.y); 375 | 376 | if (wire.crossesPos(pos)) { 377 | wire.group.addOutput(gate, gate.inputs[j]); 378 | } 379 | } 380 | 381 | for (var j = 0; j < gate.outputs.length; ++ j) { 382 | var pos = gate.outputs[j].getPosition(gate.type, gate.x, gate.y); 383 | 384 | if (wire.crossesPos(pos)) { 385 | wire.group.setInput(gate, gate.outputs[j]); 386 | } 387 | } 388 | } 389 | } 390 | 391 | // Find all wire groups that are connected to the new wire, and 392 | // dump their wires, input and outputs into the new group 393 | var wires = null; 394 | for (var i = this.wireGroups.length - 1; i >= 0; -- i) { 395 | var oldGroup = this.wireGroups[i]; 396 | if (oldGroup.canAddWire(wire)) { 397 | this.wireGroups.splice(i, 1); 398 | 399 | wires = oldGroup.getWires(); 400 | for (var j = 0; j < wires.length; ++ j) { 401 | var newWire = new Wire(wires[j].start, wires[j].end); 402 | newWire.selected = wires[j].selected; 403 | group.addWire(newWire); 404 | } 405 | 406 | if (oldGroup.input != null) { 407 | group.setInput(oldGroup.input.gate, oldGroup.input.socket); 408 | } 409 | 410 | for (var j = 0; j < oldGroup.outputs.length; ++ j) { 411 | group.addOutput(oldGroup.outputs[j].gate, oldGroup.outputs[j].socket); 412 | } 413 | } 414 | } 415 | 416 | // Merge wires that run along eachother 417 | wires = group.getWires(); 418 | for (var i = wires.length - 1; i >= 0; -- i) { 419 | var w = wires[i]; 420 | for (var j = i - 1; j >= 0; -- j) { 421 | var other = wires[j]; 422 | 423 | if (w.runsAlong(other)) { 424 | w.merge(other); 425 | wires.splice(j, 1); 426 | break; 427 | } 428 | } 429 | } 430 | 431 | // Split at intersections 432 | for (var i = 0; i < wires.length; ++ i) { 433 | var w = wires[i]; 434 | for (var j = i + 1; j < wires.length; ++ j) { 435 | var other = wires[j]; 436 | 437 | if (w.isHorizontal() == other.isHorizontal()) continue; 438 | 439 | if (w.intersects(other)) { 440 | wires.pushMany(w.split(other)); 441 | wires.pushMany(other.split(w)); 442 | } 443 | } 444 | } 445 | 446 | // Connect touching wires 447 | for (var i = 0; i < wires.length; ++ i) { 448 | var w = wires[i]; 449 | for (var j = i + 1; j < wires.length; ++ j) { 450 | var other = wires[j]; 451 | 452 | if (w.intersects(other)) { 453 | w.connect(other); 454 | other.connect(w); 455 | } 456 | } 457 | } 458 | 459 | // Add the new group to the environment 460 | this.wireGroups.push(group); 461 | } 462 | 463 | this.removeWire = function(wire) 464 | { 465 | this.removeWires([wire]); 466 | } 467 | 468 | this.removeWires = function(toRemove) 469 | { 470 | var survivors = new Array(); 471 | 472 | for (var i = 0; i < toRemove.length; ++ i) { 473 | var group = toRemove[i].group; 474 | if (this.wireGroups.contains(group)) { 475 | var wires = group.getWires(); 476 | 477 | for (var j = 0; j < wires.length; ++ j) { 478 | var w = wires[j]; 479 | if (!toRemove.containsEqual(w)) { 480 | survivors.push({start: w.start, end: w.end}); 481 | } 482 | } 483 | 484 | var gindex = this.wireGroups.indexOf(group); 485 | this.wireGroups.splice(gindex, 1); 486 | group.removeAllOutputs(); 487 | } 488 | } 489 | 490 | for (var i = 0; i < survivors.length; ++ i) { 491 | this.placeWire(survivors[i].start, survivors[i].end); 492 | } 493 | } 494 | 495 | this.step = function() 496 | { 497 | for (var i = 0; i < this.gates.length; ++ i) { 498 | this.gates[i].step(); 499 | } 500 | 501 | for (var i = 0; i < this.gates.length; ++ i) { 502 | this.gates[i].commit(); 503 | } 504 | } 505 | 506 | this.render = function(context, offset, selectClr) 507 | { 508 | if (offset == null) { 509 | offset = new Pos(0, 0); 510 | } 511 | 512 | for (var i = 0; i < this.wireGroups.length; ++ i) { 513 | this.wireGroups[i].render(context, offset, selectClr); 514 | } 515 | 516 | for (var i = 0; i < this.gates.length; ++ i) { 517 | this.gates[i].render(context, offset, selectClr); 518 | } 519 | } 520 | } 521 | -------------------------------------------------------------------------------- /scripts/gate.js: -------------------------------------------------------------------------------- 1 | SocketFace = new Object(); 2 | 3 | SocketFace.left = "LEFT"; 4 | SocketFace.top = "TOP"; 5 | SocketFace.right = "RIGHT"; 6 | SocketFace.bottom = "BOTTOM"; 7 | 8 | function SocketInfo(face, offset, label) 9 | { 10 | this.face = face; 11 | this.offset = offset; 12 | this.label = label; 13 | 14 | this.isLeft = this.face == SocketFace.left; 15 | this.isTop = this.face == SocketFace.top; 16 | this.isRight = this.face == SocketFace.right; 17 | this.isBottom = this.face == SocketFace.bottom; 18 | 19 | this.getPosition = function(gateType, x, y) 20 | { 21 | return new Pos( 22 | x + 23 | ((this.face == SocketFace.left) ? 0 24 | : (this.face == SocketFace.right) ? gateType.width 25 | : this.offset * 8), 26 | y + 27 | ((this.face == SocketFace.top) ? 0 28 | : (this.face == SocketFace.bottom) ? gateType.height 29 | : this.offset * 8) 30 | ); 31 | } 32 | } 33 | 34 | function GateType(name, width, height, inputs, outputs) 35 | { 36 | this.isGateType = true; 37 | 38 | this.name = name; 39 | 40 | this.width = width; 41 | this.height = height; 42 | 43 | this.inputs = inputs; 44 | this.outputs = outputs; 45 | 46 | this.func = function(gate, inputs) 47 | { 48 | return [false]; 49 | } 50 | 51 | this.initialize = function(gate) 52 | { 53 | 54 | } 55 | 56 | this.click = function(gate) 57 | { 58 | 59 | } 60 | 61 | this.mouseDown = function(gate) 62 | { 63 | 64 | } 65 | 66 | this.mouseUp = function(gate) 67 | { 68 | 69 | } 70 | 71 | this.saveData = function(gate) 72 | { 73 | return null; 74 | } 75 | 76 | this.loadData = function(gate, data) 77 | { 78 | 79 | } 80 | 81 | this.render = function(context, x, y, gate) 82 | { 83 | context.strokeStyle = "#000000"; 84 | context.lineWidth = 2; 85 | 86 | for (var i = 0; i < this.inputs.length + this.outputs.length; ++ i) 87 | { 88 | var inp = (i < this.inputs.length ? this.inputs[i] : this.outputs[i - this.inputs.length]); 89 | var start = inp.getPosition(this, x, y); 90 | var end = inp.getPosition(this, x, y); 91 | 92 | if (inp.face == SocketFace.left || inp.face == SocketFace.right) 93 | end.x = x + this.width / 2; 94 | else 95 | end.y = y + this.height / 2; 96 | 97 | context.beginPath(); 98 | context.moveTo(start.x, start.y); 99 | context.lineTo(end.x, end.y); 100 | context.stroke(); 101 | context.closePath(); 102 | } 103 | } 104 | } 105 | 106 | function DefaultGate(name, image, renderOverride, inputs, outputs) 107 | { 108 | this.__proto__ = new GateType(name, image.width, image.height, inputs, outputs); 109 | 110 | this.ctorname = arguments.callee.caller.name; 111 | 112 | this.image = image; 113 | this.renderOverride = renderOverride; 114 | 115 | this.render = function(context, x, y, gate) 116 | { 117 | this.__proto__.render(context, x, y, gate); 118 | if (!this.renderOverride) 119 | context.drawImage(this.image, x, y); 120 | } 121 | } 122 | 123 | function CustomIC(name, environment) 124 | { 125 | var envInputs = environment.getInputs(); 126 | var envOutputs = environment.getOutputs(); 127 | 128 | var inputs = new Array(); 129 | var outputs = new Array(); 130 | 131 | this.ctorname = arguments.callee.name; 132 | 133 | this.environment = environment; 134 | 135 | for (var i = 0; i < envInputs.length; ++ i) { 136 | var input = envInputs[i]; 137 | inputs[i] = new SocketInfo(SocketFace.left, 2 + i * 2, "I" + i) 138 | } 139 | 140 | for (var i = 0; i < envOutputs.length; ++ i) { 141 | var input = envOutputs[i]; 142 | outputs[i] = new SocketInfo(SocketFace.right, 2 + i * 2, "O" + i) 143 | } 144 | 145 | this.__proto__ = new GateType(name, 64, 146 | Math.max(32, 16 * (Math.max(envInputs.length, envOutputs.length) + 1)), 147 | inputs, outputs); 148 | 149 | this.initialize = function(gate) 150 | { 151 | gate.environment = this.environment.clone(); 152 | } 153 | 154 | this.func = function(gate, inputs) 155 | { 156 | var ins = gate.environment.getInputs(); 157 | for (var i = 0; i < ins.length; ++ i) { 158 | ins[i].value = inputs[i]; 159 | } 160 | 161 | gate.environment.step(); 162 | 163 | var vals = new Array(); 164 | var outs = gate.environment.getOutputs(); 165 | for (var i = 0; i < outs.length; ++ i) { 166 | vals[i] = outs[i].value; 167 | } 168 | 169 | return vals; 170 | } 171 | 172 | this.render = function(context, x, y, gate) 173 | { 174 | this.__proto__.render(context, x, y, gate); 175 | 176 | context.strokeStyle = "#000000"; 177 | context.fillStyle = "#ffffff"; 178 | context.lineWidth = 3; 179 | 180 | context.beginPath(); 181 | context.rect(x + 9.5, y + 1.5, this.width - 19, this.height - 3); 182 | context.fill(); 183 | context.stroke(); 184 | context.closePath(); 185 | 186 | context.fillStyle = "#000000"; 187 | context.font = "bold 16px sans-serif"; 188 | context.textAlign = "center"; 189 | context.textBaseline = "middle"; 190 | 191 | var width = context.measureText(this.name).width; 192 | 193 | if (this.width - 16 > this.height) { 194 | context.fillText(this.name, x + this.width / 2, y + this.height / 2, this.width - 24); 195 | } else { 196 | context.save(); 197 | context.translate(x + this.width / 2, y + this.height / 2); 198 | context.rotate(Math.PI / 2); 199 | context.fillText(this.name, 0, 0, this.height - 12); 200 | context.restore(); 201 | } 202 | 203 | context.textAlign = "left"; 204 | context.textBaseline = "alphabetic"; 205 | } 206 | } 207 | 208 | function BufferGate() 209 | { 210 | this.__proto__ = new DefaultGate("BUF", images.buffer, false, 211 | [ 212 | new SocketInfo(SocketFace.left, 2, "A") 213 | ], 214 | [ 215 | new SocketInfo(SocketFace.right, 2, "Q") 216 | ] 217 | ); 218 | 219 | this.func = function(gate, inputs) 220 | { 221 | return [inputs[0]]; 222 | } 223 | } 224 | 225 | function AndGate() 226 | { 227 | this.__proto__ = new DefaultGate("AND", images.and, false, 228 | [ 229 | new SocketInfo(SocketFace.left, 1, "A"), 230 | new SocketInfo(SocketFace.left, 3, "B") 231 | ], 232 | [ 233 | new SocketInfo(SocketFace.right, 2, "Q") 234 | ] 235 | ); 236 | 237 | this.func = function(gate, inputs) 238 | { 239 | return [inputs[0] && inputs[1]]; 240 | } 241 | } 242 | 243 | function OrGate() 244 | { 245 | this.__proto__ = new DefaultGate("OR", images.or, false, 246 | [ 247 | new SocketInfo(SocketFace.left, 1, "A"), 248 | new SocketInfo(SocketFace.left, 3, "B") 249 | ], 250 | [ 251 | new SocketInfo(SocketFace.right, 2, "Q") 252 | ] 253 | ); 254 | 255 | this.func = function(gate, inputs) 256 | { 257 | return [inputs[0] || inputs[1]]; 258 | } 259 | } 260 | 261 | function XorGate() 262 | { 263 | this.__proto__ = new DefaultGate("XOR", images.xor, false, 264 | [ 265 | new SocketInfo(SocketFace.left, 1, "A"), 266 | new SocketInfo(SocketFace.left, 3, "B") 267 | ], 268 | [ 269 | new SocketInfo(SocketFace.right, 2, "Q") 270 | ] 271 | ); 272 | 273 | this.func = function(gate, inputs) 274 | { 275 | return [inputs[0] ^ inputs[1]]; 276 | } 277 | } 278 | 279 | function NotGate() 280 | { 281 | this.__proto__ = new DefaultGate("NOT", images.not, false, 282 | [ 283 | new SocketInfo(SocketFace.left, 2, "A") 284 | ], 285 | [ 286 | new SocketInfo(SocketFace.right, 2, "Q") 287 | ] 288 | ); 289 | 290 | this.func = function(gate, inputs) 291 | { 292 | return [!inputs[0]]; 293 | } 294 | } 295 | 296 | function NandGate() 297 | { 298 | this.__proto__ = new DefaultGate("NAND", images.nand, false, 299 | [ 300 | new SocketInfo(SocketFace.left, 1, "A"), 301 | new SocketInfo(SocketFace.left, 3, "B") 302 | ], 303 | [ 304 | new SocketInfo(SocketFace.right, 2, "Q") 305 | ] 306 | ); 307 | 308 | this.func = function(gate, inputs) 309 | { 310 | return [!inputs[0] || !inputs[1]]; 311 | } 312 | } 313 | 314 | function NorGate() 315 | { 316 | this.__proto__ = new DefaultGate("NOR", images.nor, false, 317 | [ 318 | new SocketInfo(SocketFace.left, 1, "A"), 319 | new SocketInfo(SocketFace.left, 3, "B") 320 | ], 321 | [ 322 | new SocketInfo(SocketFace.right, 2, "Q") 323 | ] 324 | ); 325 | 326 | this.func = function(gate, inputs) 327 | { 328 | return [!inputs[0] && !inputs[1]]; 329 | } 330 | } 331 | 332 | function XnorGate() 333 | { 334 | this.__proto__ = new DefaultGate("XNOR", images.xnor, false, 335 | [ 336 | new SocketInfo(SocketFace.left, 1, "A"), 337 | new SocketInfo(SocketFace.left, 3, "B") 338 | ], 339 | [ 340 | new SocketInfo(SocketFace.right, 2, "Q") 341 | ] 342 | ); 343 | 344 | this.func = function(gate, inputs) 345 | { 346 | return [inputs[0] == inputs[1]]; 347 | } 348 | } 349 | 350 | function ConstInput() 351 | { 352 | this.onImage = images.conston; 353 | this.offImage = images.constoff; 354 | 355 | this.__proto__ = new DefaultGate("IN", this.onImage, true, [], 356 | [ 357 | new SocketInfo(SocketFace.right, 2, "Q") 358 | ] 359 | ); 360 | 361 | this.initialize = function(gate) 362 | { 363 | gate.on = true; 364 | } 365 | 366 | this.click = function(gate) 367 | { 368 | gate.on = !gate.on; 369 | } 370 | 371 | this.func = function(gate, inputs) 372 | { 373 | return [gate.on]; 374 | } 375 | 376 | this.saveData = function(gate) 377 | { 378 | return gate.on; 379 | } 380 | 381 | this.loadData = function(gate, data) 382 | { 383 | gate.on = data; 384 | } 385 | 386 | this.render = function(context, x, y, gate) 387 | { 388 | this.__proto__.render(context, x, y); 389 | context.drawImage(gate == null || gate.on ? this.onImage : this.offImage, x, y); 390 | } 391 | } 392 | 393 | function ClockInput() 394 | { 395 | this.__proto__ = new DefaultGate("CLOCK", images.clock, false, [], 396 | [ 397 | new SocketInfo(SocketFace.right, 2, "Q") 398 | ] 399 | ); 400 | 401 | this.func = function(gate, inputs) 402 | { 403 | var period = 1000 / gate.freq; 404 | return [new Date().getTime() % period >= period / 2]; 405 | } 406 | 407 | this.initialize = function(gate) 408 | { 409 | gate.freq = 1; 410 | } 411 | 412 | this.saveData = function(gate) 413 | { 414 | return gate.freq; 415 | } 416 | 417 | this.loadData = function(gate, data) 418 | { 419 | gate.freq = data; 420 | } 421 | 422 | this.click = function(gate) 423 | { 424 | gate.freq *= 2; 425 | 426 | if (gate.freq >= 32) 427 | gate.freq = 0.125; 428 | } 429 | } 430 | 431 | function ToggleSwitch() 432 | { 433 | this.openImage = images.switchopen; 434 | this.closedImage = images.switchclosed; 435 | 436 | this.__proto__ = new DefaultGate("TSWITCH", this.openImage, true, 437 | [ 438 | new SocketInfo(SocketFace.left, 2, "A"), 439 | ], 440 | [ 441 | new SocketInfo(SocketFace.right, 2, "Q") 442 | ] 443 | ); 444 | 445 | this.func = function(gate, inputs) 446 | { 447 | return [!gate.open && inputs[0]]; 448 | } 449 | 450 | this.initialize = function(gate) 451 | { 452 | gate.open = true; 453 | } 454 | 455 | this.click = function(gate) 456 | { 457 | gate.open = !gate.open; 458 | } 459 | 460 | this.saveData = function(gate) 461 | { 462 | return gate.open; 463 | } 464 | 465 | this.loadData = function(gate, data) 466 | { 467 | gate.open = data; 468 | } 469 | 470 | this.render = function(context, x, y, gate) 471 | { 472 | this.__proto__.render(context, x, y); 473 | context.drawImage(gate == null || gate.open ? this.openImage : this.closedImage, x, y); 474 | } 475 | } 476 | 477 | function PushSwitchA() 478 | { 479 | this.openImage = images.pushswitchaopen; 480 | this.closedImage = images.pushswitchaclosed; 481 | 482 | this.__proto__ = new DefaultGate("PSWITCHA", this.openImage, true, 483 | [ 484 | new SocketInfo(SocketFace.left, 2, "A"), 485 | ], 486 | [ 487 | new SocketInfo(SocketFace.right, 2, "Q") 488 | ] 489 | ); 490 | 491 | this.func = function(gate, inputs) 492 | { 493 | return [!gate.open && inputs[0]]; 494 | } 495 | 496 | this.initialize = function(gate) 497 | { 498 | gate.open = true; 499 | } 500 | 501 | this.mouseDown = function(gate) 502 | { 503 | gate.open = false; 504 | } 505 | 506 | this.mouseUp = function(gate) 507 | { 508 | gate.open = true; 509 | } 510 | 511 | this.render = function(context, x, y, gate) 512 | { 513 | this.__proto__.render(context, x, y); 514 | context.drawImage(gate == null || gate.open ? this.openImage : this.closedImage, x, y); 515 | } 516 | } 517 | 518 | function PushSwitchB() 519 | { 520 | this.openImage = images.pushswitchbopen; 521 | this.closedImage = images.pushswitchbclosed; 522 | 523 | this.__proto__ = new DefaultGate("PSWITCHB", this.closedImage, true, 524 | [ 525 | new SocketInfo(SocketFace.left, 2, "A"), 526 | ], 527 | [ 528 | new SocketInfo(SocketFace.right, 2, "Q") 529 | ] 530 | ); 531 | 532 | this.func = function(gate, inputs) 533 | { 534 | return [!gate.open && inputs[0]]; 535 | } 536 | 537 | this.initialize = function(gate) 538 | { 539 | gate.open = false; 540 | } 541 | 542 | this.mouseDown = function(gate) 543 | { 544 | gate.open = true; 545 | } 546 | 547 | this.mouseUp = function(gate) 548 | { 549 | gate.open = false; 550 | } 551 | 552 | this.render = function(context, x, y, gate) 553 | { 554 | this.__proto__.render(context, x, y); 555 | context.drawImage(gate != null && gate.open ? this.openImage : this.closedImage, x, y); 556 | } 557 | } 558 | 559 | function OutputDisplay() 560 | { 561 | this.onImage = images.outon; 562 | this.offImage = images.outoff; 563 | 564 | this.__proto__ = new DefaultGate("OUT", this.onImage, true, 565 | [ 566 | new SocketInfo(SocketFace.left, 2, "A"), 567 | ], 568 | [] 569 | ); 570 | 571 | this.func = function(gate, inputs) 572 | { 573 | gate.on = inputs[0]; 574 | return []; 575 | } 576 | 577 | this.initialize = function(gate) 578 | { 579 | gate.on = false; 580 | } 581 | 582 | this.render = function(context, x, y, gate) 583 | { 584 | this.__proto__.render(context, x, y); 585 | context.drawImage(gate == null || !gate.on ? this.offImage : this.onImage, x, y); 586 | } 587 | } 588 | 589 | function SevenSegDisplay() 590 | { 591 | this.baseImage = images.sevsegbase; 592 | this.segImages = 593 | [ 594 | images.sevsega, images.sevsegb, images.sevsegc, images.sevsegdp, 595 | images.sevsegd, images.sevsege, images.sevsegf, images.sevsegg 596 | ]; 597 | 598 | this.__proto__ = new DefaultGate("SEVSEG", this.baseImage, true, 599 | [ 600 | new SocketInfo(SocketFace.right, 2, "A"), 601 | new SocketInfo(SocketFace.right, 4, "B"), 602 | new SocketInfo(SocketFace.right, 6, "C"), 603 | new SocketInfo(SocketFace.right, 8, "DP"), 604 | new SocketInfo(SocketFace.left, 8, "D"), 605 | new SocketInfo(SocketFace.left, 6, "E"), 606 | new SocketInfo(SocketFace.left, 4, "F"), 607 | new SocketInfo(SocketFace.left, 2, "G") 608 | ], 609 | [] 610 | ); 611 | 612 | this.func = function(gate, inputs) 613 | { 614 | gate.active = inputs; 615 | return []; 616 | } 617 | 618 | this.initialize = function(gate) 619 | { 620 | gate.active = [false, false, false, false, false, false, false, false]; 621 | } 622 | 623 | this.render = function(context, x, y, gate) 624 | { 625 | this.__proto__.render(context, x, y); 626 | context.drawImage(this.baseImage, x, y); 627 | 628 | if (gate != null) 629 | for (var i = 0; i < 8; ++ i) 630 | if (gate.active[i]) 631 | context.drawImage(this.segImages[i], x, y); 632 | } 633 | } 634 | 635 | function DFlipFlop() 636 | { 637 | this.__proto__ = new DefaultGate("DFLIPFLOP", images.dflipflop, false, 638 | [ 639 | new SocketInfo(SocketFace.left, 2, "D"), 640 | new SocketInfo(SocketFace.left, 6, ">") 641 | ], 642 | [ 643 | new SocketInfo(SocketFace.right, 2, "Q"), 644 | new SocketInfo(SocketFace.right, 6, "NQ") 645 | ] 646 | ); 647 | 648 | this.func = function(gate, inputs) 649 | { 650 | if (!gate.oldClock && inputs[1]) { 651 | gate.state = inputs[0]; 652 | } 653 | 654 | gate.oldClock = inputs[1]; 655 | 656 | return [gate.state, !gate.state]; 657 | } 658 | 659 | this.initialize = function(gate) 660 | { 661 | gate.state = false; 662 | gate.oldClock = false; 663 | } 664 | 665 | this.saveData = function(gate) 666 | { 667 | return [gate.state, gate.oldClock]; 668 | } 669 | 670 | this.loadData = function(gate, data) 671 | { 672 | gate.state = data[0]; 673 | gate.oldClock = data[1]; 674 | } 675 | } 676 | 677 | function Encoder() 678 | { 679 | var inputs = []; 680 | for (var i = 0; i < 9; ++ i) 681 | inputs[i] = new SocketInfo(SocketFace.left, 2 + i * 2, "I" + i); 682 | 683 | var outputs = []; 684 | for (var i = 0; i < 4; ++ i) 685 | outputs[i] = new SocketInfo(SocketFace.right, 4 + i * 4, "O" + i); 686 | 687 | this.__proto__ = new DefaultGate("ENCODER", images.encoder, false, inputs, outputs); 688 | 689 | this.func = function(gate, inp) 690 | { 691 | var val = 0; 692 | for (var i = 8; i >= 0; -- i) 693 | { 694 | if (inp[i]) 695 | { 696 | val = i + 1; 697 | break; 698 | } 699 | } 700 | 701 | var out = []; 702 | for (var i = 0; i < 4; ++ i) 703 | out[i] = (val & (1 << i)) != 0; 704 | 705 | return out; 706 | } 707 | } 708 | 709 | function Decoder() 710 | { 711 | var inputs = []; 712 | for (var i = 0; i < 4; ++ i) 713 | inputs[i] = new SocketInfo(SocketFace.left, 4 + i * 4, "I" + i); 714 | 715 | var outputs = []; 716 | for (var i = 0; i < 9; ++ i) 717 | outputs[i] = new SocketInfo(SocketFace.right, 2 + i * 2, "O" + i); 718 | 719 | this.__proto__ = new DefaultGate("DECODER", images.decoder, false, inputs, outputs); 720 | 721 | this.func = function(gate, inp) 722 | { 723 | var val = 0; 724 | for (var i = 0; i < 4; ++ i) 725 | if (inp[i]) val += 1 << i; 726 | 727 | var out = []; 728 | for (var i = 0; i < 9; ++ i) 729 | out[i] = val == (i + 1); 730 | 731 | return out; 732 | } 733 | } 734 | 735 | function SevenSegDecoder() 736 | { 737 | var inputs = []; 738 | for (var i = 0; i < 4; ++ i) 739 | inputs[i] = new SocketInfo(SocketFace.left, 2 + i * 4, "I" + i); 740 | 741 | var outputs = []; 742 | for (var i = 0; i < 7; ++ i) 743 | outputs[i] = new SocketInfo(SocketFace.right, 2 + i * 2, "O" + i); 744 | 745 | this.__proto__ = new DefaultGate("7447", images.sevsegdecoder, false, inputs, outputs); 746 | 747 | var myOutputs = [ 748 | [ true, true, true, false, true, true, true ], 749 | [ true, true, false, false, false, false, false ], 750 | [ false, true, true, true, false, true, true ], 751 | [ true, true, true, true, false, false, true ], 752 | [ true, true, false, true, true, false, false ], 753 | [ true, false, true, true, true, false, true ], 754 | [ true, false, true, true, true, true, true ], 755 | [ true, true, true, false, false, false, false ], 756 | [ true, true, true, true, true, true, true ], 757 | [ true, true, true, true, true, false, true ], 758 | [ false, false, false, false, false, false, false ] 759 | ]; 760 | 761 | this.func = function(gate, inp) 762 | { 763 | var val = 0; 764 | for (var i = 0; i < 4; ++ i) 765 | if (inp[i]) val += 1 << i; 766 | 767 | return myOutputs[Math.min(val, myOutputs.length - 1)]; 768 | } 769 | } 770 | 771 | function ICInput() 772 | { 773 | this.__proto__ = new DefaultGate("ICINPUT", images.input, false, 774 | [], 775 | [ 776 | new SocketInfo(SocketFace.right, 2, "A") 777 | ] 778 | ); 779 | 780 | this.initialize = function(gate) 781 | { 782 | gate.value = false; 783 | } 784 | 785 | this.func = function(gate, inputs) 786 | { 787 | return [gate.value]; 788 | } 789 | } 790 | 791 | function ICOutput() 792 | { 793 | this.__proto__ = new DefaultGate("ICOUTPUT", images.output, false, 794 | [ 795 | new SocketInfo(SocketFace.left, 2, "A") 796 | ], 797 | [] 798 | ); 799 | 800 | this.initialize = function(gate) 801 | { 802 | gate.value = false; 803 | } 804 | 805 | this.func = function(gate, inputs) 806 | { 807 | gate.value = inputs[0]; 808 | return []; 809 | } 810 | } 811 | 812 | function Link(gate, socket) 813 | { 814 | this.gate = gate; 815 | this.socket = socket; 816 | 817 | this.getValue = function() 818 | { 819 | return this.gate.getOutput(this.socket); 820 | } 821 | 822 | this.equals = function(obj) 823 | { 824 | return this.gate == obj.gate && this.socket == obj.socket; 825 | } 826 | } 827 | 828 | function Gate(gateType, x, y, noInit) 829 | { 830 | if (noInit == null) noInit = false; 831 | 832 | var myOutputs = new Array(); 833 | var myNextOutputs = new Array(); 834 | var myInLinks = new Array(); 835 | 836 | this.type = gateType; 837 | 838 | this.x = x; 839 | this.y = y; 840 | 841 | this.isMouseDown = false; 842 | 843 | this.width = this.type.width; 844 | this.height = this.type.height; 845 | 846 | this.inputs = this.type.inputs; 847 | this.outputs = this.type.outputs; 848 | 849 | this.selected = false; 850 | 851 | for (var i = 0; i < this.type.inputs.length; ++i) 852 | myInLinks[i] = null; 853 | 854 | for (var i = 0; i < this.type.outputs.length; ++i) 855 | myOutputs[i] = false; 856 | 857 | this.clone = function(shallow) 858 | { 859 | if (shallow == null) shallow = false; 860 | 861 | var copy = new Gate(this.type, this.x, this.y, shallow); 862 | 863 | if (!shallow) copy.loadData(this.saveData()); 864 | 865 | return copy; 866 | } 867 | 868 | this.getRect = function(gridSize) 869 | { 870 | if (!gridSize) 871 | gridSize = 1; 872 | 873 | var rl = Math.round(this.x); 874 | var rt = Math.round(this.y); 875 | var rr = Math.round(this.x + this.width); 876 | var rb = Math.round(this.y + this.height); 877 | 878 | rl = Math.floor(rl / gridSize) * gridSize; 879 | rt = Math.floor(rt / gridSize) * gridSize; 880 | rr = Math.ceil(rr / gridSize) * gridSize; 881 | rb = Math.ceil(rb / gridSize) * gridSize; 882 | 883 | return new Rect(rl, rt, rr - rl, rb - rt); 884 | } 885 | 886 | this.linkInput = function(gate, output, input) 887 | { 888 | var index = this.inputs.indexOf(input); 889 | myInLinks[index] = new Link(gate, output); 890 | } 891 | 892 | this.isLinked = function(gate) 893 | { 894 | for (var i = 0; i < this.inputs.length; ++ i) 895 | if (myInLinks[i] != null && myInLinks[i].gate == gate) 896 | return true; 897 | 898 | return false; 899 | } 900 | 901 | this.unlinkAll = function() 902 | { 903 | for (var i = 0; i < this.inputs.length; ++ i) 904 | myInLinks[i] = null; 905 | } 906 | 907 | this.unlinkGate = function(gate) 908 | { 909 | for (var i = 0; i < this.inputs.length; ++ i) 910 | if (myInLinks[i] != null && myInLinks[i].gate == gate) 911 | myInLinks[i] = null; 912 | } 913 | 914 | this.unlinkInput = function(input) 915 | { 916 | var index = this.inputs.indexOf(input); 917 | myInLinks[index] = null; 918 | } 919 | 920 | this.getOutputs = function() 921 | { 922 | return myOutputs; 923 | } 924 | 925 | this.setOutputs = function(outputs) 926 | { 927 | myOutputs = outputs; 928 | } 929 | 930 | this.getOutput = function(output) 931 | { 932 | var index = this.outputs.indexOf(output); 933 | return myOutputs[index]; 934 | } 935 | 936 | this.click = function() 937 | { 938 | this.type.click(this); 939 | } 940 | 941 | this.mouseDown = function() 942 | { 943 | this.isMouseDown = true; 944 | this.type.mouseDown(this); 945 | } 946 | 947 | this.mouseUp = function() 948 | { 949 | this.isMouseDown = false; 950 | this.type.mouseUp(this); 951 | } 952 | 953 | this.step = function() 954 | { 955 | var inVals = new Array(); 956 | 957 | for (var i = 0; i < this.inputs.length; ++ i) 958 | { 959 | var link = myInLinks[i]; 960 | inVals[i] = (myInLinks[i] == null) 961 | ? false : link.getValue(); 962 | } 963 | 964 | myNextOutputs = this.type.func(this, inVals); 965 | } 966 | 967 | this.commit = function() 968 | { 969 | myOutputs = myNextOutputs; 970 | } 971 | 972 | this.saveData = function() 973 | { 974 | return this.type.saveData(this); 975 | } 976 | 977 | this.loadData = function(data) 978 | { 979 | this.type.loadData(this, data); 980 | } 981 | 982 | this.render = function(context, offset, selectClr) 983 | { 984 | if (this.selected) { 985 | var rect = this.getRect(); 986 | 987 | if (selectClr == null) selectClr = "#6666FF"; 988 | 989 | context.globalAlpha = 0.5; 990 | context.fillStyle = selectClr; 991 | context.fillRect(rect.left - 4 + offset.x, rect.top - 4 + offset.y, 992 | rect.width + 8, rect.height + 8); 993 | context.globalAlpha = 1.0; 994 | } 995 | 996 | this.type.render(context, this.x + offset.x, this.y + offset.y, this); 997 | 998 | context.strokeStyle = "#000000"; 999 | context.lineWidth = 2; 1000 | context.fillStyle = "#9999FF"; 1001 | 1002 | for (var i = 0; i < this.inputs.length + this.outputs.length; ++ i) { 1003 | var inp = (i < this.inputs.length ? this.inputs[i] 1004 | : this.outputs[i - this.inputs.length]); 1005 | var pos = inp.getPosition(this.type, this.x, this.y); 1006 | 1007 | if (i < this.inputs.length) { 1008 | if (myInLinks[i] != null) { 1009 | context.fillStyle = myInLinks[i].getValue() ? "#FF9999" : "#9999FF"; 1010 | } else { 1011 | context.fillStyle = "#999999"; 1012 | } 1013 | } else { 1014 | context.fillStyle = myOutputs[i - this.inputs.length] 1015 | ? "#FF9999" : "#9999FF"; 1016 | } 1017 | 1018 | context.beginPath(); 1019 | context.arc(pos.x + offset.x, pos.y + offset.y, 4, 0, Math.PI * 2, true); 1020 | context.fill(); 1021 | context.stroke(); 1022 | context.closePath(); 1023 | } 1024 | } 1025 | 1026 | if (!noInit) { 1027 | this.type.initialize(this); 1028 | } 1029 | } 1030 | -------------------------------------------------------------------------------- /scripts/lz-string-1.3.3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Pieroxy 2 | // This work is free. You can redistribute it and/or modify it 3 | // under the terms of the WTFPL, Version 2 4 | // For more information see LICENSE.txt or http://www.wtfpl.net/ 5 | // 6 | // For more information, the home page: 7 | // http://pieroxy.net/blog/pages/lz-string/testing.html 8 | // 9 | // LZ-based compression algorithm, version 1.3.3 10 | var LZString = { 11 | 12 | 13 | // private property 14 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 15 | _f : String.fromCharCode, 16 | 17 | compressToBase64 : function (input) { 18 | if (input == null) return ""; 19 | var output = ""; 20 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 21 | var i = 0; 22 | 23 | input = this.compress(input); 24 | 25 | while (i < input.length*2) { 26 | 27 | if (i%2==0) { 28 | chr1 = input.charCodeAt(i/2) >> 8; 29 | chr2 = input.charCodeAt(i/2) & 255; 30 | if (i/2+1 < input.length) 31 | chr3 = input.charCodeAt(i/2+1) >> 8; 32 | else 33 | chr3 = NaN; 34 | } else { 35 | chr1 = input.charCodeAt((i-1)/2) & 255; 36 | if ((i+1)/2 < input.length) { 37 | chr2 = input.charCodeAt((i+1)/2) >> 8; 38 | chr3 = input.charCodeAt((i+1)/2) & 255; 39 | } else 40 | chr2=chr3=NaN; 41 | } 42 | i+=3; 43 | 44 | enc1 = chr1 >> 2; 45 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 46 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 47 | enc4 = chr3 & 63; 48 | 49 | if (isNaN(chr2)) { 50 | enc3 = enc4 = 64; 51 | } else if (isNaN(chr3)) { 52 | enc4 = 64; 53 | } 54 | 55 | output = output + 56 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 57 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 58 | 59 | } 60 | 61 | return output; 62 | }, 63 | 64 | decompressFromBase64 : function (input) { 65 | if (input == null) return ""; 66 | var output = "", 67 | ol = 0, 68 | output_, 69 | chr1, chr2, chr3, 70 | enc1, enc2, enc3, enc4, 71 | i = 0, f=this._f; 72 | 73 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 74 | 75 | while (i < input.length) { 76 | 77 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 78 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 79 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 80 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 81 | 82 | chr1 = (enc1 << 2) | (enc2 >> 4); 83 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 84 | chr3 = ((enc3 & 3) << 6) | enc4; 85 | 86 | if (ol%2==0) { 87 | output_ = chr1 << 8; 88 | 89 | if (enc3 != 64) { 90 | output += f(output_ | chr2); 91 | } 92 | if (enc4 != 64) { 93 | output_ = chr3 << 8; 94 | } 95 | } else { 96 | output = output + f(output_ | chr1); 97 | 98 | if (enc3 != 64) { 99 | output_ = chr2 << 8; 100 | } 101 | if (enc4 != 64) { 102 | output += f(output_ | chr3); 103 | } 104 | } 105 | ol+=3; 106 | } 107 | 108 | return this.decompress(output); 109 | 110 | }, 111 | 112 | compressToUTF16 : function (input) { 113 | if (input == null) return ""; 114 | var output = "", 115 | i,c, 116 | current, 117 | status = 0, 118 | f = this._f; 119 | 120 | input = this.compress(input); 121 | 122 | for (i=0 ; i> 1)+32); 127 | current = (c & 1) << 14; 128 | break; 129 | case 1: 130 | output += f((current + (c >> 2))+32); 131 | current = (c & 3) << 13; 132 | break; 133 | case 2: 134 | output += f((current + (c >> 3))+32); 135 | current = (c & 7) << 12; 136 | break; 137 | case 3: 138 | output += f((current + (c >> 4))+32); 139 | current = (c & 15) << 11; 140 | break; 141 | case 4: 142 | output += f((current + (c >> 5))+32); 143 | current = (c & 31) << 10; 144 | break; 145 | case 5: 146 | output += f((current + (c >> 6))+32); 147 | current = (c & 63) << 9; 148 | break; 149 | case 6: 150 | output += f((current + (c >> 7))+32); 151 | current = (c & 127) << 8; 152 | break; 153 | case 7: 154 | output += f((current + (c >> 8))+32); 155 | current = (c & 255) << 7; 156 | break; 157 | case 8: 158 | output += f((current + (c >> 9))+32); 159 | current = (c & 511) << 6; 160 | break; 161 | case 9: 162 | output += f((current + (c >> 10))+32); 163 | current = (c & 1023) << 5; 164 | break; 165 | case 10: 166 | output += f((current + (c >> 11))+32); 167 | current = (c & 2047) << 4; 168 | break; 169 | case 11: 170 | output += f((current + (c >> 12))+32); 171 | current = (c & 4095) << 3; 172 | break; 173 | case 12: 174 | output += f((current + (c >> 13))+32); 175 | current = (c & 8191) << 2; 176 | break; 177 | case 13: 178 | output += f((current + (c >> 14))+32); 179 | current = (c & 16383) << 1; 180 | break; 181 | case 14: 182 | output += f((current + (c >> 15))+32, (c & 32767)+32); 183 | status = 0; 184 | break; 185 | } 186 | } 187 | 188 | return output + f(current + 32); 189 | }, 190 | 191 | 192 | decompressFromUTF16 : function (input) { 193 | if (input == null) return ""; 194 | var output = "", 195 | current,c, 196 | status=0, 197 | i = 0, 198 | f = this._f; 199 | 200 | while (i < input.length) { 201 | c = input.charCodeAt(i) - 32; 202 | 203 | switch (status++) { 204 | case 0: 205 | current = c << 1; 206 | break; 207 | case 1: 208 | output += f(current | (c >> 14)); 209 | current = (c&16383) << 2; 210 | break; 211 | case 2: 212 | output += f(current | (c >> 13)); 213 | current = (c&8191) << 3; 214 | break; 215 | case 3: 216 | output += f(current | (c >> 12)); 217 | current = (c&4095) << 4; 218 | break; 219 | case 4: 220 | output += f(current | (c >> 11)); 221 | current = (c&2047) << 5; 222 | break; 223 | case 5: 224 | output += f(current | (c >> 10)); 225 | current = (c&1023) << 6; 226 | break; 227 | case 6: 228 | output += f(current | (c >> 9)); 229 | current = (c&511) << 7; 230 | break; 231 | case 7: 232 | output += f(current | (c >> 8)); 233 | current = (c&255) << 8; 234 | break; 235 | case 8: 236 | output += f(current | (c >> 7)); 237 | current = (c&127) << 9; 238 | break; 239 | case 9: 240 | output += f(current | (c >> 6)); 241 | current = (c&63) << 10; 242 | break; 243 | case 10: 244 | output += f(current | (c >> 5)); 245 | current = (c&31) << 11; 246 | break; 247 | case 11: 248 | output += f(current | (c >> 4)); 249 | current = (c&15) << 12; 250 | break; 251 | case 12: 252 | output += f(current | (c >> 3)); 253 | current = (c&7) << 13; 254 | break; 255 | case 13: 256 | output += f(current | (c >> 2)); 257 | current = (c&3) << 14; 258 | break; 259 | case 14: 260 | output += f(current | (c >> 1)); 261 | current = (c&1) << 15; 262 | break; 263 | case 15: 264 | output += f(current | c); 265 | status=0; 266 | break; 267 | } 268 | 269 | 270 | i++; 271 | } 272 | 273 | return this.decompress(output); 274 | //return output; 275 | 276 | }, 277 | 278 | 279 | 280 | compress: function (uncompressed) { 281 | if (uncompressed == null) return ""; 282 | var i, value, 283 | context_dictionary= {}, 284 | context_dictionaryToCreate= {}, 285 | context_c="", 286 | context_wc="", 287 | context_w="", 288 | context_enlargeIn= 2, // Compensate for the first entry which should not count 289 | context_dictSize= 3, 290 | context_numBits= 2, 291 | context_data_string="", 292 | context_data_val=0, 293 | context_data_position=0, 294 | ii, 295 | f=this._f; 296 | 297 | for (ii = 0; ii < uncompressed.length; ii += 1) { 298 | context_c = uncompressed.charAt(ii); 299 | if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) { 300 | context_dictionary[context_c] = context_dictSize++; 301 | context_dictionaryToCreate[context_c] = true; 302 | } 303 | 304 | context_wc = context_w + context_c; 305 | if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) { 306 | context_w = context_wc; 307 | } else { 308 | if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { 309 | if (context_w.charCodeAt(0)<256) { 310 | for (i=0 ; i> 1; 331 | } 332 | } else { 333 | value = 1; 334 | for (i=0 ; i> 1; 356 | } 357 | } 358 | context_enlargeIn--; 359 | if (context_enlargeIn == 0) { 360 | context_enlargeIn = Math.pow(2, context_numBits); 361 | context_numBits++; 362 | } 363 | delete context_dictionaryToCreate[context_w]; 364 | } else { 365 | value = context_dictionary[context_w]; 366 | for (i=0 ; i> 1; 376 | } 377 | 378 | 379 | } 380 | context_enlargeIn--; 381 | if (context_enlargeIn == 0) { 382 | context_enlargeIn = Math.pow(2, context_numBits); 383 | context_numBits++; 384 | } 385 | // Add wc to the dictionary. 386 | context_dictionary[context_wc] = context_dictSize++; 387 | context_w = String(context_c); 388 | } 389 | } 390 | 391 | // Output the code for w. 392 | if (context_w !== "") { 393 | if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { 394 | if (context_w.charCodeAt(0)<256) { 395 | for (i=0 ; i> 1; 416 | } 417 | } else { 418 | value = 1; 419 | for (i=0 ; i> 1; 441 | } 442 | } 443 | context_enlargeIn--; 444 | if (context_enlargeIn == 0) { 445 | context_enlargeIn = Math.pow(2, context_numBits); 446 | context_numBits++; 447 | } 448 | delete context_dictionaryToCreate[context_w]; 449 | } else { 450 | value = context_dictionary[context_w]; 451 | for (i=0 ; i> 1; 461 | } 462 | 463 | 464 | } 465 | context_enlargeIn--; 466 | if (context_enlargeIn == 0) { 467 | context_enlargeIn = Math.pow(2, context_numBits); 468 | context_numBits++; 469 | } 470 | } 471 | 472 | // Mark the end of the stream 473 | value = 2; 474 | for (i=0 ; i> 1; 484 | } 485 | 486 | // Flush the last char 487 | while (true) { 488 | context_data_val = (context_data_val << 1); 489 | if (context_data_position == 15) { 490 | context_data_string += f(context_data_val); 491 | break; 492 | } 493 | else context_data_position++; 494 | } 495 | return context_data_string; 496 | }, 497 | 498 | decompress: function (compressed) { 499 | if (compressed == null) return ""; 500 | var dictionary = [], 501 | next, 502 | enlargeIn = 4, 503 | dictSize = 4, 504 | numBits = 3, 505 | entry = "", 506 | result = "", 507 | i, 508 | w, 509 | bits, resb, maxpower, power, 510 | c, 511 | f = this._f, 512 | data = {string:compressed, val:compressed.charCodeAt(0), position:32768, index:1}; 513 | 514 | for (i = 0; i < 3; i += 1) { 515 | dictionary[i] = i; 516 | } 517 | 518 | bits = 0; 519 | maxpower = Math.pow(2,2); 520 | power=1; 521 | while (power!=maxpower) { 522 | resb = data.val & data.position; 523 | data.position >>= 1; 524 | if (data.position == 0) { 525 | data.position = 32768; 526 | data.val = data.string.charCodeAt(data.index++); 527 | } 528 | bits |= (resb>0 ? 1 : 0) * power; 529 | power <<= 1; 530 | } 531 | 532 | switch (next = bits) { 533 | case 0: 534 | bits = 0; 535 | maxpower = Math.pow(2,8); 536 | power=1; 537 | while (power!=maxpower) { 538 | resb = data.val & data.position; 539 | data.position >>= 1; 540 | if (data.position == 0) { 541 | data.position = 32768; 542 | data.val = data.string.charCodeAt(data.index++); 543 | } 544 | bits |= (resb>0 ? 1 : 0) * power; 545 | power <<= 1; 546 | } 547 | c = f(bits); 548 | break; 549 | case 1: 550 | bits = 0; 551 | maxpower = Math.pow(2,16); 552 | power=1; 553 | while (power!=maxpower) { 554 | resb = data.val & data.position; 555 | data.position >>= 1; 556 | if (data.position == 0) { 557 | data.position = 32768; 558 | data.val = data.string.charCodeAt(data.index++); 559 | } 560 | bits |= (resb>0 ? 1 : 0) * power; 561 | power <<= 1; 562 | } 563 | c = f(bits); 564 | break; 565 | case 2: 566 | return ""; 567 | } 568 | dictionary[3] = c; 569 | w = result = c; 570 | while (true) { 571 | bits = 0; 572 | maxpower = Math.pow(2,numBits); 573 | power=1; 574 | while (power!=maxpower) { 575 | resb = data.val & data.position; 576 | data.position >>= 1; 577 | if (data.position == 0) { 578 | data.position = 32768; 579 | data.val = data.string.charCodeAt(data.index++); 580 | } 581 | bits |= (resb>0 ? 1 : 0) * power; 582 | power <<= 1; 583 | } 584 | 585 | switch (c = bits) { 586 | case 0: 587 | bits = 0; 588 | maxpower = Math.pow(2,8); 589 | power=1; 590 | while (power!=maxpower) { 591 | resb = data.val & data.position; 592 | data.position >>= 1; 593 | if (data.position == 0) { 594 | data.position = 32768; 595 | data.val = data.string.charCodeAt(data.index++); 596 | } 597 | bits |= (resb>0 ? 1 : 0) * power; 598 | power <<= 1; 599 | } 600 | 601 | dictionary[dictSize++] = f(bits); 602 | c = dictSize-1; 603 | enlargeIn--; 604 | break; 605 | case 1: 606 | bits = 0; 607 | maxpower = Math.pow(2,16); 608 | power=1; 609 | while (power!=maxpower) { 610 | resb = data.val & data.position; 611 | data.position >>= 1; 612 | if (data.position == 0) { 613 | data.position = 32768; 614 | data.val = data.string.charCodeAt(data.index++); 615 | } 616 | bits |= (resb>0 ? 1 : 0) * power; 617 | power <<= 1; 618 | } 619 | dictionary[dictSize++] = f(bits); 620 | c = dictSize-1; 621 | enlargeIn--; 622 | break; 623 | case 2: 624 | return result; 625 | } 626 | 627 | if (enlargeIn == 0) { 628 | enlargeIn = Math.pow(2, numBits); 629 | numBits++; 630 | } 631 | 632 | if (dictionary[c]) { 633 | entry = dictionary[c]; 634 | } else { 635 | if (c === dictSize) { 636 | entry = w + w.charAt(0); 637 | } else { 638 | return null; 639 | } 640 | } 641 | result += entry; 642 | 643 | // Add w+entry[0] to the dictionary. 644 | dictionary[dictSize++] = w + entry.charAt(0); 645 | enlargeIn--; 646 | 647 | w = entry; 648 | 649 | if (enlargeIn == 0) { 650 | enlargeIn = Math.pow(2, numBits); 651 | numBits++; 652 | } 653 | 654 | } 655 | } 656 | }; 657 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | var ControlMode = { 2 | wiring: 0, 3 | selecting: 1, 4 | deleting: 2 5 | }; 6 | 7 | function LogicSim() 8 | { 9 | this.__proto__ = new Environment(); 10 | 11 | var myIsDragging = false; 12 | var myIsSelecting = false; 13 | var myCanDrag = false; 14 | 15 | var myIsWiring = false; 16 | var myWireStart = null; 17 | 18 | var myGridSize = 8; 19 | var myGridImage = null; 20 | 21 | var myDeleteBtn = null; 22 | var mySelectBtn = null; 23 | var myMoveBtn = null; 24 | 25 | var myCtrlDown = false; 26 | 27 | var mySelection = new Environment(); 28 | var myCanPlace = false; 29 | var myLastDragPos = null; 30 | 31 | var myCustoms = new Array(); 32 | 33 | this.canvas = null; 34 | this.context = null; 35 | 36 | this.toolbar = null; 37 | 38 | this.mouseX = 0; 39 | this.mouseY = 0; 40 | 41 | this.mosueDownPos = null; 42 | 43 | this.mode = ControlMode.wiring; 44 | 45 | this.initialize = function() 46 | { 47 | this.canvas = document.getElementById("canvas"); 48 | this.context = this.canvas.getContext("2d"); 49 | 50 | this.toolbar = new Toolbar(); 51 | var grp = this.toolbar.addGroup("Tools"); 52 | grp.addItem(new Button.Tool(images.newfile, function() { 53 | if (confirm("Are you sure you want to delete all existing gates, " 54 | + "wires and custom circuits?")) { 55 | logicSim.clear(); 56 | } 57 | })); 58 | grp.addItem(new Button.Tool(images.save, function() { 59 | Saving.save(); 60 | })); 61 | grp.addItem(new Button.Tool(images.open, function() { 62 | Saving.loadFromPrompt(); 63 | })); 64 | myDeleteBtn = grp.addItem(new Button.Tool(images.delete, function() { 65 | if (logicSim.mode == ControlMode.deleting) 66 | logicSim.setMode(ControlMode.wiring); 67 | else 68 | logicSim.setMode(ControlMode.deleting); 69 | })); 70 | mySelectBtn = grp.addItem(new Button.Tool(images.select, function() { 71 | if (logicSim.mode == ControlMode.wiring) 72 | logicSim.setMode(ControlMode.wiring); 73 | else 74 | logicSim.setMode(ControlMode.selecting); 75 | })); 76 | grp.addItem(new Button.Tool(images.newic, function() { 77 | if (logicSim.getOutputs().length == 0) { 78 | alert("At least one output required to create an integrated circuit."); 79 | return; 80 | } 81 | 82 | var name = prompt("Please enter a name for the new integrated circuit.", ""); 83 | if (name == null) return; 84 | 85 | logicSim.customGroup.addItem(new CustomIC(name, logicSim.clone())); 86 | })); 87 | 88 | grp = this.toolbar.addGroup("Logic Gates"); 89 | grp.addItem(new BufferGate()); 90 | grp.addItem(new AndGate()); 91 | grp.addItem(new OrGate()); 92 | grp.addItem(new XorGate()); 93 | grp.addItem(new NotGate()); 94 | grp.addItem(new NandGate()); 95 | grp.addItem(new NorGate()); 96 | grp.addItem(new XnorGate()); 97 | 98 | grp = this.toolbar.addGroup("Input"); 99 | grp.addItem(new ConstInput()); 100 | grp.addItem(new ClockInput()); 101 | grp.addItem(new ToggleSwitch()); 102 | grp.addItem(new PushSwitchA()); 103 | grp.addItem(new PushSwitchB()); 104 | grp.addItem(new ICInput()); 105 | 106 | grp = this.toolbar.addGroup("Output"); 107 | grp.addItem(new OutputDisplay()); 108 | grp.addItem(new SevenSegDisplay()); 109 | grp.addItem(new ICOutput()); 110 | 111 | grp = this.toolbar.addGroup("Flip Flops", true); 112 | grp.addItem(new DFlipFlop()); 113 | 114 | grp = this.toolbar.addGroup("Integrated Circuits", true); 115 | grp.addItem(new Encoder()); 116 | grp.addItem(new Decoder()); 117 | grp.addItem(new SevenSegDecoder()); 118 | 119 | this.customGroup = this.toolbar.addGroup("Custom Circuits"); 120 | 121 | this.setGridSize(16); 122 | this.onResizeCanvas(); 123 | 124 | Saving.loadFromHash(); 125 | } 126 | 127 | this.startDragging = function(gateType) 128 | { 129 | mySelection.clear(); 130 | 131 | if (gateType != null) { 132 | this.deselectAll(); 133 | 134 | var gate = new Gate(gateType, 0, 0); 135 | gate.selected = true; 136 | 137 | mySelection.placeGate(gate); 138 | } else { 139 | var pos = this.mouseDownPos; 140 | 141 | for (var i = this.gates.length - 1; i >= 0; i--) { 142 | var gate = this.gates[i]; 143 | if (!gate.selected) continue; 144 | 145 | if (myCtrlDown) { 146 | gate.selected = false; 147 | var data = gate.saveData(); 148 | gate = new Gate(gate.type, gate.x, gate.y); 149 | gate.loadData(data); 150 | gate.selected = true; 151 | } else { 152 | this.removeGate(gate); 153 | } 154 | 155 | gate.x -= pos.x; 156 | gate.y -= pos.y; 157 | 158 | mySelection.placeGate(gate); 159 | } 160 | 161 | var wires = this.getAllWires(); 162 | var toRemove = new Array(); 163 | for (var i = 0; i < wires.length; ++ i) { 164 | var wire = wires[i]; 165 | if (!wire.selected) continue; 166 | 167 | if (myCtrlDown) { 168 | wire.selected = false; 169 | } else { 170 | toRemove.push(wire); 171 | } 172 | 173 | mySelection.placeWire(wire.start.sub(pos), wire.end.sub(pos), true); 174 | } 175 | 176 | if (!myCtrlDown) { 177 | this.removeWires(toRemove); 178 | } 179 | } 180 | 181 | myIsDragging = true; 182 | } 183 | 184 | this.getDraggedPosition = function() 185 | { 186 | var snap = myGridSize / 2; 187 | 188 | for (var i = this.gates.length - 1; i >= 0; i--) { 189 | var gate = this.gates[i]; 190 | if (gate.selected) { 191 | snap = myGridSize; 192 | break; 193 | } 194 | } 195 | 196 | if (mySelection.gates.length > 0) { 197 | snap = myGridSize; 198 | } 199 | 200 | return new Pos( 201 | Math.round(this.mouseX / snap) * snap, 202 | Math.round(this.mouseY / snap) * snap 203 | ); 204 | } 205 | 206 | this.getSelectedRect = function() 207 | { 208 | var start = new Pos(this.mouseDownPos.x, this.mouseDownPos.y); 209 | var end = this.getDraggedPosition(); 210 | 211 | if (end.x < start.x) { 212 | var temp = end.x; 213 | end.x = start.x; 214 | start.x = temp; 215 | } 216 | 217 | if (end.y < start.y) { 218 | var temp = end.y; 219 | end.y = start.y; 220 | start.y = temp; 221 | } 222 | 223 | return new Rect(start.x, start.y, end.x - start.x, end.y - start.y); 224 | } 225 | 226 | this.stopDragging = function() 227 | { 228 | myIsDragging = false; 229 | 230 | if (this.getDraggedPosition().x >= 256) { 231 | if (myCanPlace) { 232 | this.tryMerge(mySelection, this.getDraggedPosition(), true); 233 | } else { 234 | this.tryMerge(mySelection, this.mouseDownPos, true); 235 | } 236 | } 237 | 238 | mySelection.clear(); 239 | } 240 | 241 | this.setMode = function(mode) 242 | { 243 | if (mode == ControlMode.deleting) { 244 | var deleted = false; 245 | for (var i = this.gates.length - 1; i >= 0; i--) { 246 | var gate = this.gates[i]; 247 | if (gate.selected) { 248 | deleted = true; 249 | this.removeGate(gate); 250 | } 251 | } 252 | 253 | var wires = this.getAllWires(); 254 | var toRemove = new Array(); 255 | for (var i = wires.length - 1; i >= 0; i--) { 256 | var wire = wires[i]; 257 | if (wire.selected) { 258 | deleted = true; 259 | toRemove.push(wire); 260 | } 261 | } 262 | this.removeWires(toRemove); 263 | 264 | if (deleted) mode = ControlMode.wiring; 265 | } 266 | 267 | this.mode = mode; 268 | 269 | myDeleteBtn.selected = mode == ControlMode.deleting; 270 | mySelectBtn.selected = mode == ControlMode.selecting; 271 | } 272 | 273 | this.startWiring = function(x, y) 274 | { 275 | var snap = myGridSize / 2; 276 | 277 | myIsWiring = true; 278 | myWireStart = new Pos( 279 | Math.round(x / snap) * snap, 280 | Math.round(y / snap) * snap 281 | ); 282 | } 283 | 284 | this.stopWiring = function(x, y) 285 | { 286 | if (this.canPlaceWire(new Wire(myWireStart, this.getWireEnd()))) { 287 | this.deselectAll(); 288 | this.placeWire(myWireStart, this.getWireEnd()); 289 | } 290 | 291 | myIsWiring = false; 292 | } 293 | 294 | this.getWireEnd = function() 295 | { 296 | var snap = 8; 297 | 298 | var pos = new Pos( 299 | Math.round(this.mouseX / snap) * snap, 300 | Math.round(this.mouseY / snap) * snap 301 | ); 302 | 303 | var diff = pos.sub(myWireStart); 304 | 305 | if (Math.abs(diff.x) >= Math.abs(diff.y)) { 306 | pos.y = myWireStart.y; 307 | } else { 308 | pos.x = myWireStart.x; 309 | } 310 | 311 | return pos; 312 | } 313 | 314 | this.mouseMove = function(x, y, e) 315 | { 316 | this.mouseX = x; 317 | this.mouseY = y; 318 | 319 | myCtrlDown = e.ctrlKey; 320 | 321 | if (this.toolbar == null) return; 322 | 323 | if (e.shiftKey) this.setMode(ControlMode.selecting); 324 | else if (this.mode == ControlMode.selecting) this.setMode(ControlMode.wiring); 325 | 326 | this.toolbar.mouseMove(x, y); 327 | 328 | if (!myIsDragging && !myIsSelecting && myCanDrag && this.mouseDownPos != null) { 329 | var diff = new Pos(x, y).sub(this.mouseDownPos); 330 | if (Math.abs(diff.x) >= 8 || Math.abs(diff.y) >= 8) 331 | this.startDragging(); 332 | } else if (myIsDragging) { 333 | var pos = this.getDraggedPosition(); 334 | 335 | if (myLastDragPos == null || !pos.equals(myLastDragPos)) { 336 | var env = this.clone(); 337 | myCanPlace = env.tryMerge(mySelection, pos, false, true); 338 | myLastDragPos = pos; 339 | } 340 | } 341 | } 342 | 343 | this.mouseDown = function(x, y, e) 344 | { 345 | this.mouseX = x; 346 | this.mouseY = y; 347 | 348 | myCtrlDown = e.ctrlKey; 349 | 350 | if (this.toolbar == null) return; 351 | 352 | if (e.shiftKey) this.setMode(ControlMode.selecting); 353 | else if (this.mode == ControlMode.selecting) this.setMode(ControlMode.wiring); 354 | 355 | this.mouseDownPos = this.getDraggedPosition(); 356 | 357 | myCanDrag = false; 358 | 359 | var canSelect = this.mode == ControlMode.selecting; 360 | 361 | if (x < 256) { 362 | this.toolbar.mouseDown(x, y); 363 | } else { 364 | var pos = new Pos(x, y); 365 | 366 | for (var i = 0; i < this.gates.length; ++ i) { 367 | var gate = this.gates[i]; 368 | var rect = new Rect(gate.x + 8, gate.y + 8, gate.width - 16, gate.height - 16); 369 | 370 | if (rect.contains(pos)) { 371 | gate.mouseDown(); 372 | if (this.mode == ControlMode.selecting) { 373 | gate.selected = !gate.selected; 374 | canSelect = false; 375 | } else if (this.mode == ControlMode.wiring) { 376 | if (!gate.selected) { 377 | this.deselectAll(); 378 | gate.selected = true; 379 | } else { 380 | myCanDrag = true; 381 | } 382 | return; 383 | } 384 | } 385 | } 386 | 387 | var gsize = myGridSize / 2; 388 | pos.x = Math.round(pos.x / gsize) * gsize; 389 | pos.y = Math.round(pos.y / gsize) * gsize; 390 | 391 | for (var i = 0; i < this.wireGroups.length; ++ i) { 392 | var group = this.wireGroups[i]; 393 | if (group.crossesPos(pos)) { 394 | var wire = group.getWireAt(pos); 395 | 396 | if (this.mode == ControlMode.selecting) { 397 | wire.selected = !wire.selected; 398 | canSelect = false; 399 | } else if (this.mode == ControlMode.wiring) { 400 | if (!wire.selected) { 401 | this.deselectAll(); 402 | wire.selected = true; 403 | } else { 404 | myCanDrag = true; 405 | return; 406 | } 407 | } 408 | } 409 | } 410 | 411 | if (canSelect) { 412 | myIsSelecting = true; 413 | } else if (this.mode == ControlMode.wiring) { 414 | this.startWiring(x, y); 415 | } 416 | } 417 | } 418 | 419 | this.mouseUp = function(x, y, e) 420 | { 421 | this.mouseX = x; 422 | this.mouseY = y; 423 | 424 | myCtrlDown = e.ctrlKey; 425 | 426 | if (this.toolbar == null) return; 427 | 428 | if (e.shiftKey) this.setMode(ControlMode.selecting); 429 | else if (this.mode == ControlMode.selecting) this.setMode(ControlMode.wiring); 430 | 431 | if (myIsDragging) { 432 | this.stopDragging(); 433 | } else if (myIsWiring) { 434 | this.stopWiring(); 435 | } else if (myIsSelecting) { 436 | myIsSelecting = false; 437 | 438 | var rect = this.getSelectedRect(); 439 | 440 | for (var i = 0; i < this.gates.length; ++ i) { 441 | var gate = this.gates[i]; 442 | 443 | if (gate.getRect().intersects(rect)) { 444 | gate.selected = true; 445 | } 446 | } 447 | 448 | var wires = this.getAllWires(); 449 | for (var i = 0; i < wires.length; ++ i) { 450 | var wire = wires[i]; 451 | 452 | if (rect.intersectsWire(wire)) { 453 | wire.selected = true; 454 | } 455 | } 456 | } else if (x < 256) { 457 | this.toolbar.mouseUp(x, y); 458 | } else { 459 | var pos = new Pos(x, y); 460 | 461 | var deleted = false; 462 | 463 | for (var i = 0; i < this.gates.length; ++ i) { 464 | var gate = this.gates[i]; 465 | 466 | if (gate.isMouseDown) { 467 | var rect = new Rect(gate.x + 8, gate.y + 8, gate.width - 16, gate.height - 16); 468 | 469 | if (rect.contains(pos)) { 470 | if (this.mode == ControlMode.deleting && !deleted) { 471 | this.removeGate(gate); 472 | deleted = true; 473 | } else { 474 | gate.click(); 475 | } 476 | } 477 | 478 | gate.mouseUp(); 479 | } 480 | } 481 | 482 | if (this.mode == ControlMode.deleting && !deleted) { 483 | var gsize = 8; 484 | pos.x = Math.round(pos.x / gsize) * gsize; 485 | pos.y = Math.round(pos.y / gsize) * gsize; 486 | 487 | if (this.mouseDownPos.equals(pos)) { 488 | for (var i = 0; i < this.wireGroups.length; ++ i) { 489 | var group = this.wireGroups[i]; 490 | if (group.crossesPos(pos)) { 491 | var wire = group.getWireAt(pos); 492 | this.removeWire(wire); 493 | break; 494 | } 495 | } 496 | } 497 | } 498 | } 499 | 500 | this.mouseDownPos = null; 501 | } 502 | 503 | this.click = function(x, y, e) 504 | { 505 | this.mouseX = x; 506 | this.mouseY = y; 507 | 508 | myCtrlDown = e.ctrlKey; 509 | 510 | if (e.shiftKey) this.setMode(ControlMode.selecting); 511 | else if (this.mode == ControlMode.selecting) this.setMode(ControlMode.wiring); 512 | 513 | if (x < 256) { 514 | this.toolbar.click(x, y); 515 | } 516 | } 517 | 518 | this.keyDown = function(e) 519 | { 520 | if (e.keyCode == 46) this.setMode(ControlMode.deleting); 521 | if (e.keyCode == 16) this.setMode(ControlMode.selecting); 522 | if (e.keyCode == 17) myCtrlDown = true; 523 | 524 | if (e.keyCode == 83 && e.ctrlKey) { 525 | Saving.save(); 526 | e.preventDefault(); 527 | } 528 | 529 | if (e.keyCode == 79 && e.ctrlKey) { 530 | Saving.loadFromPrompt(); 531 | e.preventDefault(); 532 | } 533 | } 534 | 535 | this.keyUp = function(e) 536 | { 537 | if ((e.keyCode == 46 && this.mode == ControlMode.deleting) 538 | || (e.keyCode == 16 && this.mode == ControlMode.selecting)) 539 | this.setMode(ControlMode.wiring); 540 | 541 | if (e.keyCode == 17) myCtrlDown = false; 542 | } 543 | 544 | this.getGridSize = function() 545 | { 546 | return myGridSize; 547 | } 548 | 549 | this.setGridSize = function(size) 550 | { 551 | myGridSize = size; 552 | myGridImage = document.createElement("canvas"); 553 | myGridImage.width = myGridSize * 2; 554 | myGridImage.height = myGridSize * 2; 555 | 556 | var context = myGridImage.getContext("2d"); 557 | 558 | context.fillStyle = "#CCCCCC"; 559 | context.fillRect(0, 0, myGridSize * 2, myGridSize * 2); 560 | context.fillStyle = "#DDDDDD"; 561 | context.fillRect(0, 0, myGridSize, myGridSize); 562 | context.fillRect(myGridSize, myGridSize, myGridSize, myGridSize); 563 | } 564 | 565 | this.onResizeCanvas = function() 566 | { 567 | this.canvas.width = window.innerWidth; 568 | this.canvas.height = window.innerHeight; 569 | } 570 | 571 | this.run = function() 572 | { 573 | setInterval(this.mainLoop, 1000.0 / 60.0, this); 574 | } 575 | 576 | this.mainLoop = function(self) 577 | { 578 | for (var i = 0; i < 4; ++ i) { 579 | self.step(); 580 | } 581 | 582 | self.context.fillStyle = self.context.createPattern(myGridImage, "repeat"); 583 | self.context.fillRect(256, 0, self.canvas.width - 256, self.canvas.height); 584 | 585 | self.render(self.context); 586 | 587 | if (myIsDragging) { 588 | var pos = self.getDraggedPosition(); 589 | mySelection.render(self.context, pos, myCanPlace ? "#6666ff" : "#ff6666"); 590 | } else if (myIsWiring) { 591 | var end = self.getWireEnd(); 592 | 593 | self.context.strokeStyle = self.canPlaceWire(new Wire(myWireStart, self.getWireEnd())) 594 | ? "#009900" : "#990000"; 595 | self.context.lineWidth = 2; 596 | self.context.beginPath(); 597 | self.context.moveTo(myWireStart.x, myWireStart.y); 598 | self.context.lineTo(end.x, end.y); 599 | self.context.stroke(); 600 | self.context.closePath(); 601 | } else if (myIsSelecting) { 602 | var rect = self.getSelectedRect(); 603 | 604 | self.context.beginPath(); 605 | self.context.rect(rect.x - 1, rect.y - 1, 606 | rect.width + 2, rect.height + 2); 607 | self.context.globalAlpha = 0.25; 608 | self.context.fillStyle = "#3333ff"; 609 | self.context.fill(); 610 | self.context.globalAlpha = 0.5; 611 | self.context.strokeStyle = "#6666ff"; 612 | self.context.stroke(); 613 | self.context.closePath(); 614 | self.context.globalAlpha = 1.0; 615 | } 616 | 617 | self.toolbar.render(self.context); 618 | } 619 | } 620 | 621 | logicSim = new LogicSim(); 622 | 623 | window.onload = function(e) 624 | { 625 | if (!images.allImagesLoaded()) { 626 | images.onAllLoaded = function() 627 | { 628 | logicSim.initialize(); 629 | logicSim.run(); 630 | } 631 | } else { 632 | logicSim.initialize(); 633 | logicSim.run(); 634 | } 635 | } 636 | 637 | window.onmousemove = function(e) 638 | { 639 | if (e) { 640 | logicSim.mouseMove(e.pageX, e.pageY, e); 641 | } else { 642 | logicSim.mouseMove(window.event.clientX, window.event.clientY, window.event); 643 | } 644 | } 645 | 646 | window.onmousedown = function(e) 647 | { 648 | if (e) { 649 | logicSim.mouseDown(e.pageX, e.pageY, e); 650 | } else { 651 | logicSim.mouseDown(window.event.clientX, window.event.clientY, window.event); 652 | } 653 | } 654 | 655 | window.onmouseup = function(e) 656 | { 657 | if (e) { 658 | logicSim.mouseUp(e.pageX, e.pageY, e); 659 | } else { 660 | logicSim.mouseUp(window.event.clientX, window.event.clientY, window.event); 661 | } 662 | } 663 | 664 | window.onclick = function(e) 665 | { 666 | if (e) { 667 | logicSim.click(e.pageX, e.pageY, e); 668 | } else { 669 | logicSim.click(window.event.clientX, window.event.clientY, window.event); 670 | } 671 | } 672 | 673 | window.onkeydown = function(e) 674 | { 675 | logicSim.keyDown(e); 676 | } 677 | 678 | window.onkeyup = function(e) 679 | { 680 | logicSim.keyUp(e); 681 | } 682 | 683 | function onResizeCanvas() 684 | { 685 | logicSim.onResizeCanvas(); 686 | } 687 | -------------------------------------------------------------------------------- /scripts/saving.js: -------------------------------------------------------------------------------- 1 | Saving = new Object(); 2 | Saving.save = function() 3 | { 4 | var obj = { ics: [], root: logicSim.save() }; 5 | 6 | for (var i = 0; i < logicSim.customGroup.items.length; ++i) { 7 | var ic = logicSim.customGroup.items[i]; 8 | obj.ics.push({ name: ic.name, env: ic.environment.save() }); 9 | } 10 | 11 | var str = LZString.compressToBase64(JSON.stringify(obj)); 12 | 13 | window.open("data:text/plain;charset=UTF-8," + str, "_blank"); 14 | } 15 | 16 | Saving.loadFromHash = function() 17 | { 18 | if (window.location.hash === null || window.location.hash.length <= 1) return; 19 | Saving.load(window.location.hash.substring(1)); 20 | } 21 | 22 | Saving.loadFromPrompt = function() 23 | { 24 | var str = prompt("Paste a previously copied save code with Ctrl+V.", ""); 25 | if (str != null && str.length > 0) Saving.load(str); 26 | } 27 | 28 | Saving.load = function(str) 29 | { 30 | var obj = JSON.parse(LZString.decompressFromBase64(str)); 31 | 32 | var ics = new Array(); 33 | for (var i = 0; i < obj.ics.length; ++ i) { 34 | var ic = obj.ics[i]; 35 | var env = new Environment(); 36 | env.load(ic.env, ics); 37 | ics[i] = new CustomIC(ic.name, env); 38 | logicSim.customGroup.addItem(ics[i]); 39 | } 40 | 41 | logicSim.load(obj.root, ics); 42 | } 43 | -------------------------------------------------------------------------------- /scripts/toolbar.js: -------------------------------------------------------------------------------- 1 | function Toolbar() 2 | { 3 | this.sepimage = new Object(); 4 | this.sepimage.end = images.sepend; 5 | this.sepimage.mid = images.sepmid; 6 | 7 | this.arrimage = new Object(); 8 | this.arrimage.down = images.arrdown; 9 | this.arrimage.up = images.arrup; 10 | 11 | this.width = 256; 12 | this.isOpen = true; 13 | 14 | this.groups = new Array(); 15 | 16 | this.addGroup = function(name, hide) 17 | { 18 | var group = new ToolbarGroup(this, name); 19 | this.groups.push(group); 20 | 21 | if (hide) group.isOpen = false; 22 | 23 | return group; 24 | } 25 | 26 | this.render = function(context) 27 | { 28 | context.fillStyle = "#FFFFFF"; 29 | context.fillRect(0, 0, this.width, window.innerHeight); 30 | 31 | var yPos = 0; 32 | for (var i = 0; i < this.groups.length; ++ i) 33 | { 34 | this.groups[i].y = yPos; 35 | yPos += this.groups[i].render(context); 36 | } 37 | 38 | context.fillStyle = "#000000"; 39 | context.fillRect(this.width - 1, 0, 1, window.innerHeight); 40 | } 41 | 42 | this.mouseMove = function(x, y) 43 | { 44 | for (var i = 0; i < this.groups.length; ++ i) 45 | this.groups[i].mouseMove(x, y); 46 | } 47 | 48 | this.mouseDown = function(x, y) 49 | { 50 | var yPos = 0; 51 | for (var i = 0; i < this.groups.length; ++ i) 52 | { 53 | var height = this.groups[i].getInnerHeight() + 24; 54 | 55 | if (y < yPos + height) 56 | { 57 | this.groups[i].mouseDown(x, y); 58 | break; 59 | } 60 | 61 | yPos += height; 62 | } 63 | } 64 | 65 | this.mouseUp = function(x, y) 66 | { 67 | var yPos = 0; 68 | for (var i = 0; i < this.groups.length; ++ i) 69 | { 70 | var height = this.groups[i].getInnerHeight() + 24; 71 | 72 | if (y < yPos + height) 73 | { 74 | this.groups[i].mouseUp(x, y); 75 | break; 76 | } 77 | 78 | yPos += height; 79 | } 80 | } 81 | 82 | this.click = function(x, y) 83 | { 84 | var yPos = 0; 85 | for (var i = 0; i < this.groups.length; ++ i) 86 | { 87 | var height = this.groups[i].getInnerHeight() + 24; 88 | 89 | if (y < yPos + height) 90 | { 91 | this.groups[i].click(x, y); 92 | break; 93 | } 94 | 95 | yPos += height; 96 | } 97 | } 98 | } 99 | 100 | function ToolbarGroup(toolbar, name) 101 | { 102 | this.toolbar = toolbar; 103 | 104 | this.name = name; 105 | this.items = new Array(); 106 | this.buttons = new Array(); 107 | 108 | this.padding = 16; 109 | this.minItemWidth = 80; 110 | 111 | this.y = 0; 112 | 113 | this.isOpen = true; 114 | this.curDelta = 1.0; 115 | 116 | var self = this; 117 | 118 | this.openButton = new Button.Small(0, 0, 24); 119 | this.openButton.mouseDown = function(mouseX, mouseY) 120 | { 121 | if (self.items.length != 0) 122 | { 123 | if (self.isOpen) 124 | self.close(); 125 | else 126 | self.open(); 127 | } 128 | } 129 | this.buttons.push(this.openButton); 130 | 131 | this.getItemsPerRow = function() 132 | { 133 | return Math.max(Math.floor(this.toolbar.width / this.minItemWidth), 1); 134 | } 135 | 136 | this.getItemWidth = function() 137 | { 138 | return this.toolbar.width / this.getItemsPerRow(); 139 | } 140 | 141 | this.getRowCount = function() 142 | { 143 | return Math.ceil(this.items.length / this.getItemsPerRow()); 144 | } 145 | 146 | this.getRowHeight = function(row) 147 | { 148 | var start = this.getItemsPerRow() * row; 149 | var end = start + this.getItemsPerRow(); 150 | 151 | var height = 0; 152 | 153 | for (var i = start; i < Math.min(end, this.items.length); ++i) 154 | height = Math.max(height, this.items[i].height); 155 | 156 | return height + this.padding; 157 | } 158 | 159 | this.getRowOffset = function(row) 160 | { 161 | row = Math.min(row, this.getRowCount()); 162 | 163 | var height = 0; 164 | for (var i = 0; i < row; ++ i) 165 | height += this.getRowHeight(i); 166 | 167 | return height; 168 | } 169 | 170 | this.getInnerHeight = function() 171 | { 172 | var openSize = this.getRowOffset(this.getRowCount()); 173 | 174 | this.curDelta += Math.max((1.0 - this.curDelta) / 4.0, 1.0 / openSize); 175 | 176 | if (this.curDelta > 1.0) 177 | this.curDelta = 1.0; 178 | 179 | var delta = this.curDelta; 180 | 181 | if (!this.isOpen) 182 | delta = 1.0 - delta; 183 | 184 | return Math.round(openSize * delta); 185 | } 186 | 187 | this.addItem = function(item) 188 | { 189 | this.items.push(item); 190 | return item; 191 | } 192 | 193 | this.addButton = function(width, contents, mouseDown) 194 | { 195 | var btn = new Button.Small(0, 0, width, contents); 196 | btn.mouseDown = mouseDown; 197 | this.buttons.push(btn); 198 | } 199 | 200 | this.open = function() 201 | { 202 | if (!this.isOpen) 203 | { 204 | this.curDelta = 0.0; 205 | this.isOpen = true; 206 | } 207 | } 208 | 209 | this.close = function() 210 | { 211 | if (this.isOpen) 212 | { 213 | this.curDelta = 0.0; 214 | this.isOpen = false; 215 | } 216 | } 217 | 218 | this.mouseMove = function(x, y) 219 | { 220 | for (var i = this.buttons.length - 1; i >= 0; i--) 221 | this.buttons[i].mouseMove(x, y); 222 | } 223 | 224 | this.mouseDown = function(x, y) 225 | { 226 | if (y <= this.y + 24) { 227 | for (var i = this.buttons.length - 1; i >= 0; i--) 228 | { 229 | var btn = this.buttons[i]; 230 | if (btn == this.openButton && this.items.length == 0) continue; 231 | if (btn.isPositionOver(x, y)) 232 | { 233 | btn.mouseDown(x, y) 234 | break; 235 | } 236 | } 237 | } else { 238 | var ipr = this.getItemsPerRow(); 239 | var wid = this.getItemWidth(); 240 | for (var i = 0; i < this.items.length; ++i) 241 | { 242 | var item = this.items[i]; 243 | var row = Math.floor(i / ipr); 244 | var imgX = (i % ipr) * wid + (wid - item.width) / 2; 245 | var imgY = this.getRowOffset(row) + this.y + 24 246 | + (this.getRowHeight(row) - item.height) / 2; 247 | 248 | if (x >= imgX && y >= imgY && x < imgX + item.width && y < imgY + item.height) 249 | { 250 | if (item.isGateType) 251 | logicSim.startDragging(item); 252 | else 253 | item.mouseDown(x, y); 254 | 255 | break; 256 | } 257 | } 258 | } 259 | } 260 | 261 | this.mouseUp = function(x, y) 262 | { 263 | 264 | } 265 | 266 | this.click = function(x, y) 267 | { 268 | 269 | } 270 | 271 | this.render = function(context) 272 | { 273 | context.translate(0, this.y); 274 | 275 | context.fillStyle = context.createPattern(this.toolbar.sepimage.mid, "repeat-x"); 276 | context.fillRect(1, 0, this.toolbar.width - 2, 24); 277 | 278 | context.drawImage(this.toolbar.sepimage.end, 0, 0); 279 | context.drawImage(this.toolbar.sepimage.end, this.toolbar.width - 2, 0); 280 | 281 | context.translate(0, -this.y); 282 | 283 | this.openButton.image = this.isOpen ? this.toolbar.arrimage.up : this.toolbar.arrimage.down; 284 | 285 | var btnx = this.toolbar.width; 286 | for (var i = 0; i < this.buttons.length; ++i) 287 | { 288 | var btn = this.buttons[i]; 289 | if (btn == this.openButton && this.items.length == 0) continue; 290 | btn.y = this.y + 4; 291 | btnx -= btn.width + 4; 292 | btn.x = btnx; 293 | btn.render(context); 294 | } 295 | 296 | context.translate(0, this.y); 297 | 298 | context.fillStyle = "#FFFFFF"; 299 | context.font = "bold 12px sans-serif"; 300 | context.shadowOffsetX = 0; 301 | context.shadowOffsetY = -1; 302 | context.shadowColor = "#000000"; 303 | context.fillText(this.name, 4, 16, this.toolbar.width - 8); 304 | 305 | context.shadowOffsetX = 0; 306 | context.shadowOffsetY = 0; 307 | 308 | context.translate(0, -this.y); 309 | 310 | if (this.isOpen && this.curDelta > 0.9) 311 | { 312 | var ipr = this.getItemsPerRow(); 313 | var wid = this.getItemWidth(); 314 | for (var i = 0; i < this.items.length; ++i) 315 | { 316 | var item = this.items[i]; 317 | var row = Math.floor(i / ipr); 318 | var imgX = (i % ipr) * wid + (wid - item.width) / 2; 319 | var imgY = this.getRowOffset(row) + this.y + 24 320 | + (this.getRowHeight(row) - item.height) / 2; 321 | 322 | if (item.isGateType) 323 | item.render(context, Math.round(imgX), imgY); 324 | else 325 | { 326 | item.x = Math.round(imgX); 327 | item.y = imgY; 328 | item.render(context); 329 | } 330 | } 331 | } 332 | 333 | return 24 + this.getInnerHeight(); 334 | } 335 | } -------------------------------------------------------------------------------- /scripts/tools.js: -------------------------------------------------------------------------------- 1 | Array.prototype.contains = function(obj) 2 | { 3 | var i = this.length; 4 | while (--i >= 0) 5 | if (this[i] === obj) 6 | return true; 7 | 8 | return false; 9 | } 10 | 11 | Array.prototype.pushMany = function(arr) 12 | { 13 | for (var i = 0; i < arr.length; ++i) { 14 | this.push(arr[i]); 15 | } 16 | } 17 | 18 | Array.prototype.containsEqual = function(obj) 19 | { 20 | var i = this.length; 21 | while (--i >= 0) 22 | if (this[i].equals(obj)) 23 | return true; 24 | 25 | return false; 26 | } 27 | 28 | var images = new Object(); 29 | images.myToLoadCount = 0; 30 | images.onAllLoaded = function() {} 31 | 32 | images.onImageLoad = function() 33 | { 34 | --images.myToLoadCount; 35 | 36 | if(images.myToLoadCount == 0) 37 | images.onAllLoaded(); 38 | } 39 | 40 | images.load = function(path) 41 | { 42 | ++images.myToLoadCount; 43 | var img = new Image(); 44 | img.src = "images/" + path; 45 | 46 | img.onload = images.onImageLoad; 47 | 48 | images[path.substring(0, path.length - 4)] = img; 49 | return img; 50 | } 51 | 52 | images.allImagesLoaded = function() 53 | { 54 | return (images.myToLoadCount == 0); 55 | } 56 | 57 | images.load("and.png"); 58 | images.load("arrdown.png"); 59 | images.load("arrup.png"); 60 | images.load("btnsmallleft.png"); 61 | images.load("btnsmallleftover.png"); 62 | images.load("btnsmallmid.png"); 63 | images.load("btnsmallmidover.png"); 64 | images.load("btnsmallright.png"); 65 | images.load("btnsmallrightover.png"); 66 | images.load("buffer.png"); 67 | images.load("clock.png"); 68 | images.load("constoff.png"); 69 | images.load("conston.png"); 70 | images.load("decoder.png"); 71 | images.load("delete.png"); 72 | images.load("delic.png"); 73 | images.load("dflipflop.png"); 74 | images.load("encoder.png"); 75 | images.load("input.png"); 76 | images.load("move.png"); 77 | images.load("nand.png"); 78 | images.load("newfile.png"); 79 | images.load("newic.png"); 80 | images.load("nor.png"); 81 | images.load("not.png"); 82 | images.load("open.png"); 83 | images.load("or.png"); 84 | images.load("outoff.png"); 85 | images.load("outon.png"); 86 | images.load("output.png"); 87 | images.load("pushswitchaclosed.png"); 88 | images.load("pushswitchaopen.png"); 89 | images.load("pushswitchbclosed.png"); 90 | images.load("pushswitchbopen.png"); 91 | images.load("save.png"); 92 | images.load("select.png"); 93 | images.load("sepend.png"); 94 | images.load("sepmid.png"); 95 | images.load("sevsega.png"); 96 | images.load("sevsegb.png"); 97 | images.load("sevsegbase.png"); 98 | images.load("sevsegc.png"); 99 | images.load("sevsegd.png"); 100 | images.load("sevsegdecoder.png"); 101 | images.load("sevsegdp.png"); 102 | images.load("sevsege.png"); 103 | images.load("sevsegf.png"); 104 | images.load("sevsegg.png"); 105 | images.load("switchclosed.png"); 106 | images.load("switchopen.png"); 107 | images.load("xnor.png"); 108 | images.load("xor.png"); 109 | 110 | function Rect(x, y, width, height) 111 | { 112 | this.x = x; 113 | this.y = y; 114 | 115 | this.width = width; 116 | this.height = height; 117 | 118 | this.left = x; 119 | this.top = y; 120 | this.right = x + width; 121 | this.bottom = y + height; 122 | 123 | this.setLeft = function(value) 124 | { 125 | this.left = value; 126 | this.x = value; 127 | this.width = this.right - value; 128 | } 129 | 130 | this.setTop = function(value) 131 | { 132 | this.top = value; 133 | this.y = value; 134 | this.height = this.bottom - value; 135 | } 136 | 137 | this.setRight = function(value) 138 | { 139 | this.right = value; 140 | this.width = value - this.left; 141 | } 142 | 143 | this.setBottom = function(value) 144 | { 145 | this.bottom = value; 146 | this.height = value - this.top; 147 | } 148 | 149 | this.intersects = function(rect) 150 | { 151 | return this.left < rect.right && rect.left < this.right 152 | && this.top < rect.bottom && rect.top < this.bottom; 153 | } 154 | 155 | this.intersectsWire = function(wire, ends) 156 | { 157 | if (ends) { 158 | return wire.start.x <= this.right && wire.end.x >= this.left 159 | && wire.start.y <= this.bottom && wire.end.y >= this.top; 160 | } 161 | 162 | if (wire.isHorizontal()) { 163 | return wire.start.x < this.right && wire.end.x > this.left 164 | && wire.start.y <= this.bottom && wire.end.y >= this.top; 165 | } else { 166 | return wire.start.x <= this.right && wire.end.x >= this.left 167 | && wire.start.y < this.bottom && wire.end.y > this.top; 168 | } 169 | } 170 | 171 | this.contains = function(pos) 172 | { 173 | return pos.x >= this.left && pos.x <= this.right 174 | && pos.y >= this.top && pos.y <= this.bottom; 175 | } 176 | } 177 | 178 | function Pos(x, y) 179 | { 180 | this.x = x; 181 | this.y = y; 182 | 183 | this.add = function(pos) 184 | { 185 | return new Pos(this.x + pos.x, this.y + pos.y); 186 | } 187 | 188 | this.sub = function(pos) 189 | { 190 | return new Pos(this.x - pos.x, this.y - pos.y); 191 | } 192 | 193 | this.equals = function(pos) 194 | { 195 | return this.x == pos.x && this.y == pos.y; 196 | } 197 | 198 | this.toString = function() 199 | { 200 | return "(" + this.x + "," + this.y + ")"; 201 | } 202 | } -------------------------------------------------------------------------------- /scripts/wire.js: -------------------------------------------------------------------------------- 1 | function Wire(start, end) 2 | { 3 | var myStartConns = new Array(); 4 | var myEndConns = new Array(); 5 | 6 | this.group = null; 7 | 8 | this.start = new Pos(start.x, start.y); 9 | this.end = new Pos(end.x, end.y); 10 | 11 | this.selected = false; 12 | 13 | if (this.start.x > this.end.x || this.start.y > this.end.y) 14 | { 15 | var temp = this.start; 16 | this.start = this.end; 17 | this.end = temp; 18 | } 19 | 20 | this.clone = function() 21 | { 22 | return new Wire(this.start, this.end); 23 | } 24 | 25 | this.equals = function(wire) 26 | { 27 | return wire.start.equals(this.start) && wire.end.equals(this.end); 28 | } 29 | 30 | this.render = function(context, offset, selectClr) 31 | { 32 | if (this.selected) 33 | { 34 | if (selectClr == null) selectClr = "#6666FF"; 35 | 36 | context.globalAlpha = 0.5; 37 | context.fillStyle = selectClr; 38 | context.fillRect(this.start.x + offset.x - 4, this.start.y + offset.y - 4, 39 | this.end.x - this.start.x + 8, this.end.y - this.start.y + 8); 40 | context.globalAlpha = 1.0; 41 | } 42 | 43 | context.strokeStyle = "#000000"; 44 | context.lineWidth = 2; 45 | 46 | context.beginPath(); 47 | context.moveTo(this.start.x + offset.x, this.start.y + offset.y); 48 | context.lineTo(this.end.x + offset.x, this.end.y + offset.y); 49 | context.stroke(); 50 | context.closePath(); 51 | 52 | context.fillStyle = "#000000"; 53 | 54 | if (myStartConns.length > 1) { 55 | context.beginPath(); 56 | context.arc(this.start.x + offset.x, this.start.y + offset.y, 3, 0, Math.PI * 2, true); 57 | context.fill(); 58 | context.closePath(); 59 | } 60 | 61 | if (myEndConns.length > 1) { 62 | context.beginPath(); 63 | context.arc(this.end.x + offset.x, this.end.y + offset.y, 3, 0, Math.PI * 2, true); 64 | context.fill(); 65 | context.closePath(); 66 | } 67 | } 68 | 69 | this.getConnections = function() 70 | { 71 | return myStartConns.concat(myEndConns); 72 | } 73 | 74 | this.isHorizontal = function() 75 | { 76 | return this.start.y == this.end.y; 77 | } 78 | 79 | this.isVertical = function() 80 | { 81 | return this.start.x == this.end.x; 82 | } 83 | 84 | this.runsAlong = function(wire) 85 | { 86 | return (this.isHorizontal() && wire.isHorizontal() 87 | && this.start.y == wire.start.y && this.start.x <= wire.end.x 88 | && this.end.x >= wire.start.x) 89 | || (this.isVertical() && wire.isVertical() 90 | && this.start.x == wire.start.x && this.start.y <= wire.end.y 91 | && this.end.y >= wire.start.y); 92 | } 93 | 94 | this.split = function(wire) 95 | { 96 | if (this.isHorizontal()) { 97 | if (wire.start.x == this.start.x || wire.start.x == this.end.x) { 98 | return []; 99 | } 100 | 101 | var splat = new Wire(new Pos(wire.start.x, this.start.y), this.end); 102 | splat.group = this.group; 103 | splat.selected = this.selected; 104 | this.end = new Pos(wire.start.x, this.start.y); 105 | 106 | return [splat]; 107 | } else { 108 | if (wire.start.y == this.start.y || wire.start.y == this.end.y) { 109 | return []; 110 | } 111 | 112 | var splat = new Wire(new Pos(this.start.x, wire.start.y), this.end); 113 | splat.group = this.group; 114 | splat.selected = this.selected; 115 | this.end = new Pos(this.start.x, wire.start.y); 116 | 117 | return [splat]; 118 | } 119 | } 120 | 121 | this.merge = function(wire) 122 | { 123 | this.selected = this.selected || wire.selected; 124 | 125 | if (this.isHorizontal()) { 126 | this.start.x = Math.min(this.start.x, wire.start.x); 127 | this.end.x = Math.max(this.end.x, wire.end.x ); 128 | } else { 129 | this.start.y = Math.min(this.start.y, wire.start.y); 130 | this.end.y = Math.max(this.end.y, wire.end.y ); 131 | } 132 | } 133 | 134 | this.crossesPos = function(pos) 135 | { 136 | return (this.isHorizontal() && this.start.y == pos.y 137 | && this.start.x <= pos.x && this.end.x >= pos.x) 138 | || (this.isVertical() && this.start.x == pos.x 139 | && this.start.y <= pos.y && this.end.y >= pos.y); 140 | } 141 | 142 | this.intersects = function(wire) 143 | { 144 | return this.start.x <= wire.end.x && this.end.x >= wire.start.x && 145 | this.start.y <= wire.end.y && this.end.y >= wire.start.y; 146 | } 147 | 148 | this.crosses = function(wire) 149 | { 150 | return this.start.x < wire.end.x && this.end.x > wire.start.x && 151 | this.start.y < wire.end.y && this.end.y > wire.start.y; 152 | } 153 | 154 | this.crossPos = function(wire) 155 | { 156 | if (this.isVertical() && wire.isHorizontal()) { 157 | return new Pos(this.start.x, wire.start.y); 158 | } else { 159 | return new Pos(wire.start.x, this.start.y); 160 | } 161 | } 162 | 163 | this.canConnect = function(wire) 164 | { 165 | return !myStartConns.contains(wire) && !myEndConns.contains(wire) 166 | && this.intersects(wire) && !this.crosses(wire); 167 | } 168 | 169 | this.hasConnection = function(pos) 170 | { 171 | if (pos.equals(this.start)) { 172 | return myStartConns.length > 0; 173 | } 174 | 175 | if (pos.equals(this.end)) { 176 | return myEndConns.length > 0; 177 | } 178 | 179 | return false; 180 | } 181 | 182 | this.connect = function(wire) 183 | { 184 | if (wire == this) return; 185 | 186 | var conns = myStartConns; 187 | 188 | if (this.end.equals(wire.start) || this.end.equals(wire.end)) { 189 | conns = myEndConns; 190 | } 191 | 192 | if (!conns.contains(wire)) { 193 | conns.push(wire); 194 | } 195 | } 196 | 197 | this.disconnect = function(wire) 198 | { 199 | var index = myConnections.indexOf(wire); 200 | myConnections.splice(index, 1); 201 | } 202 | 203 | this.toString = function() 204 | { 205 | return "(" + this.start.x + "," + this.start.y + ":" + this.end.x + "," + this.end.y + ")"; 206 | } 207 | } -------------------------------------------------------------------------------- /scripts/wiregroup.js: -------------------------------------------------------------------------------- 1 | function WireGroup() 2 | { 3 | var myWires = new Array(); 4 | var myBounds = null; 5 | 6 | this.input = null; 7 | this.outputs = new Array(); 8 | 9 | this.isEmpty = false; 10 | 11 | this.getWires = function() 12 | { 13 | return myWires; 14 | } 15 | 16 | this.canAddWire = function(wire) 17 | { 18 | if (myBounds == null || !myBounds.intersectsWire(wire, true)) return false; 19 | 20 | for (var i = 0; i < myWires.length; ++ i) { 21 | if (myWires[i].canConnect(wire)) { 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | 29 | this.crossesPos = function(pos) 30 | { 31 | if (myBounds == null || !myBounds.contains(pos)) return false; 32 | 33 | for (var i = 0; i < myWires.length; ++ i) { 34 | if (myWires[i].crossesPos(pos)) { 35 | return true; 36 | } 37 | } 38 | 39 | return false; 40 | } 41 | 42 | this.getWireAt = function(pos) 43 | { 44 | if (myBounds == null || !myBounds.contains(pos)) return null; 45 | 46 | for (var i = 0; i < myWires.length; ++ i) { 47 | if (myWires[i].crossesPos(pos)) return myWires[i]; 48 | } 49 | 50 | return null; 51 | } 52 | 53 | this.setInput = function(gate, output) 54 | { 55 | this.input = new Link(gate, output); 56 | 57 | for (var i = 0; i < this.outputs.length; ++ i) { 58 | var link = this.outputs[i]; 59 | link.gate.linkInput(this.input.gate, this.input.socket, link.socket); 60 | } 61 | } 62 | 63 | this.removeInput = function() 64 | { 65 | this.input = null; 66 | 67 | var wires = myWires; 68 | myWires = []; 69 | 70 | for (var i = 0; i < this.outputs.length; ++ i) { 71 | var link = this.outputs[i]; 72 | logicSim.removeGate(link.gate); 73 | logicSim.placeGate(link.gate); 74 | } 75 | 76 | myWires = wires; 77 | } 78 | 79 | this.addOutput = function(gate, input) 80 | { 81 | var link = new Link(gate, input); 82 | 83 | if (this.outputs.containsEqual(link)) return; 84 | 85 | if (this.input != null) { 86 | gate.linkInput(this.input.gate, this.input.socket, link.socket); 87 | } 88 | 89 | this.outputs.push(link); 90 | } 91 | 92 | this.removeOutput = function(link) 93 | { 94 | logicSim.removeGate(link.gate); 95 | logicSim.placeGate(link.gate); 96 | } 97 | 98 | this.removeAllOutputs = function() 99 | { 100 | var wires = myWires; 101 | myWires = []; 102 | 103 | for (var i = this.outputs.length - 1; i >= 0; -- i) { 104 | this.removeOutput(this.outputs[i]); 105 | } 106 | 107 | myWires = wires; 108 | } 109 | 110 | this.addWire = function(wire) 111 | { 112 | if (wire.group == this) return; 113 | 114 | if (myBounds == null) { 115 | myBounds = new Rect(wire.start.x, wire.start.y, 116 | wire.end.x - wire.start.x, wire.end.y - wire.start.y); 117 | } else { 118 | if (wire.start.x < myBounds.left) { 119 | myBounds.setLeft(wire.start.x); 120 | } 121 | if (wire.end.x > myBounds.right) { 122 | myBounds.setRight(wire.end.x); 123 | } 124 | if (wire.start.y < myBounds.top) { 125 | myBounds.setTop(wire.start.y); 126 | } 127 | if (wire.end.y > myBounds.bottom) { 128 | myBounds.setBottom(wire.end.y); 129 | } 130 | } 131 | 132 | wire.group = this; 133 | 134 | myWires.push(wire); 135 | } 136 | 137 | this.render = function(context, offset, selectClr) 138 | { 139 | for (var i = 0; i < myWires.length; ++ i) { 140 | myWires[i].render(context, offset, selectClr); 141 | } 142 | } 143 | } 144 | --------------------------------------------------------------------------------