cpu-simulator/build/editor/js/index.js

426 lines
14 KiB
JavaScript
Raw Permalink Normal View History

2023-12-15 19:43:36 +00:00
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.pin<chip.inputs)
chip.pins[hoveredPin.pin]=1-chip.pins[hoveredPin.pin];
} else {
if(wireBeingDrawn.length>0){
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<canvas.width/2?this.x:this.x-width,this.y-25-(26*offset),width,25);
ctx.fillStyle="white";
ctx.fillText(this.message,this.x<canvas.width/2?(this.x+3):(this.x-width+3),this.y-(26*offset));
}
}
function drawTooltip(message){
if(!message) return
if(!tt){
tt = new Tooltip(pos.x,pos.y,message);
}
}
function drawCanvas() {
//console.log(`Processed ${numUpdates} times`);
numUpdates=0;
// loadChip();
pI = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.fillStyle="darkgray";
ctx.fillRect(0,0,20,canvas.height-60);
ctx.fillRect(canvas.width-20,0,canvas.width,canvas.height-60);
ctx.fillStyle="#303030";
ctx.fillRect(0,canvas.height-60,canvas.width,canvas.height);
var cm=[];
var curX = 5;
Object.entries(chips).forEach(chip=>{
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;i<wireBeingDrawn.length-1;i++){
if(i==0)
p1=pinImages.find(p=>p.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;i<wv.path.length-1;i++){
if(i==0)
p1=pinImages.find(p=>p.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<chip.inputs){
centerX=10;
otherpins = chip.inputs;
pindex=i;
} else {
centerX = canvas.width - 10;
otherpins = chip.pins.length - chip.inputs;
pindex = i - chip.inputs;
}
ys = makeArr(0-(20/otherpins),0+(canvas.height-60)+(20/otherpins),otherpins+2);
ys.shift();
ys.pop();
centerY = ys[pindex];
//ctx.beginPath();
//ctx.arc(Math.floor(centerX), Math.floor(centerY), 10, 0, 2*Math.PI, false)
(hoveredPin.component==0&&hoveredPin.pin==i&&hoveredPin.type==0) ? offset=2 : offset=0;
newGroup = -1;
chip.groups.forEach((group,n)=>{
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<chip.inputs?centerX+20:centerX-20);
pI.push({component:0,pin:i,position:{x:secondaryX,y:centerY},radius:5,type:0});
fillStyle1=["black","red","grey","pink"][chip.pins[i]+offset];
fillStyle2=(i<chip.inputs?["black","red","grey","pink"]:["black","red","black","red"])[chip.pins[i]+((hoveredPin.component==0&&hoveredPin.pin==i&&hoveredPin.type==1)?2:0)];
//ctx.fill();
drawLine({x:centerX,y:centerY},{x:secondaryX,y:centerY},1,["black","red"][chip.pins[i]],false);
drawCircle(centerX,centerY,10,fillStyle2);
drawCircle(secondaryX,centerY,5,fillStyle1);
if(newGroup>-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<c.inputs){
centerX = c.position.x-5;
if(["xor","nor","or"].includes(c.type)){
centerX += 9;
}
otherpins = c.inputs;
pindex=i;
}
ys = makeArr(c.position.y-(20/otherpins),c.position.y+img.height+(20/otherpins),otherpins+2);
ys.shift();
ys.pop();
centerY = ys[pindex];
pI.push({component:chip.subComponents.indexOf(c),pin:i,position:{x:centerX,y:centerY},radius:10,type:0});
ctx.beginPath();
ctx.arc(Math.floor(centerX), Math.floor(centerY), 5, 0, 2*Math.PI, false);
(hoveredPin.component==chip.subComponents.indexOf(c)&&hoveredPin.pin==i) ? offset=2 : offset=0;
ctx.fillStyle=["black","red","grey","pink"][c.pins[i]+offset];
ctx.fill();
});
});
pinImages=pI;
tt = null;
if(hoveredPin.component>=0){
try{
drawTooltip(chip.subComponents[hoveredPin.component].tooltips[hoveredPin.pin]);
} catch(ex){console.log(ex)}
} else {
}
if(tt) tt.draw();
requestAnimationFrame(drawCanvas);
}