var canvas, ctx var selectedItem var mouseisdown var chip = new Component("main"); var pinImages = []; var wireVisuals = []; var wireBeingDrawn = []; images = {}; var chipoffset=0; var numUpdates = 0; var x; document.addEventListener("DOMContentLoaded", function(event){ canvas = document.getElementById('logicsim'); canvas.addEventListener("mousedown",selectItem); canvas.addEventListener("mouseup", function(){mouseisdown=false}); canvas.addEventListener("mousemove",mousemove); document.addEventListener("keydown",checkKey); document.addEventListener("keyup",function(e){shifted=e.shiftKey}); ctx = canvas.getContext('2d'); preloadAssets(); requestAnimationFrame(drawCanvas); //x=setInterval(function(){var st = performance.now(); chip.process(console.log(performance.now()-st))},1.1); y=0 x=setInterval(function(){chip.process();numUpdates+=1},0.3); storedChips = localStorage.getItem('chips'); if(storedChips) chips = JSON.parse(storedChips); }); function getMousePos(c,e){ var rect = c.getBoundingClientRect(); return { x: e.clientX - rect.left, y: e.clientY - rect.top } } function checkKey(e){ e=e||window.event; if(e.keyCode==27){ selectedPin={component:-1,pin:-1}; wireBeingDrawn=[]; } else if(e.key=="n"){ var name = prompt('name new chip or cancel'); if (name.length){ chips[name]=JSON.parse(chip.getJSONandName(name)); localStorage.setItem('chips',JSON.stringify(chips)); chip = new Component(); close(); } } else if(e.keyCode==8 || e.keyCode==46){if(selectedItem>=0){chip.removeComponent(selectedItem); wireVisuals.forEach(wv=>{ wv.path.forEach(p=>{if(p.component>selectedItem){p.component-=1}}); });}} else if (e.keyCode==37) { chipoffset+=10; } else if (e.keyCode==39) { chipoffset-=10}; shifted=e.shiftKey; } function isInside(c,point){ return ( point.x > c.position.x && point.x < c.position.x + images[c.type].width) && ( point.y > c.position.y && point.y < c.position.y + images[c.type].height); } function isInsidePin(p,point){ return Math.sqrt(Math.pow((p.position.x-point.x),2)+Math.pow((p.position.y-point.y),2)) < p.radius; } var clickpos; var shifted = 0; var nWireAttempts=0; var selectedPin = {component:-1,pin:-1}; var hoveredPin = {component:-1,pin:-1,type:-1}; function selectItem(e){ pos = getMousePos(canvas, e) if(pos.y>canvas.height-60){ chipMenu.forEach(cm=>{ if((pos.x>cm.topLeft.x && pos.x < (cm.topLeft.x+cm.width))&&(pos.y>cm.topLeft.y&&pos.y<(cm.topLeft.y+40))){ console.log(cm.type); chip.addComponent(cm.type); //.position = {x:pos.x,y:pos.y} } else { } }); return; } if(hoveredPin.component>=0){ if(hoveredPin.type==1){ if(hoveredPin.pin0){ if(chip.connect(selectedPin, hoveredPin, nWireAttempts)){ wireBeingDrawn[wireBeingDrawn.length-1]={component:hoveredPin.component,pin:hoveredPin.pin,shift:shifted,offset:wireBeingDrawn[1].offset}; wireVisuals.push({wireId:nWireAttempts,path:wireBeingDrawn.slice()}); wireBeingDrawn=[]; selectedPin={component:-1,pin:-1}; nWireAttempts+=1; } else { console.log("noooo"); } } else { inp = chip.subComponents[hoveredPin.component].inputs; defaultOffset = 10; if(hoveredPin.component == 0 && hoveredPin.pin < inp){ o = 7*hoveredPin.pin; rev = 1 } else if(hoveredPin.component == 0) { o = 7*(hoveredPin.pin-inp); rev = -1 } else if (hoveredPin.component>0 && hoveredPin.pin < inp) { o = 7*hoveredPin.pin; rev = -1 } else { o = 7*(hoveredPin.pin-inp) rev = 1 } wireBeingDrawn.push({component:hoveredPin.component,pin:hoveredPin.pin,shift:shifted,rev:rev}); wireBeingDrawn.push({x:pos.x,y:pos.y,shift:shifted,offset:o}); selectedPin = {component:hoveredPin.component,pin:hoveredPin.pin}; } } } else if(pos.x<20){ if(typeof editDisabled == 'undefined') chip.addPin(1); //wireVisuals.forEach(wv=>{if(wv.path[0].component==0){wv.path[wv.path.length-1].pin+=1;}}); return true; } else if(pos.x>canvas.width-20){ if(typeof editDisabled == 'undefined') chip.addPin(); return true; } else { if(wireBeingDrawn.length>0){ wireBeingDrawn[wireBeingDrawn.length-1]={x:pos.x,y:pos.y,shift:shifted,offset:wireBeingDrawn[1].offset}; wireBeingDrawn.push({x:pos.x,y:pos.y,shift:shifted,offset:wireBeingDrawn[1].offset})//slice()); } } selectedItem=-1; chip.zIndex.forEach(function(c,i){ if (isInside(c, pos) && ctx.getImageData(pos.x,pos.y,1,1).data[3]) { mouseisdown = true; clickpos = pos; chip.sendToFront(chip.getProperIndex(c)); selectedItem = chip.getProperIndex(c); console.log(selectedItem); // DELETE THIS LATER // return; } }); } var pos; function mousemove(e){ pos = getMousePos(canvas,e); if(wireBeingDrawn.length>0){ o = wireBeingDrawn[wireBeingDrawn.length-1].offset; wireBeingDrawn[wireBeingDrawn.length-1]={x:pos.x,y:pos.y,shift:shifted,offset:o}; } if(mouseisdown){ last = chip.zIndex.length-1; chip.zIndex[last].position.x -= (clickpos.x - pos.x); chip.zIndex[last].position.y -= (clickpos.y - pos.y); clickpos = pos; } if(!pinImages.some(pi=>{ if(isInsidePin(pi,pos)){ hoveredPin = {component:pi.component,pin:pi.pin,type:pi.type} return true; } })){ hoveredPin={component:-1,pin:-1}; } } function preloadAssets(){ Object.entries(chips).forEach(function(c){ if (!(c[1].image in images)){ images[c[0]] = new Image(); images[c[0]].src = c[1].image; } }); if(typeof loadChip == 'function'){ loadChip(); } } function makeArr(startValue, stopValue, cardinality) { var arr = []; var step = (stopValue - startValue) / (cardinality - 1); for (var i = 0; i < cardinality; i++) { arr.push(startValue + (step * i)); } return arr; } function drawCircle(x,y,radius,colour){ ctx.beginPath(); ctx.arc(Math.floor(x),Math.floor(y),radius,0,2*Math.PI,false); ctx.fillStyle=colour; ctx.fill(); } function drawLine(origin,destination,width,colour,taxi,offset,rev){ if(!taxi||destination.shift){ ctx.beginPath(); ctx.moveTo(origin.x,origin.y); ctx.lineTo(destination.x,destination.y); ctx.strokeStyle=colour; ctx.lineWidth=width; ctx.stroke(); } else { ctx.beginPath(); ctx.moveTo(origin.x,origin.y); if(destination.y>origin.y){ offset = offset*-rev; } else { offset = offset * rev; } multip = 0.4; if(rev<0) { multip = 1-multip; } ctx.lineTo(origin.x+(destination.x-origin.x)*multip+offset,origin.y); ctx.lineTo(origin.x+(destination.x-origin.x)*multip+offset,destination.y); ctx.lineTo(destination.x,destination.y); ctx.strokeStyle=colour; ctx.lineWidth=width; ctx.stroke(); } } chipMenu = []; tt = null; class Tooltip { constructor(x,y,message,persist=false){ this.x=x; this.y=y; this.message=message; this.persist=persist; } draw(offset){ //ctx.fillStyle='pink'; //ctx.fillRect(0,0,canvas.width,canvas.height); //return; ctx.fillStyle="rgba(0,0,0,0.65)"; ctx.font="20px vcr_osd_monoregular"; ctx.textBaseline="bottom"; ctx.textAlign="left"; var width=ctx.measureText(this.message).width+6; offset=0 ctx.fillRect(this.x{ ctx.font = "20px vcr_osd_monoregular" ctx.fillStyle="#1A3060"; width=ctx.measureText(chip[0]).width+8; ctx.fillRect(curX+chipoffset,canvas.height-50,width,40); ctx.fillStyle="white"; ctx.fillText(chip[0],curX+chipoffset+4,canvas.height-25); cm.push({type:chip[0],topLeft:{x: curX+chipoffset, y: canvas.height-50},width:width}); curX+=width+5; }); chipMenu=cm; //components.slice().reverse().forEach(function(c){ for(var i=0;ip.component==wireBeingDrawn[0].component && p.pin==wireBeingDrawn[0].pin && p.type==0).position; else p1=wireBeingDrawn[i]; //if(i==wireBeingDrawn.length-2) // p2=pinImages.find(p=>p.component==wireBeingDrawn[i+1].component&&p.pin==wireBeingDrawn[i+1].pin&&p.type==0).position; //else p2=wireBeingDrawn[i+1] o=0.5; if(wireBeingDrawn[i+1].offset != undefined){ o=wireBeingDrawn[i+1].offset; } drawLine(p1,p2,3,["lightgray","red"][chip.subComponents[wireBeingDrawn[0].component].pins[wireBeingDrawn[0].pin]],true,o,wireBeingDrawn[0].rev); } wireVisuals=wireVisuals.filter(wv=> {return chip.wires.findIndex(w=>w.wireId==wv.wireId)>=0}); wireVisuals.forEach(wv=>{ for(var i=0;ip.component==wv.path[0].component && p.pin==wv.path[0].pin && p.type==0).position; else p1=wv.path[i]; if(i==wv.path.length-2) p2=pinImages.find(p=>p.component==wv.path[i+1].component&&p.pin==wv.path[i+1].pin&&p.type==0).position; else p2=wv.path[i+1]; drawLine(p1,p2,3,["lightgray","red"][chip.subComponents[wv.path[0].component].pins[wv.path[0].pin]],true,wv.path[1].offset,wv.path[0].rev); } }); currentGroup=-1; groupn=1; chip.pins.forEach((p,i)=>{ if(i{ if(group.includes(i)) newGroup = n; }); if(newGroup>-1 && newGroup==currentGroup) { centerY -=20*groupn; groupn++; } else {groupn=1}; pI.push({component:0,pin:i,position:{x:centerX,y:centerY},radius:7,type:1}); secondaryX=(i-1){ ctx.fillStyle="white"; old=[ctx.textAlign, ctx.textBaseline]; ctx.textAlign="center"; ctx.textBaseline="middle"; ctx.font='11px monospace' if(!chip.no) ctx.fillText(Math.pow(2,groupn-1), centerX, centerY); else if(!chip.no.includes(i)) ctx.fillText(Math.pow(2,groupn-1), centerX, centerY); ctx.textAlign=old[0]; ctx.textBaseline=old[1]; } currentGroup=newGroup; }) chip.zIndex.forEach(c=>{ img = images[c.type]; if(img!==undefined&&img.dummy!=1) ctx.drawImage(img,c.position.x,c.position.y); else { ctx.font = "20px vcr_osd_monoregular" ctx.fillStyle="#1A3060"; h = parseInt(md5(c.type).substring(1,10),16); poss_colors = ["white","red","lightblue","pink","yellow","lightgreen","orange","violet"]; ctx.fillStyle = poss_colors[h%poss_colors.length]; width = ctx.measureText(c.type).width + 30; if (c.inputs>c.pins.length/2){ height = c.inputs * 15 } else { height = (c.pins.length-c.inputs)*15; } ctx.fillRect(c.position.x,c.position.y,width,height); ctx.fillStyle="black"; ctx.fillText(c.type,c.position.x+15,c.position.y+(height/2)+4); img = {width,height,dummy:1}; images[c.type]=img; } c.pins.forEach((p,i)=>{ //trueCenterY = (c.position.y+(c.position.y + c.height))/2 centerX = c.position.x+img.width+5; otherpins = c.pins.length - c.inputs; pindex = i - c.inputs; if(i=0){ try{ drawTooltip(chip.subComponents[hoveredPin.component].tooltips[hoveredPin.pin]); } catch(ex){console.log(ex)} } else { } if(tt) tt.draw(); requestAnimationFrame(drawCanvas); }