把前端从CircuitVerse中拆了出来

This commit is contained in:
zhbaor 2022-01-23 17:33:42 +08:00
commit 5bf1284599
2182 changed files with 189323 additions and 0 deletions

132
public/js/Counter.js Normal file
View file

@ -0,0 +1,132 @@
/**
* Counter component.
*
* Counts from zero to a particular maximum value, which is either
* specified by an input pin or determined by the Counter's bitWidth.
*
* The counter outputs its current value and a flag that indicates
* when the output value is zero and the clock is 1.
*
* The counter can be reset to zero at any point using the RESET pin.
*/
function Counter(x, y, scope = globalScope, bitWidth = 8) {
CircuitElement.call(this, x, y, scope, "RIGHT", bitWidth);
this.directionFixed = true;
this.rectangleObject = true;
this.setDimensions(20, 20);
this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, "MaxValue");
this.clock = new Node(-20, +10, 0, this, 1, "Clock");
this.reset = new Node(0, 20, 0, this, 1, "Reset");
this.output = new Node(20, -10, 1, this, this.bitWidth, "Value");
this.zero = new Node(20, 10, 1, this, 1, "Zero");
this.value = 0;
this.prevClockState = undefined;
}
Counter.prototype = Object.create(CircuitElement.prototype);
Counter.prototype.constructor = Counter;
Counter.prototype.tooltipText = "Counter: a binary counter from zero to a given maximum value";
Counter.prototype.helplink = "https://docs.circuitverse.org/#/inputElements?id=counter";
Counter.prototype.customSave = function () {
return {
nodes: {
maxValue: findNode(this.maxValue),
clock: findNode(this.clock),
reset: findNode(this.reset),
output: findNode(this.output),
zero: findNode(this.zero),
},
constructorParamaters: [this.bitWidth]
};
}
Counter.prototype.newBitWidth = function (bitWidth) {
this.bitWidth = bitWidth;
this.maxValue.bitWidth = bitWidth;
this.output.bitWidth = bitWidth;
};
Counter.prototype.isResolvable = function () {
return true;
}
Counter.prototype.resolve = function () {
// Max value is either the value in the input pin or the max allowed by the bitWidth.
var maxValue = this.maxValue.value != undefined ? this.maxValue.value : (1 << this.bitWidth) - 1;
var outputValue = this.value;
// Increase value when clock is raised
if (this.clock.value != this.prevClockState && this.clock.value == 1) {
outputValue++;
}
this.prevClockState = this.clock.value;
// Limit to the effective maximum value; this also accounts for bitWidth changes.
outputValue = outputValue % (maxValue + 1);
// Reset to zero if RESET pin is on
if (this.reset.value == 1) {
outputValue = 0;
}
// Output the new value
this.value = outputValue;
if (this.output.value != outputValue) {
this.output.value = outputValue;
simulationArea.simulationQueue.add(this.output);
}
// Output the zero signal
var zeroValue = this.clock.value == 1 && outputValue == 0 ? 1 : 0;
if (this.zero.value != zeroValue) {
this.zero.value = zeroValue;
simulationArea.simulationQueue.add(this.zero);
}
}
Counter.prototype.customDraw = function () {
var ctx = simulationArea.context;
var xx = this.x;
var yy = this.y;
ctx.beginPath();
ctx.font = "20px Georgia";
ctx.fillStyle = "green";
ctx.textAlign = "center";
fillText(ctx, this.value.toString(16), this.x, this.y + 5);
ctx.fill();
ctx.beginPath();
moveTo(ctx, -20, 5, xx, yy, this.direction);
lineTo(ctx, -15, 10, xx, yy, this.direction);
lineTo(ctx, -20, 15, xx, yy, this.direction);
ctx.stroke();
}
Counter.moduleVerilog = function () {
return `
module Counter(val, zero, max, clk, rst);
parameter WIDTH = 1;
output reg [WIDTH-1:0] val;
output reg zero;
input [WIDTH-1:0] max;
input clk, rst;
initial
val = 0;
always @ (val)
if (val == 0)
zero = 1;
else
zero = 0;
always @ (posedge clk or posedge rst) begin
if (rst)
val <= 0;
else
if (val == max)
val <= 0;
else
val <= val + 1;
end
endmodule`;
}

854
public/js/Node.js Normal file
View file

@ -0,0 +1,854 @@
function constructNodeConnections(node, data) {
for (var i = 0; i < data["connections"].length; i++)
if (!node.connections.contains(node.scope.allNodes[data["connections"][i]])) node.connect(node.scope.allNodes[data["connections"][i]]);
}
//Fn to replace node by node @ index in global Node List - used when loading
function replace(node, index) {
if (index == -1) {
return node;
}
var scope = node.scope;
var parent = node.parent;
parent.nodeList.clean(node);
node.delete();
node = scope.allNodes[index];
node.parent = parent;
parent.nodeList.push(node);
node.updateRotation();
return node;
}
function extractBits(num, start, end) {
return (num << (32 - end)) >>> (32 - (end - start + 1));
}
function bin2dec(binString) {
return parseInt(binString, 2);
}
function dec2bin(dec, bitWidth = undefined) {
// only for positive nos
var bin = (dec).toString(2);
if (bitWidth == undefined) return bin;
return '0'.repeat(bitWidth - bin.length) + bin;
}
//find Index of a node
function findNode(x) {
return x.scope.allNodes.indexOf(x);
}
function loadNode(data, scope) {
var n = new Node(data["x"], data["y"], data["type"], scope.root, data["bitWidth"], data["label"]);
}
//get Node in index x in scope and set parent
function extractNode(x, scope, parent) {
var n = scope.allNodes[x];
n.parent = parent;
return n;
}
//output node=1
//input node=0
//intermediate node =2
NODE_OUTPUT = 1;
NODE_INPUT = 0;
NODE_INTERMEDIATE = 2;
function Node(x, y, type, parent, bitWidth = undefined, label = "") {
// debugger
// Should never raise, but just in case
if(isNaN(x) || isNaN(y)){
this.delete();
showError("Fatal error occurred");
return;
}
forceResetNodes = true;
this.objectType = "Node";
this.id = 'node' + uniqueIdCounter;
uniqueIdCounter++;
this.parent = parent;
if (type != 2 && this.parent.nodeList !== undefined)
this.parent.nodeList.push(this);
if (bitWidth == undefined) {
this.bitWidth = parent.bitWidth;
} else {
this.bitWidth = bitWidth;
}
this.label = label;
this.prevx = undefined;
this.prevy = undefined;
this.leftx = x;
this.lefty = y;
this.x = x;
this.y = y;
this.type = type;
this.connections = new Array();
this.value = undefined;
this.radius = 5;
this.clicked = false;
this.hover = false;
this.wasClicked = false;
this.scope = this.parent.scope;
this.prev = 'a';
this.count = 0;
this.highlighted = false;
//This fn is called during rotations and setup
this.refresh();
if (this.type == 2)
this.parent.scope.nodes.push(this);
this.parent.scope.allNodes.push(this);
this.queueProperties = {
inQueue: false,
time: undefined,
index: undefined,
}
}
Node.prototype.propagationDelay = 0;
Node.prototype.subcircuitOverride = false;
Node.prototype.setLabel = function(label) {
this.label = label; //|| "";
}
Node.prototype.converToIntermediate = function() {
this.type = 2;
this.x = this.absX();
this.y = this.absY();
this.parent = this.scope.root;
this.scope.nodes.push(this);
}
Node.prototype.startDragging = function() {
this.oldx = this.x;
this.oldy = this.y;
}
Node.prototype.drag = function() {
this.x = this.oldx + simulationArea.mouseX - simulationArea.mouseDownX;
this.y = this.oldy + simulationArea.mouseY - simulationArea.mouseDownY;
}
Node.prototype.saveObject = function() {
if (this.type == 2) {
this.leftx = this.x;
this.lefty = this.y;
}
var data = {
x: this.leftx,
y: this.lefty,
type: this.type,
bitWidth: this.bitWidth,
label: this.label,
connections: [],
}
for (var i = 0; i < this.connections.length; i++) {
data["connections"].push(findNode(this.connections[i]));
}
return data;
}
Node.prototype.updateRotation = function() {
var x, y;
[x, y] = rotate(this.leftx, this.lefty, this.parent.direction);
this.x = x;
this.y = y;
}
Node.prototype.refresh = function() {
this.updateRotation();
for (var i = 0; i < this.connections.length; i++) {
this.connections[i].connections.clean(this);
}
this.connections = [];
}
Node.prototype.absX = function() {
return this.x + this.parent.x;
}
Node.prototype.absY = function() {
return this.y + this.parent.y;
}
Node.prototype.updateScope = function(scope) {
this.scope = scope;
if (this.type == 2) this.parent = scope.root;
}
Node.prototype.isResolvable = function() {
return this.value != undefined;
}
Node.prototype.reset = function() {
this.value = undefined;
this.highlighted = false;
}
Node.prototype.connect = function(n) {
if (n == this) return;
if (n.connections.contains(this)) return;
var w = new Wire(this, n, this.parent.scope);
this.connections.push(n);
n.connections.push(this);
updateCanvas = true;
updateSimulation = true;
scheduleUpdate();
}
Node.prototype.connectWireLess = function(n) {
if (n == this) return;
if (n.connections.contains(this)) return;
this.connections.push(n);
n.connections.push(this);
updateCanvas = true;
updateSimulation = true;
scheduleUpdate();
}
Node.prototype.disconnectWireLess = function(n) {
this.connections.clean(n);
n.connections.clean(this);
}
Node.prototype.resolve = function() {
// Remove Propogation of values (TriState)
if (this.value == undefined) {
for (var i = 0; i < this.connections.length; i++) {
if (this.connections[i].value !== undefined) {
this.connections[i].value = undefined;
simulationArea.simulationQueue.add(this.connections[i]);
}
}
if (this.type == NODE_INPUT) {
if (this.parent.objectType == "Splitter") {
this.parent.removePropagation();
} else
if (this.parent.isResolvable())
simulationArea.simulationQueue.add(this.parent);
else
this.parent.removePropagation();
}
if (this.type == NODE_OUTPUT && !this.subcircuitOverride) {
if (this.parent.isResolvable() && !this.parent.queueProperties.inQueue) {
if (this.parent.objectType == "TriState") {
if (this.parent.state.value)
simulationArea.simulationQueue.add(this.parent);
} else {
simulationArea.simulationQueue.add(this.parent);
}
}
}
return;
}
if (this.type == 0) {
if (this.parent.isResolvable())
simulationArea.simulationQueue.add(this.parent);
}
for (var i = 0; i < this.connections.length; i++) {
let node = this.connections[i];
if (node.value != this.value) {
if (node.type == 1 && node.value != undefined && node.parent.objectType != "TriState" && !(node.subcircuitOverride && node.scope != this.scope)) {
this.highlighted = true;
node.highlighted = true;
showError("Contention Error: " + this.value + " and " + node.value);
} else if (node.bitWidth == this.bitWidth || node.type == 2) {
if (node.parent.objectType == "TriState" && node.value != undefined && node.type == 1) {
if (node.parent.state.value)
simulationArea.contentionPending.push(node.parent);
}
node.bitWidth = this.bitWidth;
node.value = this.value;
simulationArea.simulationQueue.add(node);
} else {
this.highlighted = true;
node.highlighted = true;
showError("BitWidth Error: " + this.bitWidth + " and " + node.bitWidth);
}
}
}
}
Node.prototype.checkHover = function() {
if (!simulationArea.mouseDown) {
if (simulationArea.hover == this) {
this.hover = this.isHover();
if (!this.hover) {
simulationArea.hover = undefined;
this.showHover = false;
}
} else if (!simulationArea.hover) {
this.hover = this.isHover();
if (this.hover) {
simulationArea.hover = this;
} else {
this.showHover = false;
}
} else {
this.hover = false;
this.showHover = false;
}
}
}
Node.prototype.draw = function() {
if (this.type == 2) this.checkHover();
var ctx = simulationArea.context;
if (this.clicked) {
if (this.prev == 'x') {
drawLine(ctx, this.absX(), this.absY(), simulationArea.mouseX, this.absY(), "black", 3);
drawLine(ctx, simulationArea.mouseX, this.absY(), simulationArea.mouseX, simulationArea.mouseY, "black", 3);
} else if (this.prev == 'y') {
drawLine(ctx, this.absX(), this.absY(), this.absX(), simulationArea.mouseY, "black", 3);
drawLine(ctx, this.absX(), simulationArea.mouseY, simulationArea.mouseX, simulationArea.mouseY, "black", 3);
} else {
if (Math.abs(this.x + this.parent.x - simulationArea.mouseX) > Math.abs(this.y + this.parent.y - simulationArea.mouseY)) {
drawLine(ctx, this.absX(), this.absY(), simulationArea.mouseX, this.absY(), "black", 3);
} else {
drawLine(ctx, this.absX(), this.absY(), this.absX(), simulationArea.mouseY, "black", 3);
}
}
}
var color = "black";
if (this.bitWidth == 1) color = ["green", "lightgreen"][this.value];
if (this.value == undefined) color = "red";
if (this.type == 2)
drawCircle(ctx, this.absX(), this.absY(), 3, color);
else drawCircle(ctx, this.absX(), this.absY(), 3, "green");
if (this.highlighted || simulationArea.lastSelected == this || (this.isHover() && !simulationArea.selected && !simulationArea.shiftDown) || simulationArea.multipleObjectSelections.contains(this)) {
ctx.strokeStyle = "green";
ctx.beginPath();
ctx.lineWidth = 3;
arc(ctx, this.x, this.y, 8, 0, Math.PI * 2, this.parent.x, this.parent.y, "RIGHT");
ctx.closePath();
ctx.stroke();
}
if (this.hover || (simulationArea.lastSelected == this)) {
if (this.showHover || simulationArea.lastSelected == this) {
canvasMessageData = {
x: this.absX(),
y: this.absY() - 15
}
if (this.type == 2) {
var v = "X";
if (this.value !== undefined)
v = this.value.toString(16);
if (this.label.length) {
canvasMessageData.string = this.label + " : " + v;
} else {
canvasMessageData.string = v;
}
} else if (this.label.length) {
canvasMessageData.string = this.label;
}
} else {
setTimeout(function() {
if (simulationArea.hover) simulationArea.hover.showHover = true;
canvasUpdate = true;
renderCanvas(globalScope)
}, 400);
}
}
}
Node.prototype.checkDeleted = function() {
if (this.deleted) this.delete();
if (this.connections.length == 0 && this.type == 2) this.delete();
}
Node.prototype.update = function() {
if (embed) return;
if (this == simulationArea.hover) simulationArea.hover = undefined;
this.hover = this.isHover();
// M CONNECT NODE
if (createNode) {
if (this.absX() != this.prevx || this.absY() != this.prevy) { // Connect to any node
this.prevx = this.absX();
this.prevy = this.absY();
this.nodeConnect();
}
}
if (this.hover) {
simulationArea.hover = this;
}
if (createNode && ((this.hover && !simulationArea.selected) || simulationArea.lastSelected == this)) {
simulationArea.selected = true;
simulationArea.lastSelected = this;
this.clicked = true;
} else {
this.clicked = false;
}
if (!this.wasClicked && this.clicked) {
this.wasClicked = true;
this.prev = 'a';
if (this.type == 2) {
if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
simulationArea.multipleObjectSelections[i].startDragging();
}
}
if (simulationArea.shiftDown) {
simulationArea.lastSelected = undefined;
if (simulationArea.multipleObjectSelections.contains(this)) {
simulationArea.multipleObjectSelections.clean(this);
} else {
simulationArea.multipleObjectSelections.push(this);
}
} else {
simulationArea.lastSelected = this;
}
}
} else if (this.wasClicked && this.clicked) {
if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
simulationArea.multipleObjectSelections[i].drag();
}
}
if (this.type == 2) {
if (this.connections.length == 1 && this.connections[0].absX() == simulationArea.mouseX && this.absX() == simulationArea.mouseX) {
this.y = simulationArea.mouseY - this.parent.y;
this.prev = 'a';
return;
} else if (this.connections.length == 1 && this.connections[0].absY() == simulationArea.mouseY && this.absY() == simulationArea.mouseY) {
this.x = simulationArea.mouseX - this.parent.x;
this.prev = 'a';
return;
}
if (this.connections.length == 1 && this.connections[0].absX() == this.absX() && this.connections[0].absY() == this.absY()) {
this.connections[0].clicked = true;
this.connections[0].wasClicked = true;
simulationArea.lastSelected = this.connections[0];
this.delete();
return;
}
}
if (this.prev == 'a' && distance(simulationArea.mouseX, simulationArea.mouseY, this.absX(), this.absY()) >= 10) {
if (Math.abs(this.x + this.parent.x - simulationArea.mouseX) > Math.abs(this.y + this.parent.y - simulationArea.mouseY)) {
this.prev = 'x';
} else {
this.prev = 'y';
}
} else if (this.prev == 'x' && this.absY() == simulationArea.mouseY) {
this.prev = 'a';
} else if (this.prev == 'y' && this.absX() == simulationArea.mouseX) {
this.prev = 'a';
}
} else if (this.wasClicked && !this.clicked) {
this.wasClicked = false;
if (simulationArea.mouseX == this.absX() && simulationArea.mouseY == this.absY()) {
return; // no new node situation
}
var x1, y1, x2, y2, flag = 0;
var n1, n2;
// (x,y) present node, (x1,y1) node 1 , (x2,y2) node 2
// n1 - node 1, n2 - node 2
// node 1 may or may not be there
// flag = 0 - node 2 only
// flag = 1 - node 1 and node 2
x2 = simulationArea.mouseX;
y2 = simulationArea.mouseY;
x = this.absX();
y = this.absY();
if (x != x2 && y != y2) {
// Rare Exception Cases
if (this.prev == 'a' && distance(simulationArea.mouseX, simulationArea.mouseY, this.absX(), this.absY()) >= 10) {
if (Math.abs(this.x + this.parent.x - simulationArea.mouseX) > Math.abs(this.y + this.parent.y - simulationArea.mouseY)) {
this.prev = 'x';
} else {
this.prev = 'y';
}
}
flag = 1;
if (this.prev == 'x') {
x1 = x2;
y1 = y;
} else if (this.prev == 'y') {
y1 = y2;
x1 = x
}
}
if (flag == 1) {
for (var i = 0; i < this.parent.scope.allNodes.length; i++) {
if (x1 == this.parent.scope.allNodes[i].absX() && y1 == this.parent.scope.allNodes[i].absY()) {
n1 = this.parent.scope.allNodes[i];
stopWire = true
break;
}
}
if (n1 == undefined) {
n1 = new Node(x1, y1, 2, this.scope.root);
for (var i = 0; i < this.parent.scope.wires.length; i++) {
if (this.parent.scope.wires[i].checkConvergence(n1)) {
this.parent.scope.wires[i].converge(n1);
break;
}
}
}
this.connect(n1);
}
for (var i = 0; i < this.parent.scope.allNodes.length; i++) {
if (x2 == this.parent.scope.allNodes[i].absX() && y2 == this.parent.scope.allNodes[i].absY()) {
n2 = this.parent.scope.allNodes[i];
stopWire = true
createNode = false
break;
}
}
if (n2 == undefined) {
n2 = new Node(x2, y2, 2, this.scope.root);
for (var i = 0; i < this.parent.scope.wires.length; i++) {
if (this.parent.scope.wires[i].checkConvergence(n2)) {
this.parent.scope.wires[i].converge(n2);
break;
}
}
}
if (flag == 0) this.connect(n2);
else n1.connect(n2);
if (simulationArea.lastSelected == this) simulationArea.lastSelected = n2;
}
if (this.type == 2 && createNode == false) {
if (this.connections.length == 2) {
if ((this.connections[0].absX() == this.connections[1].absX()) || (this.connections[0].absY() == this.connections[1].absY())) {
this.connections[0].connect(this.connections[1]);
this.delete();
}
} else if (this.connections.length == 0) this.delete();
}
}
Node.prototype.delete = function() {
updateSimulation = true;
this.deleted = true;
this.parent.scope.allNodes.clean(this);
this.parent.scope.nodes.clean(this);
this.parent.scope.root.nodeList.clean(this); // Hope this works! - Can cause bugs
if (simulationArea.lastSelected == this) simulationArea.lastSelected = undefined;
for (var i = 0; i < this.connections.length; i++) {
this.connections[i].connections.clean(this);
this.connections[i].checkDeleted();
}
wireToBeChecked = true;
forceResetNodes = true;
scheduleUpdate();
}
Node.prototype.isClicked = function() {
return this.absX() == simulationArea.mouseX && this.absY() == simulationArea.mouseY;
}
Node.prototype.isHover = function() {
return this.absX() == simulationArea.mouseX && this.absY() == simulationArea.mouseY;
}
Node.prototype.nodeConnect = function() {
var x = this.absX();
var y = this.absY();
var n;
for (var i = 0; i < this.parent.scope.allNodes.length; i++) {
if (this != this.parent.scope.allNodes[i] && x == this.parent.scope.allNodes[i].absX() && y == this.parent.scope.allNodes[i].absY()) {
n = this.parent.scope.allNodes[i];
if (this.type == 2) {
for (var j = 0; j < this.connections.length; j++) {
n.connect(this.connections[j]);
}
this.delete();
} else {
this.connect(n);
}
break;
}
}
if (n == undefined) {
for (var i = 0; i < this.parent.scope.wires.length; i++) {
if (this.parent.scope.wires[i].checkConvergence(this)) {
var n = this;
if (this.type != 2) {
n = new Node(this.absX(), this.absY(), 2, this.scope.root);
this.connect(n);
}
this.parent.scope.wires[i].converge(n);
break;
}
}
}
}
Node.prototype.cleanDelete = Node.prototype.delete;
Node.prototype.processVerilog = function() {
if (this.type == NODE_INPUT) {
this.scope.stack.push(this.parent);
}
for (var i = 0; i < this.connections.length; i++) {
if (this.connections[i].verilogLabel != this.verilogLabel) {
this.connections[i].verilogLabel = this.verilogLabel;
this.scope.stack.push(this.connections[i]);
}
}
}
// Kept for archival purposes
// function oldNodeUpdate() {
//
// if (!this.clicked && !simulationArea.mouseDown) {
// var px = this.prevx;
// var py = this.prevy;
// this.prevx = this.absX();
// this.prevy = this.absY();
// if (this.absX() != px || this.absY() != py) {
// updated = true;
// this.nodeConnect();
// return updated;
// }
// }
//
// var updated = false;
// if (!simulationArea.mouseDown) this.hover = false;
// if ((this.clicked || !simulationArea.hover) && this.isHover()) {
// this.hover = true;
// simulationArea.hover = this;
// } else if (!simulationArea.mouseDown && this.hover && this.isHover() == false) {
// if (this.hover) simulationArea.hover = undefined;
// this.hover = false;
// }
//
// if (simulationArea.mouseDown && (this.clicked)) {
//
// if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
// for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
// simulationArea.multipleObjectSelections[i].drag();
// }
// }
//
// if (this.type == 2) {
// if (this.absX() == simulationArea.mouseX && this.absY() == simulationArea.mouseY) {
// updated = false;
// this.prev = 'a';
// } else if (this.connections.length == 1 && this.connections[0].absX() == simulationArea.mouseX && this.absX() == simulationArea.mouseX) {
// this.y = simulationArea.mouseY - this.parent.y;
// this.prev = 'a';
// updated = true;
// } else if (this.connections.length == 1 && this.connections[0].absY() == simulationArea.mouseY && this.absY() == simulationArea.mouseY) {
// this.x = simulationArea.mouseX - this.parent.x;
// this.prev = 'a';
// updated = true;
// }
// if (this.connections.length == 1 && this.connections[0].absX() == this.absX() && this.connections[0].absY() == this.absY()) {
// this.connections[0].clicked = true;
// this.connections[0].wasClicked = true;
// this.delete();
// updated = true;
// }
// }
// if (this.prev == 'a' && distance(simulationArea.mouseX, simulationArea.mouseY, this.absX(), this.absY()) >= 10) {
// if (Math.abs(this.x + this.parent.x - simulationArea.mouseX) > Math.abs(this.y + this.parent.y - simulationArea.mouseY)) {
// this.prev = 'x';
// } else {
// this.prev = 'y';
// }
// }
// } else if (simulationArea.mouseDown && !simulationArea.selected) {
// simulationArea.selected = this.clicked = this.hover;
// updated |= this.clicked;
// this.prev = 'a';
// } else if (!simulationArea.mouseDown) {
// if (this.clicked) simulationArea.selected = false;
// this.clicked = false;
// this.count = 0;
// }
//
// if (this.clicked && !this.wasClicked) {
// this.wasClicked = true;
// if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.contains(this)) {
// for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
// simulationArea.multipleObjectSelections[i].startDragging();
// }
// }
//
// if (this.type == 2) {
// if (simulationArea.shiftDown) {
// simulationArea.lastSelected = undefined;
// if (simulationArea.multipleObjectSelections.contains(this)) {
// simulationArea.multipleObjectSelections.clean(this);
// } else {
// simulationArea.multipleObjectSelections.push(this);
// }
// } else {
// simulationArea.lastSelected = this;
// }
// }
// }
//
// if (this.wasClicked && !this.clicked) {
// this.wasClicked = false;
//
// if (simulationArea.mouseX == this.absX() && simulationArea.mouseY == this.absY()) {
// this.nodeConnect();
// return updated;
// }
//
// var n, n1;
// var x, y, x1, y1, flag = -1;
// x1 = simulationArea.mouseX;
// y1 = simulationArea.mouseY;
// if (this.prev == 'x') {
// x = x1;
// y = this.absY();
// flag = 0;
// } else if (this.prev == 'y') {
// y = y1;
// x = this.absX();
// flag = 1;
// }
// if (this.type == 'a') return; // this should never happen!!
//
// for (var i = 0; i < this.parent.scope.allNodes.length; i++) {
// if (x == this.parent.scope.allNodes[i].absX() && y == this.parent.scope.allNodes[i].absY()) {
// n = this.parent.scope.allNodes[i];
// this.connect(n);
// break;
// }
// }
//
// if (n == undefined) {
// n = new Node(x, y, 2, this.scope.root);
// this.connect(n);
// for (var i = 0; i < this.parent.scope.wires.length; i++) {
// if (this.parent.scope.wires[i].checkConvergence(n)) {
// this.parent.scope.wires[i].converge(n);
// }
// }
// }
// this.prev = 'a';
//
// if (flag == 0 && (this.y + this.parent.y - simulationArea.mouseY) != 0) {
// y = y1;
// flag = 2;
// } else if ((this.x + this.parent.x - simulationArea.mouseX) != 0 && flag == 1) {
// x = x1;
// flag = 2;
// }
// if (flag == 2) {
// for (var i = 0; i < this.parent.scope.allNodes.length; i++) {
// if (x == this.parent.scope.allNodes[i].absX() && y == this.parent.scope.allNodes[i].absY()) {
// n1 = this.parent.scope.allNodes[i];
// n.connect(n1);
// break;
// }
// }
// if (n1 == undefined) {
// n1 = new Node(x, y, 2, this.scope.root);
// n.connect(n1);
// for (var i = 0; i < this.parent.scope.wires.length; i++) {
// if (this.parent.scope.wires[i].checkConvergence(n1)) {
// this.parent.scope.wires[i].converge(n1);
// }
// }
// }
//
// }
// updated = true;
//
// if (simulationArea.lastSelected == this) simulationArea.lastSelected = undefined;
// }
//
// if (this.type == 2) {
// if (this.connections.length == 2 && simulationArea.mouseDown == false) {
// if ((this.connections[0].absX() == this.connections[1].absX()) || (this.connections[0].absY() == this.connections[1].absY())) {
// this.connections[0].connect(this.connections[1]);
// this.delete();
// }
// } else if (this.connections.length == 0) this.delete();
// }
//
// // if (this.clicked && this.type == 2 && simulationArea.lastSelected == undefined) simulationArea.lastSelected = this;
// return updated;
//
//
//
// }

298
public/js/Plot.js Normal file
View file

@ -0,0 +1,298 @@
function addPlot(){
plotArea.ox = 0;
plotArea.oy = 0;
plotArea.count = 0;
plotArea.unit = 1000;//parseInt(prompt("Enter unit of time(in milli seconds)"));
plotArea.specificTimeX = 0;
}
StopWatch = function()
{
this.StartMilliseconds = 0;
this.ElapsedMilliseconds = 0;
}
StopWatch.prototype.Start = function()
{
this.StartMilliseconds = new Date().getTime();
}
StopWatch.prototype.Stop = function()
{
this.ElapsedMilliseconds = new Date().getTime() - this.StartMilliseconds;
}
function startPlot(){
plotArea.stopWatch.Start();
for(var i=0;i<globalScope.Flag.length;i++) {
globalScope.Flag[i].plotValues = [[0, globalScope.Flag[i].inp1.value]];
globalScope.Flag[i].cachedIndex= 0;
}
// play();
plotArea.scroll=1;
addPlot();
}
var plotArea = {
ox : 0,
oy : 0,
unit : 0,
c : document.getElementById("plotArea"),
count:0,
specificTimeX:0,
scale : 1,
pixel : 100,
startTime : new Date(),
endTime : new Date(),
scroll : 1,
setup:function(){
this.stopWatch =new StopWatch()
this.stopWatch.Start();
this.ctx=this.c.getContext("2d");
startPlot();
this.timeOutPlot = setInterval(function(){
plotArea.plot();
},100);
},
plot : function()
{
if(globalScope.Flag.length==0){
this.c.width=this.c.height=0;
return;
}
this.stopWatch.Stop();
var time=this.stopWatch.ElapsedMilliseconds;
this.c.width = document.getElementById("simulationArea").clientWidth; //innerWidth;
this.c.height=globalScope.Flag.length*30+40;
// if(document.getElementById("plot").style.height!=this.c.height+"px"){
document.getElementById("plot").style.height = Math.min(this.c.height,400) ;
document.getElementById("plot").style.width = this.c.width ;
// }
// else if(document.getElementById("plot").style.width!=this.c.width+"px"){
// document.getElementById("plot").style.height = this.c.height ;
// document.getElementById("plot").style.width = this.c.width ;
// }
if(this.scroll){
this.ox = Math.max(0,(time/this.unit)*this.pixel -this.c.width+70);
}
else if(!plotArea.mouseDown){
this.ox-=plotArea.scrollAcc;
plotArea.scrollAcc*=0.95;
if(this.ox<0)plotArea.scrollAcc=-0.2+0.2*this.ox;
if(Math.abs(this.ox)<0.5)this.ox=0;
}
var ctx = this.ctx;
this.clear(ctx);
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, this.c.width, this.c.height);
var unit= (this.pixel/(plotArea.unit*plotArea.scale))
ctx.strokeStyle = 'cyan';
ctx.lineWidth = 2;
for(var i=0;i<globalScope.Flag.length;i++)
{
var arr=globalScope.Flag[i].plotValues;
var j=0;
// var start=arr[j][0];
if(globalScope.Flag[i].cachedIndex)
j=globalScope.Flag[i].cachedIndex;
while(j<arr.length && 80+(arr[j][0]*unit)-plotArea.ox<0){
// console.log("HIT2");
j++;
// start=
}
while(j<arr.length && j>0 && 80+(arr[j][0]*unit)-plotArea.ox>0 ){
// console.log("HIT1");
j--;
// start=
}
if(j)j--;
globalScope.Flag[i].cachedIndex=j;
for( ;j<arr.length;j++)
{
var start=arr[j][0];
if(j+1==arr.length)
var end=time;
else
var end=arr[j+1][0];
if(start<=time)
{
if(globalScope.Flag[i].bitWidth==1)
{
if(arr[j][1]!==undefined){
if(ctx.strokeStyle=="#000000"){
ctx.stroke()
ctx.beginPath();
ctx.lineWidth=2;
// ctx.moveTo(80+(start*unit)-plotArea.ox,2*(30+i*15-yOff)-plotArea.oy)
ctx.strokeStyle="cyan";
}
var yOff=5*arr[j][1];
}
else{
ctx.stroke();
ctx.beginPath();
// ctx.moveTo(80+(start*unit)-plotArea.ox,2*(30+i*15-yOff)-plotArea.oy);
ctx.lineWidth=12;
ctx.strokeStyle = '#000000'; // DIABLED Z STATE FOR NOW
yOff=2.5;
}
ctx.lineTo(80+(start*unit)-plotArea.ox,2*(30+i*15-yOff)-plotArea.oy);
ctx.lineTo(80+(end*unit)-plotArea.ox,2*(30+i*15-yOff)-plotArea.oy);
}
else {
ctx.beginPath();
ctx.moveTo(80+(end*unit)-plotArea.ox,55+30*i-plotArea.oy);
ctx.lineTo(80+(end*unit)-5-plotArea.ox,2*(25+i*15)-plotArea.oy);
ctx.lineTo(80+(start*unit)+5-plotArea.ox,2*(25+i*15)-plotArea.oy);
ctx.lineTo(80+(start*unit)-plotArea.ox,55+30*i-plotArea.oy);
ctx.lineTo(80+(start*unit)+5-plotArea.ox,2*(30+i*15)-plotArea.oy);
ctx.lineTo(80+(end*unit)-5-plotArea.ox,2*(30+i*15)-plotArea.oy);
ctx.lineTo(80+(end*unit)-plotArea.ox,55+30*i-plotArea.oy);
mid = 80+((end+start)/Math.round(plotArea.unit*plotArea.scale))*this.pixel/2;
ctx.font="12px Georgia";
ctx.fillStyle = 'white';
if((start*unit)+10-plotArea.ox <=0 && (end*unit)+10-plotArea.ox >=0){
mid = 80+((end-3000)/Math.round(plotArea.unit*plotArea.scale))*this.pixel;
}
ctx.fillText(arr[j][1].toString()||"x",mid-plotArea.ox,57+30*i-plotArea.oy);
ctx.stroke();
}
}
else {
break;
}
if(80+(end*unit)-plotArea.ox>this.c.width)break;
}
ctx.stroke();
ctx.beginPath();
}
// 2 rectangles showing the time and labels
ctx.fillStyle = '#eee';
ctx.fillRect(0, 0, this.c.width, 30);
ctx.font="14px Georgia";
ctx.fillStyle = 'black';
for(var i=1; i*Math.round(plotArea.unit*plotArea.scale)<=time + Math.round(plotArea.unit*plotArea.scale) ;i++)
{
ctx.fillText(Math.round(plotArea.unit*plotArea.scale)*i/1000+"s",48+this.pixel*i-plotArea.ox,20);
}
ctx.fillStyle = '#eee';
ctx.fillRect(0,0,75,this.c.height);
ctx.font="15px Georgia";
ctx.fillStyle = 'black';
for(var i=0;i<globalScope.Flag.length;i++){
ctx.fillText(globalScope.Flag[i].identifier,5,2*(25+i*15) - plotArea.oy);
ctx.fillRect(0,2*(13+i*15)+4 - plotArea.oy, 75, 3);
}
ctx.fillStyle = '#eee';
ctx.fillRect(0,0,75,30);
ctx.fillStyle = 'black';
ctx.font="16px Georgia";
ctx.fillText("Time",10,20);
ctx.strokeStyle = 'black';
ctx.moveTo(0,25);
ctx.lineTo(75,25);
// for yellow line to show specific time
var specificTime = (plotArea.specificTimeX+plotArea.ox-80)*Math.round(plotArea.unit*plotArea.scale)/(this.pixel);;
// ctx.strokeStyle = 'white';
// ctx.moveTo(plotArea.specificTimeX,0);
// ctx.lineTo(plotArea.specificTimeX,plotArea.c.height);
// ctx.stroke();
if(1.115*plotArea.specificTimeX >= 80){
ctx.fillStyle = 'black';
ctx.fillRect(plotArea.specificTimeX - 35, 0, 70, 30);
ctx.fillStyle = 'red';
ctx.fillRect(plotArea.specificTimeX - 30, 2, 60, 26);
ctx.font="12px Georgia";
ctx.fillStyle = 'black';
ctx.fillText(Math.round(specificTime)+"ms",plotArea.specificTimeX - 25, 20);
}
// borders
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(0,this.c.height);
// ctx.fillRect(0, 0, 3, this.c.height);
ctx.moveTo(74,0);
ctx.lineTo(74,this.c.height);
// ctx.fillRect(74, 0, 3, this.c.height);
ctx.moveTo(0,0);
ctx.lineTo(this.c.width,0);
// ctx.fillRect(0, 0, this.c.width, 3);
// ctx.moveTo(0,27);
// ctx.lineTo(this.c.width,27);
// ctx.fillRect(0, 27, this.c.width, 3);
ctx.stroke();
},
clear: function(ctx){
ctx.clearRect(0,0,plotArea.c.width,plotArea.c.height);
// clearInterval(timeOutPlot);
}
}
document.getElementById("plotArea").addEventListener('mousedown', function(e) {
var rect = plotArea.c.getBoundingClientRect();
var x=e.clientX - rect.left;
plotArea.scrollAcc=0;
if(e.shiftKey){
plotArea.specificTimeX = x;
}
else{
plotArea.scroll=0;
plotArea.mouseDown=true;
plotArea.prevX=x;
console.log("HIT");
}
});
document.getElementById("plotArea").addEventListener('mouseup', function(e) {
plotArea.mouseDown=false;
});
document.getElementById("plotArea").addEventListener('mousemove', function(e) {
var rect = plotArea.c.getBoundingClientRect();
var x=e.clientX - rect.left;
if(!e.shiftKey&&plotArea.mouseDown){
plotArea.ox-=x-plotArea.prevX;
plotArea.scrollAcc=x-plotArea.prevX;
plotArea.prevX=x;
// plotArea.ox=Math.max(0,plotArea.ox)
}
else{
plotArea.mouseDown=false;
}
});

218
public/js/Quin_McCluskey.js Normal file
View file

@ -0,0 +1,218 @@
// Algorithm used for Combinational Analysis
function BooleanMinimize(numVarsArg, minTermsArg, dontCaresArg = []) {
var __result;
Object.defineProperties(
this, {
'minTerms': {
value: minTermsArg,
enumerable: false,
writable: false,
configurable: true
},
'dontCares': {
value: dontCaresArg,
enumerable: false,
writable: false,
configurable: true
},
'numVars': {
value: numVarsArg,
enumerable: false,
writable: false,
configurable: true
},
'result': {
enumerable: true,
configurable: true,
get: function() {
if (__result === undefined) {
__result = BooleanMinimize.prototype.solve.call(this);
}
return __result;
},
set: function() {
throw new Error("result cannot be assigned a value");
}
}
}
)
}
BooleanMinimize.prototype.solve = function() {
function dec_to_binary_string(n) {
var str = n.toString(2);
while (str.length != this.numVars) {
str = '0' + str;
}
return str;
};
function num_set_bits(s) {
var ans = 0;
for (let i = 0; i < s.length; ++i)
if (s[i] === '1') ans++;
return ans;
};
function get_prime_implicants(allTerms) {
var table = [];
var primeImplicants = new Set();
var reduced;
while (1) {
for (let i = 0; i <= this.numVars; ++i) table[i] = new Set();
for (let i = 0; i < allTerms.length; ++i) table[num_set_bits(allTerms[i])].add(allTerms[i]);
allTerms = [];
reduced = new Set();
for (let i = 0; i < table.length - 1; ++i) {
for (let str1 of table[i]) {
for (let str2 of table[i + 1]) {
let diff = -1;
for (let j = 0; j < this.numVars; ++j) {
if (str1[j] != str2[j]) {
if (diff === -1) {
diff = j;
} else {
diff = -1;
break;
}
}
}
if (diff !== -1) {
allTerms.push(str1.slice(0, diff) + '-' + str1.slice(diff + 1));
reduced.add(str1);
reduced.add(str2);
}
}
}
}
for (let t of table) {
for (let str of t) {
if (!(reduced.has(str))) primeImplicants.add(str);
}
}
if (!reduced.size) break;
}
return primeImplicants;
};
function get_essential_prime_implicants(primeImplicants, minTerms) {
var table = [],
column;
function check_if_similar(minTerm, primeImplicant) {
for (let i = 0; i < primeImplicant.length; ++i) {
if (primeImplicant[i] !== '-' && (minTerm[i] !== primeImplicant[i])) return false;
}
return true;
}
function get_complexity(terms) {
var complexity = terms.length;
for (let t of terms) {
for (let i = 0; i < t.length; ++i) {
if (t[i] !== '-') {
complexity++;
if (t[i] === '0') complexity++;
}
}
}
return complexity;
}
function isSubset(sub, sup) {
for (let i of sub) {
if (!(sup.has(i))) return false;
}
return true;
}
for (let m of minTerms) {
column = [];
for (let i = 0; i < primeImplicants.length; ++i) {
if (check_if_similar(m, primeImplicants[i])) {
column.push(i);
}
}
table.push(column);
}
var possibleSets = [],
tempSets;
for (let i of table[0]) {
possibleSets.push(new Set([i]));
}
for (let i = 1; i < table.length; ++i) {
tempSets = [];
for (let s of possibleSets) {
for (let p of table[i]) {
let x = new Set(s);
x.add(p);
let append = true;
for (let j = tempSets.length - 1; j >= 0; --j) {
if (isSubset(x, tempSets[j])) {
tempSets.splice(j, 1);
} else {
append = false;
}
}
if (append) {
tempSets.push(x);
}
}
possibleSets = tempSets;
}
}
var essentialImplicants, minComplexity = 1e9;
for (let s of possibleSets) {
let p = [];
for (let i of s) {
p.push(primeImplicants[i]);
}
let comp = get_complexity(p);
if (comp < minComplexity) {
essentialImplicants = p;
minComplexity = comp;
}
}
return essentialImplicants;
};
var minTerms = this.minTerms.map(dec_to_binary_string.bind(this));
var dontCares = this.dontCares.map(dec_to_binary_string.bind(this));
return get_essential_prime_implicants.call(
this,
Array.from(get_prime_implicants.call(this, minTerms.concat(dontCares))),
minTerms
);
};

299
public/js/RGBLedMatrix.js Normal file
View file

@ -0,0 +1,299 @@
function RGBLedMatrix(
x,
y,
scope = globalScope,
{
rows = 8,
columns = 8,
ledSize = 2,
showGrid = true,
colors = [],
} = {},
) {
CircuitElement.call(this, x, y, scope, 'RIGHT', 8);
this.fixedBitWidth = true;
this.directionFixed = true;
this.rectangleObject = true;
this.alwaysResolve = true;
this.labelDirection = 'UP';
this.leftDimensionX = 0;
this.upDimensionY = 0;
// These pins provide bulk-editing of the colors
this.rowEnableNodes = []; // 1-bit pin for each row, on the left side.
this.columnEnableNodes = []; // 1-bit pin for each column, on the bottom.
this.columnColorNodes = []; // 24-bit pin for each column, on the top.
// These pins provide single-pixel editing; these are on the right side.
this.colorNode = new Node(0, -10, NODE_INPUT, this, 24, 'COLOR');
this.rowNode = new Node(0, 0, NODE_INPUT, this, 1, 'ROW');
this.columnNode = new Node(0, 10, NODE_INPUT, this, 1, 'COLUMN');
this.colors = colors;
this.showGrid = showGrid;
this.changeSize(rows, columns, ledSize, false);
}
RGBLedMatrix.prototype = Object.create(CircuitElement.prototype);
RGBLedMatrix.prototype.constructor = RGBLedMatrix;
RGBLedMatrix.prototype.tooltipText = 'RGB Led Matrix';
// Limit the size of the matrix otherwise the simulation starts to lag.
RGBLedMatrix.prototype.maxRows = 128;
RGBLedMatrix.prototype.maxColumns = 128;
// Let the user choose between 3 sizes of LEDs: small, medium and large.
RGBLedMatrix.prototype.maxLedSize = 3;
RGBLedMatrix.prototype.mutableProperties = {
rows: {
name: 'Rows',
type: 'number',
max: RGBLedMatrix.prototype.maxRows,
min: 1,
func: 'changeRows',
},
columns: {
name: 'Columns',
type: 'number',
max: RGBLedMatrix.prototype.maxColumns,
min: 1,
func: 'changeColumns',
},
ledSize: {
name: 'LED Size',
type: 'number',
max: RGBLedMatrix.prototype.maxLedSize,
min: 1,
func: 'changeLedSize',
},
showGrid: {
name: 'Toggle Grid',
type: 'button',
max: RGBLedMatrix.prototype.maxLedSize,
min: 1,
func: 'toggleGrid',
},
};
RGBLedMatrix.prototype.toggleGrid = function () {
this.showGrid = !this.showGrid;
};
RGBLedMatrix.prototype.changeRows = function (rows) {
this.changeSize(rows, this.columns, this.ledSize, true);
};
RGBLedMatrix.prototype.changeColumns = function (columns) {
this.changeSize(this.rows, columns, this.ledSize, true);
};
RGBLedMatrix.prototype.changeLedSize = function (ledSize) {
this.changeSize(this.rows, this.columns, ledSize, true);
};
RGBLedMatrix.prototype.changeSize = function (rows, columns, ledSize, move) {
rows = parseInt(rows, 10);
if (isNaN(rows) || rows < 0 || rows > this.maxRows) return;
columns = parseInt(columns, 10);
if (isNaN(columns) || columns < 0 || columns > this.maxColumns) return;
ledSize = parseInt(ledSize, 10);
if (isNaN(ledSize) || ledSize < 0 || ledSize > this.maxLedSize) return;
// The size of an individual LED, in canvas units.
var ledWidth = 10 * ledSize;
var ledHeight = 10 * ledSize;
// The size of the LED matrix, in canvas units.
var gridWidth = ledWidth * columns;
var gridHeight = ledHeight * rows;
// We need to position the element in the 10x10 grid.
// Depending on the size of the leds we need to add different paddings so position correctly.
var padding = ledSize % 2 ? 5 : 10;
// The dimensions of the element, in canvas units.
var halfWidth = gridWidth / 2 + padding;
var halfHeight = gridHeight / 2 + padding;
// Move the element in order to keep the position of the nodes stable so wires don't break.
if (move) {
this.x -= this.leftDimensionX - halfWidth;
this.y -= this.upDimensionY - halfHeight;
}
// Update the dimensions of the element.
this.setDimensions(halfWidth, halfHeight);
// Offset of the nodes in relation to the element's center.
var nodePadding = [10, 20, 20][ledSize - 1];
var nodeOffsetX = nodePadding - halfWidth;
var nodeOffsetY = nodePadding - halfHeight;
// When the led size changes it is better to delete all nodes to break connected the wires.
// Otherwise, wires can end up connected in unexpected ways.
var resetAllNodes = ledSize != this.ledSize;
// Delete unused row-enable nodes, reposition remaining nodes and add new nodes.
this.rowEnableNodes.splice(resetAllNodes ? 0 : rows).forEach(node => node.delete());
this.rowEnableNodes.forEach((node, i) => {
node.x = node.leftx = -halfWidth;
node.y = node.lefty = i * ledHeight + nodeOffsetY;
});
while (this.rowEnableNodes.length < rows) {
this.rowEnableNodes.push(new Node(-halfWidth, this.rowEnableNodes.length * ledHeight + nodeOffsetY, NODE_INPUT, this, 1, 'R' + this.rowEnableNodes.length));
}
// Delete unused column-enable nodes, reposition remaining nodes and add new nodes.
this.columnEnableNodes.splice(resetAllNodes ? 0 : columns).forEach(node => node.delete());
this.columnEnableNodes.forEach((node, i) => {
node.x = node.leftx = i * ledWidth + nodeOffsetX;
node.y = node.lefty = halfHeight;
});
while (this.columnEnableNodes.length < columns) {
this.columnEnableNodes.push(new Node(this.columnEnableNodes.length * ledWidth + nodeOffsetX, halfHeight, NODE_INPUT, this, 1, 'C' + this.columnEnableNodes.length));
}
// Delete unused column color nodes, reposition remaining nodes and add new nodes.
this.columnColorNodes.splice(resetAllNodes ? 0 : columns).forEach(node => node.delete());
this.columnColorNodes.forEach((node, i) => {
node.x = node.leftx = i * ledWidth + nodeOffsetX;
node.y = node.lefty = -halfHeight;
});
while (this.columnColorNodes.length < columns) {
this.columnColorNodes.push(new Node(this.columnColorNodes.length * ledWidth + nodeOffsetX, -halfHeight, NODE_INPUT, this, 24, 'CLR' + this.columnColorNodes.length));
}
// Delete unused color storage and add storage for new rows.
this.colors.splice(rows);
this.colors.forEach(c => c.splice(columns));
while (this.colors.length < rows) {
this.colors.push([]);
}
// Reposition the single-pixel nodes
this.rowNode.bitWidth = Math.ceil(Math.log2(rows));
this.rowNode.label = 'ROW (' + this.rowNode.bitWidth + ' bits)';
this.columnNode.bitWidth = Math.ceil(Math.log2(columns));
this.columnNode.label = 'COLUMN (' + this.columnNode.bitWidth + ' bits)';
var singlePixelNodePadding = rows > 1 ? nodeOffsetY : nodeOffsetY - 10;
var singlePixelNodeDistance = (rows <= 2) ? 10 : ledHeight;
[this.colorNode, this.rowNode, this.columnNode].forEach((node, i) => {
node.x = node.leftx = halfWidth;
node.y = node.lefty = i * singlePixelNodeDistance + singlePixelNodePadding;
});
// Store the new values
this.rows = rows;
this.columns = columns;
this.ledSize = ledSize;
return this;
};
RGBLedMatrix.prototype.customSave = function () {
// Save the size of the LED matrix.
// Unlike a read LED matrix, we also persist the color of each pixel.
// This allows circuit preview to show the colors at the time the simulation was saved.
return {
constructorParamaters: [{
rows: this.rows,
columns: this.columns,
ledSize: this.ledSize,
showGrid: this.showGrid,
colors: this.colors
}],
nodes: {
rowEnableNodes: this.rowEnableNodes.map(findNode),
columnEnableNodes: this.columnEnableNodes.map(findNode),
columnColorNodes: this.columnColorNodes.map(findNode),
colorNode: findNode(this.colorNode),
rowNode: findNode(this.rowNode),
columnNode: findNode(this.columnNode),
},
}
};
RGBLedMatrix.prototype.resolve = function () {
var colorValue = this.colorNode.value;
var hasColorValue = colorValue != undefined;
var rows = this.rows;
var columns = this.columns;
var rowEnableNodes = this.rowEnableNodes;
var columnEnableNodes = this.columnEnableNodes;
var columnColorNodes = this.columnColorNodes;
var colors = this.colors;
for (var row = 0; row < rows; row++) {
if (rowEnableNodes[row].value === 1) {
for (var column = 0; column < columns; column++) {
// Method 1: set pixel by rowEnable + columnColor pins
var columnColor = columnColorNodes[column].value;
if (columnColor !== undefined) {
colors[row][column] = columnColor;
}
// Method 2: set pixel by rowEnable + columnEnable + color pins
if (hasColorValue && columnEnableNodes[column].value === 1) {
colors[row][column] = colorValue;
}
}
}
}
// Method 3: set pixel by write + pixel index + color pins.
var hasRowNodeValue = this.rowNode.value != undefined || rows == 1;
var hasColumnNodeValue = this.columnNode.value != undefined || columns == 1;
if (hasColorValue && hasRowNodeValue && hasColumnNodeValue) {
var rowNodeValue = this.rowNode.value || 0;
var columnNodeValue = this.columnNode.value || 0;
if (rowNodeValue < rows && columnNodeValue < columns) {
colors[rowNodeValue][columnNodeValue] = colorValue;
}
}
};
RGBLedMatrix.prototype.customDraw = function () {
var ctx = simulationArea.context;
var rows = this.rows;
var columns = this.columns;
var colors = this.colors;
var xx = this.x;
var yy = this.y;
var dir = this.direction;
var ledWidth = 10 * this.ledSize;
var ledHeight = 10 * this.ledSize;
var top = this.rowEnableNodes[0].y - ledHeight / 2;
var left = this.columnColorNodes[0].x - ledWidth / 2;
var width = this.columns * ledWidth;
var height = this.rows * ledHeight;
var bottom = top + height;
var right = left + width;
var [w, h] = rotate(ledWidth * globalScope.scale, ledHeight * globalScope.scale, dir);
var xoffset = Math.round(globalScope.ox + xx * globalScope.scale);
var yoffset = Math.round(globalScope.oy + yy * globalScope.scale);
for (var row = 0; row < rows; row++) {
for (var column = 0; column < columns; column++) {
var color = colors[row][column] || 0;
ctx.beginPath();
ctx.fillStyle = 'rgb(' + ((color & 0xFF0000) >> 16) + ',' + ((color & 0xFF00) >> 8) + ',' + (color & 0xFF) + ')';
[x1, y1] = rotate(left + column * ledWidth, top + row * ledHeight, dir);
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
ctx.rect(xoffset + x1, yoffset + y1, w, h);
ctx.fill();
}
}
if (this.showGrid) {
ctx.beginPath();
ctx.strokeStyle = '#323232';
ctx.lineWidth = correctWidth(1);
rect2(ctx, left, top, width, height, xx, yy, dir);
for (var x = left + ledWidth; x < right; x += ledWidth) {
moveTo(ctx, x, top, xx, yy, dir);
lineTo(ctx, x, bottom, xx, yy, dir);
}
for (var y = top + ledHeight; y < bottom; y += ledHeight) {
moveTo(ctx, left, y, xx, yy, dir);
lineTo(ctx, right, y, xx, yy, dir);
}
ctx.stroke();
}
};

3
public/js/SAP_DATA.js Normal file

File diff suppressed because one or more lines are too long

328
public/js/UX.js Normal file
View file

@ -0,0 +1,328 @@
var smartDropXX = 50;
var smartDropYY = 80;
// Object stores the position of context menu
var ctxPos = {
x: 0,
y: 0,
visible: false,
};
// Function hides the context menu
function hideContextMenu() {
var el = document.getElementById('contextMenu');
el.style = 'opacity:0;';
setTimeout(() => {
el.style = 'visibility:hidden;';
ctxPos.visible = false;
}, 200); // Hide after 2 sec
}
// Function displays context menu
function showContextMenu() {
if (layoutMode) return false; // Hide context menu when it is in Layout Mode
$('#contextMenu').css({
visibility: 'visible',
opacity: 1,
top: `${ctxPos.y}px`,
left: `${ctxPos.x}px`,
});
ctxPos.visible = true;
return false;
}
// Function is called when context item is clicked
// eslint-disable-next-line no-unused-vars
function menuItemClicked(id) {
hideContextMenu();
if (id === 0) {
document.execCommand('copy');
} else if (id === 1) {
document.execCommand('cut');
} else if (id === 2) {
// document.execCommand('paste'); it is restricted to sove this problem we use dataPasted variable
paste(localStorage.getItem('clipboardData'));
} else if (id === 3) {
delete_selected();
} else if (id === 4) {
undo();
undo();
} else if (id === 5) {
newCircuit();
} else if (id === 6) {
createSubCircuitPrompt();
} else if (id === 7) {
globalScope.centerFocus(false);
}
}
function setupUI() {
var ctxEl = document.getElementById('contextMenu');
document.addEventListener('mousedown', (e) => {
// Check if mouse is not inside the context menu and menu is visible
if (!((e.clientX >= ctxPos.x && e.clientX <= ctxPos.x + ctxEl.offsetWidth)
&& (e.clientY >= ctxPos.y && e.clientY <= ctxPos.y + ctxEl.offsetHeight))
&& (ctxPos.visible && e.which !== 3)) {
hideContextMenu();
}
// Change the position of context whenever mouse is clicked
ctxPos.x = e.clientX;
ctxPos.y = e.clientY;
});
document.getElementById('canvasArea').oncontextmenu = showContextMenu;
$("#sideBar").resizable({
handles: 'e',
// minWidth:270,
});
$("#menu").accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
// $( "#plot" ).resizable({
// handles: 'n',
// // minHeight:200,
// });
$('.logixModules').mousedown(function () {
//////console.log(smartDropXX,smartDropYY);
if (simulationArea.lastSelected && simulationArea.lastSelected.newElement) simulationArea.lastSelected.delete();
var obj = new window[this.id](); //(simulationArea.mouseX,simulationArea.mouseY);
simulationArea.lastSelected = obj;
// simulationArea.lastSelected=obj;
// simulationArea.mouseDown=true;
smartDropXX += 70;
if (smartDropXX / globalScope.scale > width) {
smartDropXX = 50;
smartDropYY += 80;
}
});
$('.logixButton').click(function () {
window[this.id]();
});
// var dummyCounter=0;
$('.logixModules').hover(function () {
// Tooltip can be statically defined in the prototype.
var tooltipText = window[this.id].prototype.tooltipText;
if (!tooltipText) return;
$("#Help").addClass("show");
$("#Help").empty();
////console.log("SHOWING")
$("#Help").append(tooltipText);
}); // code goes in document ready fn only
$('.logixModules').mouseleave(function () {
$("#Help").removeClass("show");
}); // code goes in document ready fn only
// $('#saveAsImg').click(function(){
// saveAsImg();
// });
// $('#Save').click(function(){
// Save();
// });
// $('#moduleProperty').draggable();
}
var prevPropertyObj = undefined;
function showProperties(obj) {
if (obj == prevPropertyObj) return;
hideProperties();
prevPropertyObj = obj;
if (simulationArea.lastSelected === undefined || ["Wire", "CircuitElement", "Node"].indexOf(simulationArea.lastSelected.objectType) !== -1) {
$('#moduleProperty').show();
$('#moduleProperty-inner').append("<div id='moduleProperty-header'>" + "Project Properties" + "</div>");
$('#moduleProperty-inner').append("<p>Project : <input class='objectPropertyAttribute' type='text' name='setProjectName' value='" + (projectName || "Untitled") + "'></p>");
$('#moduleProperty-inner').append("<p>Circuit : <input class='objectPropertyAttribute' type='text' name='changeCircuitName' value='" + (globalScope.name || "Untitled") + "'></p>");
$('#moduleProperty-inner').append("<p>Clock Time : <input class='objectPropertyAttribute' min='50' type='number' style='width:100px' step='10' name='changeClockTime' value='" + (simulationArea.timePeriod) + "'>ms</p>");
$('#moduleProperty-inner').append("<p>Clock Enabled : <label class='switch'> <input type='checkbox' " + ["", "checked"][simulationArea.clockEnabled + 0] + " class='objectPropertyAttributeChecked' name='changeClockEnable' > <span class='slider'></span> </label></p>");
$('#moduleProperty-inner').append("<p>Lite Mode : <label class='switch'> <input type='checkbox' " + ["", "checked"][lightMode + 0] + " class='objectPropertyAttributeChecked' name='changeLightMode' > <span class='slider'></span> </label></p>");
// $('#moduleProperty-inner').append("<p> ");
$('#moduleProperty-inner').append("<p><button type='button' class='objectPropertyAttributeChecked btn btn-danger btn-xs' name='deleteCurrentCircuit' >Delete Circuit</button> <button type='button' class='objectPropertyAttributeChecked btn btn-primary btn-xs' name='toggleLayoutMode' >Edit Layout</button> </p>");
} else {
$('#moduleProperty').show();
$('#moduleProperty-inner').append("<div id='moduleProperty-header'>" + obj.objectType + "</div>");
// $('#moduleProperty').append("<input type='range' name='points' min='1' max='32' value="+obj.bitWidth+">");
if (!obj.fixedBitWidth)
$('#moduleProperty-inner').append("<p>BitWidth: <input class='objectPropertyAttribute' type='number' name='newBitWidth' min='1' max='32' value=" + obj.bitWidth + "></p>");
if (obj.changeInputSize)
$('#moduleProperty-inner').append("<p>Input Size: <input class='objectPropertyAttribute' type='number' name='changeInputSize' min='2' max='10' value=" + obj.inputSize + "></p>");
if (!obj.propagationDelayFixed)
$('#moduleProperty-inner').append("<p>Delay: <input class='objectPropertyAttribute' type='number' name='changePropagationDelay' min='0' max='100000' value=" + obj.propagationDelay + "></p>");
$('#moduleProperty-inner').append("<p>Label: <input class='objectPropertyAttribute' type='text' name='setLabel' value='" + escapeHtml(obj.label) + "'></p>");
if (!obj.labelDirectionFixed) {
var s = $("<select class='objectPropertyAttribute' name='newLabelDirection'>" + "<option value='RIGHT' " + ["", "selected"][+(obj.labelDirection == "RIGHT")] + " >RIGHT</option><option value='DOWN' " + ["", "selected"][+(obj.labelDirection == "DOWN")] + " >DOWN</option><option value='LEFT' " + "<option value='RIGHT'" + ["", "selected"][+(obj.labelDirection == "LEFT")] + " >LEFT</option><option value='UP' " + "<option value='RIGHT'" + ["", "selected"][+(obj.labelDirection == "UP")] + " >UP</option>" + "</select>");
s.val(obj.labelDirection);
$('#moduleProperty-inner').append("<p>Label Direction: " + $(s).prop('outerHTML') + "</p>");
}
if (!obj.directionFixed) {
var s = $("<select class='objectPropertyAttribute' name='newDirection'>" + "<option value='RIGHT' " + ["", "selected"][+(obj.direction == "RIGHT")] + " >RIGHT</option><option value='DOWN' " + ["", "selected"][+(obj.direction == "DOWN")] + " >DOWN</option><option value='LEFT' " + "<option value='RIGHT'" + ["", "selected"][+(obj.direction == "LEFT")] + " >LEFT</option><option value='UP' " + "<option value='RIGHT'" + ["", "selected"][+(obj.direction == "UP")] + " >UP</option>" + "</select>");
$('#moduleProperty-inner').append("<p>Direction: " + $(s).prop('outerHTML') + "</p>");
} else if (!obj.orientationFixed) {
var s = $("<select class='objectPropertyAttribute' name='newDirection'>" + "<option value='RIGHT' " + ["", "selected"][+(obj.direction == "RIGHT")] + " >RIGHT</option><option value='DOWN' " + ["", "selected"][+(obj.direction == "DOWN")] + " >DOWN</option><option value='LEFT' " + "<option value='RIGHT'" + ["", "selected"][+(obj.direction == "LEFT")] + " >LEFT</option><option value='UP' " + "<option value='RIGHT'" + ["", "selected"][+(obj.direction == "UP")] + " >UP</option>" + "</select>");
$('#moduleProperty-inner').append("<p>Orientation: " + $(s).prop('outerHTML') + "</p>");
}
if (obj.mutableProperties) {
for (attr in obj.mutableProperties) {
var prop = obj.mutableProperties[attr];
if (obj.mutableProperties[attr].type == "number") {
var s = "<p>" + prop.name + "<input class='objectPropertyAttribute' type='number' name='" + prop.func + "' min='" + (prop.min || 0) + "' max='" + (prop.max || 200) + "' value=" + obj[attr] + "></p>";
$('#moduleProperty-inner').append(s);
}
else if (obj.mutableProperties[attr].type == "text") {
var s = "<p>" + prop.name + "<input class='objectPropertyAttribute' type='text' name='" + prop.func + "' maxlength='" + (prop.maxlength || 200) + "' value=" + obj[attr] + "></p>";
$('#moduleProperty-inner').append(s);
}
else if (obj.mutableProperties[attr].type == "button") {
var s = "<p><button class='objectPropertyAttribute btn btn-primary btn-xs' type='button' name='" + prop.func + "'>" + prop.name + "</button></p>";
$('#moduleProperty-inner').append(s);
}
}
}
}
var helplink = obj && (obj.helplink);
if (helplink) {
$('#moduleProperty-inner').append('<p><button id="HelpButton" class="btn btn-primary btn-xs" type="button" >Help &#9432</button></p>');
$('#HelpButton').click(function () {
window.open(helplink);
});
}
function checkValidBitWidth() {
const selector = $("[name='newBitWidth']");
if (selector == undefined
|| selector.val() > 32
|| selector.val() < 1
|| !$.isNumeric(selector.val())) {
// fallback to previously saves state
selector.val(selector.attr("old-val"));
} else {
selector.attr("old-val", selector.val());
}
}
$(".objectPropertyAttribute").on("change keyup paste click", function () {
// return;
//////console.log(this.name+":"+this.value);
checkValidBitWidth()
scheduleUpdate();
updateCanvas = true;
wireToBeChecked = 1;
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name])
prevPropertyObj = simulationArea.lastSelected[this.name](this.value) || prevPropertyObj;
else
window[this.name](this.value);
})
$(".objectPropertyAttributeChecked").on("change keyup paste click", function () {
// return;
//////console.log(this.name+":"+this.value);
scheduleUpdate();
updateCanvas = true;
wireToBeChecked = 1;
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name])
prevPropertyObj = simulationArea.lastSelected[this.name](this.value) || prevPropertyObj;
else
window[this.name](this.checked);
})
}
function hideProperties() {
$('#moduleProperty-inner').empty();
$('#moduleProperty').hide();
prevPropertyObj = undefined;
$(".objectPropertyAttribute").unbind("change keyup paste click");
}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
$('#bitconverterprompt').append(`
<label style='color:grey'>Decimal value</label><br><input type='text' id='decimalInput' label="Decimal" name='text1'><br><br>
<label style='color:grey'>Binary value</label><br><input type='text' id='binaryInput' label="Binary" name='text1'><br><br>
<label style='color:grey'>Octal value</label><br><input type='text' id='octalInput' label="Octal" name='text1'><br><br>
<label style='color:grey'>Hexadecimal value</label><br><input type='text' id='hexInput' label="Hex" name='text1'><br><br>
`);
$('#bitconverter').click(function () {
$('#bitconverterprompt').dialog({
buttons: [
{
text: "Reset",
click: function () {
$("#decimalInput").val("0");
$("#binaryInput").val("0");
$("#octalInput").val("0");
$("#hexInput").val("0");
}
}
]
});
})
// convertors
convertors = {
dec2bin: x => "0b" + x.toString(2),
dec2hex: x => "0x" + x.toString(16),
dec2octal: x => "0" + x.toString(8),
}
function setBaseValues(x) {
if (isNaN(x)) return;
$("#binaryInput").val(convertors.dec2bin(x));
$("#octalInput").val(convertors.dec2octal(x));
$("#hexInput").val(convertors.dec2hex(x));
$("#decimalInput").val(x);
}
$("#decimalInput").on('keyup', function () {
var x = parseInt($("#decimalInput").val(), 10);
setBaseValues(x);
})
$("#binaryInput").on('keyup', function () {
var x = parseInt($("#binaryInput").val(), 2);
setBaseValues(x);
})
$("#hexInput").on('keyup', function () {
var x = parseInt($("#hexInput").val(), 16);
setBaseValues(x);
})
$("#octalInput").on('keyup', function () {
var x = parseInt($("#octalInput").val(), 8);
setBaseValues(x);
})

1214
public/js/canvas2svg.js Normal file

File diff suppressed because it is too large Load diff

418
public/js/canvasApi.js Normal file
View file

@ -0,0 +1,418 @@
// Function used to change the zoom level wrt to a point
// fn to change scale (zoom) - It also shifts origin so that the position
// of the object in focus doesn't change
function changeScale(delta,xx,yy,method=1) {
// method = 3/2 - Zoom wrt center of screen
// method = 1 - Zoom wrt position of mouse
// Otherwise zoom wrt to selected object
if(method==3){
xx =(width/2 - globalScope.ox) / globalScope.scale
yy = (height/2 - globalScope.oy) / globalScope.scale
}
else if(xx===undefined||yy===undefined||xx=='zoomButton'||yy=='zoomButton'){
if (simulationArea.lastSelected && simulationArea.lastSelected.objectType!="Wire") { // selected object
xx = simulationArea.lastSelected.x;
yy = simulationArea.lastSelected.y;
} else { //mouse location
if(method==1){
xx = simulationArea.mouseX;
yy = simulationArea.mouseY;
}
else if(method==2){
xx =(width/2 - globalScope.ox) / globalScope.scale
yy = (height/2 - globalScope.oy) / globalScope.scale
}
}
}
var oldScale = globalScope.scale;
globalScope.scale = Math.max(0.5,Math.min(4*DPR,globalScope.scale+delta));
globalScope.scale = Math.round(globalScope.scale * 10) / 10;
globalScope.ox -= Math.round(xx * (globalScope.scale - oldScale)); // Shift accordingly, so that we zoom wrt to the selected point
globalScope.oy -= Math.round(yy * (globalScope.scale - oldScale));
// dots(true,false);
// MiniMap
if(!embed && !lightMode){
findDimensions(globalScope);
miniMapArea.setup();
$('#miniMap').show();
lastMiniMapShown = new Date().getTime();
$('#miniMap').show();
setTimeout(removeMiniMap,2000);
}
}
//fn to draw Dots on screen
//the function is called only when the zoom level or size of screen changes.
// Otherwise for normal panning, the canvas itself is moved to give the illusion of movement
function dots(dots=true, transparentBackground=false,force=false) {
var scale = unit * globalScope.scale;
var ox = globalScope.ox % scale; //offset
var oy = globalScope.oy % scale; //offset
document.getElementById("backgroundArea").style.left=(ox-scale)/DPR;
document.getElementById("backgroundArea").style.top=(oy-scale)/DPR;
if(globalScope.scale==simulationArea.prevScale && !force)return;
if(!backgroundArea.context)return;
simulationArea.prevScale=globalScope.scale;
var canvasWidth = backgroundArea.canvas.width; //max X distance
var canvasHeight = backgroundArea.canvas.height; //max Y distance
var ctx = backgroundArea.context;
ctx.beginPath();
backgroundArea.clear();
ctx.strokeStyle="#eee";
ctx.lineWidth=1;
if (!transparentBackground) {
ctx.fillStyle = "white";
ctx.rect(0, 0, canvasWidth, canvasHeight);
ctx.fill();
}
var correction=0.5*(ctx.lineWidth%2);
for (var i = 0 ; i < canvasWidth; i += scale){
ctx.moveTo(Math.round(i+correction)-correction,0);
ctx.lineTo(Math.round(i+correction)-correction,canvasHeight);
}
for (var j = 0 ; j < canvasHeight; j += scale){
ctx.moveTo(0,Math.round(j+correction)-correction);
ctx.lineTo(canvasWidth,Math.round(j+correction)-correction);
}
ctx.stroke();
return ;
// Old Code
// function drawPixel(x, y, r, g, b, a) {
// var index = (x + y * canvasWidth) * 4;
// canvasData.data[index + 0] = r;
// canvasData.data[index + 1] = g;
// canvasData.data[index + 2] = b;
// canvasData.data[index + 3] = a;
// }
// if (dots) {
// var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
//
//
//
// for (var i = 0 + ox; i < canvasWidth; i += scale)
// for (var j = 0 + oy; j < canvasHeight; j += scale)
// drawPixel(i, j, 0, 0, 0, 255);
// ctx.putImageData(canvasData, 0, 0);
// }
}
// Helper canvas API starts here
// All canvas functions are wrt to a center point (xx,yy),
// direction is used to abstract rotation of everything by a certain angle
// Possible values for direction = "RIGHT" (default), "LEFT", "UP", "DOWN"
function bezierCurveTo(x1, y1, x2, y2, x3, y3, xx, yy, dir) {
[x1, y1] = rotate(x1, y1, dir);
[x2, y2] = rotate(x2, y2, dir);
[x3, y3] = rotate(x3, y3, dir);
var ox = globalScope.ox;
var oy = globalScope.oy;
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
x3 *= globalScope.scale;
y3 *= globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
ctx.bezierCurveTo(Math.round(xx + ox + x1), Math.round(yy + oy + y1), Math.round(xx + ox + x2), Math.round(yy + oy + y2), Math.round(xx + ox + x3), Math.round(yy + oy + y3));
}
function moveTo(ctx, x1, y1, xx, yy, dir,bypass=false) {
var correction=0.5*(ctx.lineWidth%2);
[newX, newY] = rotate(x1, y1, dir);
newX = newX * globalScope.scale;
newY = newY * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
if(bypass)
ctx.moveTo(xx + globalScope.ox + newX,yy + globalScope.oy + newY);
else
ctx.moveTo(Math.round(xx + globalScope.ox + newX-correction)+correction, Math.round(yy + globalScope.oy + newY-correction)+correction);
}
function lineTo(ctx, x1, y1, xx, yy, dir) {
var correction=0.5*(ctx.lineWidth%2);
[newX, newY] = rotate(x1, y1, dir);
newX = newX * globalScope.scale;
newY = newY * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
ctx.lineTo(Math.round(xx + globalScope.ox + newX-correction)+correction, Math.round(yy + globalScope.oy + newY-correction)+correction);
}
function arc(ctx, sx, sy, radius, start, stop, xx, yy, dir) {
//ox-x of origin, xx- x of element , sx - shift in x from element
var correction=0.5*(ctx.lineWidth%2);
[Sx, Sy] = rotate(sx, sy, dir);
Sx = Sx * globalScope.scale;
Sy = Sy * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
radius *= globalScope.scale;
[newStart, newStop, counterClock] = rotateAngle(start, stop, dir);
ctx.arc(Math.round(xx + globalScope.ox + Sx+correction)-correction, Math.round(yy + globalScope.oy + Sy+correction)-correction, Math.round(radius), newStart, newStop, counterClock);
}
function arc2(ctx, sx, sy, radius, start, stop, xx, yy, dir) {
//ox-x of origin, xx- x of element , sx - shift in x from element
var correction=0.5*(ctx.lineWidth%2);
[Sx, Sy] = rotate(sx, sy, dir);
Sx = Sx * globalScope.scale;
Sy = Sy * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
radius *= globalScope.scale;
[newStart, newStop, counterClock] = rotateAngle(start, stop, dir);
var pi = 0;
if(counterClock)
pi = Math.PI;
ctx.arc(Math.round(xx + globalScope.ox + Sx +correction)-correction, Math.round(yy + globalScope.oy + Sy + correction)-correction, Math.round(radius), newStart + pi, newStop + pi);
}
function drawCircle2(ctx, sx, sy, radius, xx, yy, dir) { //ox-x of origin, xx- x of element , sx - shift in x from element
[Sx, Sy] = rotate(sx, sy, dir);
Sx = Sx * globalScope.scale;
Sy = Sy * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
radius *= globalScope.scale;
ctx.arc(Math.round(xx + globalScope.ox + Sx), Math.round(yy + globalScope.oy + Sy), Math.round(radius), 0, 2*Math.PI);
}
function rect(ctx, x1, y1, x2, y2) {
var correction=0.5*(ctx.lineWidth%2)
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
x2 = x2 * globalScope.scale;
y2 = y2 * globalScope.scale;
ctx.rect(Math.round(globalScope.ox + x1-correction)+correction, Math.round(globalScope.oy + y1-correction)+correction, Math.round(x2),Math.round( y2));
}
function rect2(ctx, x1, y1, x2, y2, xx, yy, dir="RIGHT") {
var correction = 0.5*(ctx.lineWidth%2);
[x1, y1] = rotate(x1, y1, dir);
[x2, y2] = rotate(x2, y2, dir);
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
x2 = x2 * globalScope.scale;
y2 = y2 * globalScope.scale;
xx *= globalScope.scale;
yy *= globalScope.scale;
ctx.rect(Math.round(globalScope.ox + xx + x1-correction)+correction, Math.round(globalScope.oy + yy + y1-correction)+correction, Math.round(x2), Math.round(y2));
}
function rotate(x1, y1, dir) {
if (dir == "LEFT")
return [-x1, y1];
else if (dir == "DOWN")
return [y1, x1];
else if (dir == "UP")
return [y1, -x1];
else
return [x1, y1];
}
function correctWidth(w){
return Math.max(1,Math.round(w*globalScope.scale));
}
function rotateAngle(start, stop, dir) {
if (dir == "LEFT")
return [start, stop, true];
else if (dir == "DOWN")
return [start - Math.PI / 2, stop - Math.PI / 2, true];
else if (dir == "UP")
return [start - Math.PI / 2, stop - Math.PI / 2, false];
else
return [start, stop, false];
}
function drawLine(ctx, x1, y1, x2, y2, color, width) {
x1 *= globalScope.scale;
y1 *= globalScope.scale;
x2 *= globalScope.scale;
y2 *= globalScope.scale;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineCap = "round";
ctx.lineWidth = correctWidth(width);//*globalScope.scale;
var correction = 0.5*(ctx.lineWidth%2);
var hCorrection=0;
var vCorrection=0;
if(y1==y2)vCorrection=correction;
if(x1==x2)hCorrection=correction;
ctx.moveTo(Math.round(x1 + globalScope.ox+hCorrection)-hCorrection, Math.round(y1 + globalScope.oy+vCorrection)-vCorrection);
ctx.lineTo(Math.round(x2 + globalScope.ox+hCorrection)-hCorrection,Math.round(y2 + globalScope.oy+vCorrection)-vCorrection);
ctx.stroke();
}
// Checks if string color is a valid color using a hack
function validColor(color){
var $div = $("<div>");
$div.css("border", "1px solid "+color);
return ($div.css("border-color")!="")
}
// Helper function to color "RED" to RGBA
function colorToRGBA(color) {
var cvs, ctx;
cvs = document.createElement('canvas');
cvs.height = 1;
cvs.width = 1;
ctx = cvs.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
return ctx.getImageData(0, 0, 1, 1).data;
}
function drawCircle(ctx, x1, y1, r, color) {
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(Math.round(x1 + globalScope.ox), Math.round(y1 + globalScope.oy), Math.round(r*globalScope.scale), 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
}
// To show message like values, node name etc
function canvasMessage(ctx,str,x1,y1,fontSize=10){
if(!str||!str.length)return;
ctx.font = Math.round(fontSize * globalScope.scale) + "px Georgia";
ctx.textAlign = "center"
var width=ctx.measureText(str).width/globalScope.scale+8;
var height=13;
ctx.strokeStyle="black";
ctx.lineWidth=correctWidth(1);
ctx.fillStyle="yellow";
ctx.save();
ctx.beginPath();
rect(ctx,x1 -width/2 , y1 -height/2-3, width, height);
ctx.shadowColor = '#999';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.stroke();
ctx.fill();
ctx.restore();
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
ctx.beginPath();
ctx.fillStyle="black";
ctx.fillText(str, Math.round(x1 + globalScope.ox), Math.round( y1 + globalScope.oy));
ctx.fill();
}
function fillText(ctx, str, x1, y1, fontSize = 20) {
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
ctx.font = Math.round(fontSize * globalScope.scale) + "px Georgia";
ctx.fillText(str, Math.round(x1 + globalScope.ox), Math.round( y1 + globalScope.oy));
}
function fillText2(ctx, str, x1, y1, xx, yy, dir) {
angle = {
"RIGHT": 0,
"LEFT": 0,
"DOWN": Math.PI / 2,
"UP": -Math.PI / 2,
}
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
[x1, y1] = rotate(x1, y1, dir);
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
ctx.font = Math.round(14 * globalScope.scale) + "px Georgia";
ctx.save();
ctx.translate( Math.round(xx + x1 + globalScope.ox), Math.round( yy + y1 + globalScope.oy));
ctx.rotate(angle[dir]);
ctx.textAlign = "center";
ctx.fillText(str, 0, Math.round(4 * globalScope.scale)*(1-0*(+(dir=="DOWN"))));
ctx.restore();
}
function fillText4(ctx, str, x1, y1, xx, yy, dir,fontSize=14,textAlign="center") {
angle = {
"RIGHT": 0,
"LEFT": 0,
"DOWN": Math.PI / 2,
"UP": -Math.PI / 2,
}
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
[x1, y1] = rotate(x1, y1, dir);
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
ctx.font = Math.round(fontSize * globalScope.scale) + "px Georgia";
// ctx.font = 20+"px Georgia";
ctx.textAlign = textAlign;
ctx.fillText(str,xx+x1+globalScope.ox,yy+y1+globalScope.oy+ Math.round(fontSize/3 * globalScope.scale));
}
function fillText3(ctx, str, x1, y1, xx = 0, yy = 0, fontSize = 14, font = "Georgia", textAlign = "center") {
x1 = x1 * globalScope.scale;
y1 = y1 * globalScope.scale;
xx = xx * globalScope.scale;
yy = yy * globalScope.scale;
ctx.font = Math.round(fontSize * globalScope.scale) + "px " + font;
ctx.textAlign = textAlign;
ctx.fillText(str, Math.round( xx + x1 + globalScope.ox), Math.round( yy + y1 + globalScope.oy));
}
oppositeDirection = {
"RIGHT": "LEFT",
"LEFT": "RIGHT",
"DOWN": "UP",
"UP": "DOWN",
}
fixDirection = {
"right": "LEFT",
"left": "RIGHT",
"down": "UP",
"up": "DOWN",
"LEFT": "LEFT",
"RIGHT": "RIGHT",
"UP": "UP",
"DOWN": "DOWN",
}

View file

@ -0,0 +1,276 @@
var inputSample = 5;
var dataSample=[['01---','11110','01---','00000'],['01110','1-1-1','----0'],['01---','11110','01110','1-1-1','0---0'],['----1']];
var sampleInputListNames=["A", "B"];
var sampleOutputListNames=["X"];
createCombinationalAnalysisPrompt=function(scope=globalScope){
//console.log("Ya");
scheduleBackup();
$('#combinationalAnalysis').empty();
$('#combinationalAnalysis').append("<p>Enter Input names separated by commas: <input id='inputNameList' type='text' placeHolder='eg. In A, In B'></p>");
$('#combinationalAnalysis').append("<p>Enter Output names separated by commas: <input id='outputNameList' type='text' placeHolder='eg. Out X, Out Y'></p>");
$('#combinationalAnalysis').append("<p>Do you need a decimal column? <input id='decimalColumnBox' type='checkbox'></p>");
$('#combinationalAnalysis').dialog({
width:"auto",
buttons: [
{
text: "Next",
click: function() {
var inputList = $("#inputNameList").val().split(',');
var outputList = $("#outputNameList").val().split(',');
inputList = inputList.map(x => x.trim());
inputList = inputList.filter(e => e);
outputList = outputList.map(x => x.trim());
outputList = outputList.filter(e => e);
if(inputList.length > 0 && outputList.length > 0){
$(this).dialog("close");
createBooleanPrompt(inputList,outputList,scope);
}
else{
alert("Enter Input / Output Variable(s) !");
}
},
}
]
});
}
function createBooleanPrompt(inputListNames,outputListNames,scope=globalScope){
inputListNames=inputListNames||(prompt("Enter inputs separated by commas").split(','));
outputListNames=outputListNames||(prompt("Enter outputs separated by commas").split(','));
outputListNamesInteger=[];
for (var i = 0; i < outputListNames.length; i++)
outputListNamesInteger[i] = 7*i + 13;//assigning an integer to the value, 7*i + 13 is random
var s='<table>';
s+='<tbody style="display:block; max-height:70vh; overflow-y:scroll" >';
s+='<tr>';
if($("#decimalColumnBox").is(":checked"))
s+='<th>'+'dec'+'</th>';
for(var i=0;i<inputListNames.length;i++)
s+='<th>'+inputListNames[i]+'</th>';
for(var i=0;i<outputListNames.length;i++)
s+='<th>'+outputListNames[i]+'</th>';
s+='</tr>';
var matrix = [];
for(var i=0; i<inputListNames.length; i++) {
matrix[i] = new Array((1<<inputListNames.length));
}
for(var i=0;i<inputListNames.length;i++){
for(var j=0;j<(1<<inputListNames.length);j++){
matrix[i][j]=(+((j&(1<<(inputListNames.length-i-1)))!=0));
}
}
for(var j=0;j<(1<<inputListNames.length);j++){
s+='<tr>';
if($("#decimalColumnBox").is(":checked"))
s+='<td>'+j+'</td>';
for(var i=0;i<inputListNames.length;i++){
s+='<td>'+matrix[i][j]+'</td>';
}
for(var i=0;i<outputListNamesInteger.length;i++){
s+='<td class ="output '+outputListNamesInteger[i]+'" id="'+j+'">'+'x'+'</td>';
//using hash values as they'll be used in the generateBooleanTableData function
}
s+='</tr>';
}
s+='</tbody>';
s+='</table>';
//console.log(s)
$('#combinationalAnalysis').empty()
$('#combinationalAnalysis').append(s)
$('#combinationalAnalysis').dialog({
width:"auto",
buttons: [
{
text: "Generate Circuit",
click: function() {
$( this ).dialog( "close" );
var data = generateBooleanTableData(outputListNamesInteger);
//passing the hash values to avoid spaces being passed which is causing a problem
minmizedCircuit = [];
for(let output in data){
let temp = new BooleanMinimize(
inputListNames.length,
data[output][1].map(Number),
data[output]['x'].map(Number)
)
minmizedCircuit.push(temp.result);
}
// //console.log(dataSample);
drawCombinationalAnalysis(minmizedCircuit,inputListNames,outputListNames,scope)
},
},
{
text: "Print Truth Table",
click: function() {
var sTable = document.getElementById('combinationalAnalysis').innerHTML;
var style = "<style> table {font: 20px Calibri;} table, th, td {border: solid 1px #DDD;border-collapse: collapse;} padding: 2px 3px;text-align: center;} </style>";
var win = window.open('', '', 'height=700,width=700');
win.document.write('<html><head>');
win.document.write('<title>Boolean Logic Table</title>');
win.document.write(style);
win.document.write('</head>');
win.document.write('<body>');
win.document.write('<center>'+sTable+'</center>');
win.document.write('</body></html>');
win.document.close();
win.print();
},
},
]
});
$('.output').click(function (){
var v=$(this).html();
if(v==0)v=$(this).html(1);
else if(v==1)v=$(this).html('x');
else if(v=='x')v=$(this).html(0);
})
}
function generateBooleanTableData(outputListNames){
var data={};
for(var i=0;i<outputListNames.length;i++){
data[outputListNames[i]]={
'x':[],
'1':[],
'0':[],
}
$rows=$('.'+outputListNames[i]);
for(j=0;j<$rows.length;j++){
//console.log($rows[j].innerHTML)
data[outputListNames[i]][$rows[j].innerHTML].push($rows[j].id);
}
}
//console.log(data);
return data;
}
function drawCombinationalAnalysis(combinationalData,inputList,outputListNames,scope=globalScope){
//console.log(combinationalData);
var inputCount=inputList.length;
var maxTerms=0;
for(var i=0;i<combinationalData.length;i++)
maxTerms=Math.max(maxTerms,combinationalData[i].length);
var startPosX=200;
var startPosY=200;
var currentPosY=300;
var andPosX=startPosX+inputCount*40+40;
var orPosX=andPosX+Math.floor(maxTerms/2)*10+80;
var outputPosX=orPosX+60;
var inputObjects=[];
var logixNodes=[];
for(var i=0;i<inputCount;i++){
inputObjects.push(new Input(startPosX+i*40,startPosY,scope,"DOWN",1));
inputObjects[i].setLabel(inputList[i]);
inputObjects[i].newLabelDirection("UP");
var v1=new Node(startPosX+i*40,startPosY+20,2,scope.root);
inputObjects[i].output1.connect(v1);
var v2=new Node(startPosX+i*40+20,startPosY+20,2,scope.root);
v1.connect(v2);
var notG=new NotGate(startPosX+i*40+20, startPosY+40, scope, "DOWN", 1);
notG.inp1.connect(v2);
logixNodes.push(v1);
logixNodes.push(notG.output1);
}
function countTerm(s){
var c=0;
for(var i=0;i<s.length;i++)
if(s[i]!=='-')c++;
return c;
}
for(var i=0;i<combinationalData.length;i++){
// //console.log(combinationalData[i]);
var andGateNodes=[];
for(var j=0;j<combinationalData[i].length;j++){
var c=countTerm(combinationalData[i][j]);
if(c>1){
var andGate=new AndGate(andPosX, currentPosY, scope, "RIGHT", c, 1);
andGateNodes.push(andGate.output1);
var misses=0;
for(var k=0;k<combinationalData[i][j].length;k++){
if(combinationalData[i][j][k]=='-'){misses++;continue;}
var index=2*k+(combinationalData[i][j][k]==0);
//console.log(index);
//console.log(andGate);
var v=new Node(logixNodes[index].absX(),andGate.inp[k-misses].absY(),2,scope.root);
logixNodes[index].connect(v);
logixNodes[index]=v;
v.connect(andGate.inp[k-misses]);
}
}
else{
for(var k=0;k<combinationalData[i][j].length;k++){
if(combinationalData[i][j][k]=='-')continue;
var index=2*k+(combinationalData[i][j][k]==0);
var andGateSubstituteNode= new Node(andPosX, currentPosY, 2,scope.root);
var v=new Node(logixNodes[index].absX(),andGateSubstituteNode.absY(),2,scope.root);
logixNodes[index].connect(v);
logixNodes[index]=v;
v.connect(andGateSubstituteNode);
andGateNodes.push(andGateSubstituteNode);
}
}
currentPosY+=c*10+30;
}
var andGateCount=andGateNodes.length;
var midWay=Math.floor(andGateCount/2);
var orGatePosY=(andGateNodes[midWay].absY()+andGateNodes[Math.floor((andGateCount-1)/2)].absY())/2;
if( orGatePosY%10 == 5)
orGatePosY += 5; // To make or gate fall in grid
if(andGateCount>1){
var o=new OrGate(orPosX,orGatePosY,scope,"RIGHT",andGateCount,1);
if(andGateCount%2==1)andGateNodes[midWay].connect(o.inp[midWay]);
for(var j=0;j<midWay;j++){
var v=new Node(andPosX+30+(midWay-j)*10,andGateNodes[j].absY(),2,scope.root);
v.connect(andGateNodes[j]);
var v2=new Node(andPosX+30+(midWay-j)*10,o.inp[j].absY(),2,scope.root);
v2.connect(v)
o.inp[j].connect(v2);
var v=new Node(andPosX+30+(midWay-j)*10,andGateNodes[andGateCount-j-1].absY(),2,scope.root);
v.connect(andGateNodes[andGateCount-j-1]);
var v2=new Node(andPosX+30+(midWay-j)*10,o.inp[andGateCount-j-1].absY(),2,scope.root);
v2.connect(v)
o.inp[andGateCount-j-1].connect(v2);
}
var out=new Output(outputPosX,o.y,scope,"LEFT",1);
out.inp1.connect(o.output1);
}
else{
var out=new Output(outputPosX,andGateNodes[0].absY(),scope,"LEFT",1);
out.inp1.connect(andGateNodes[0]);
}
out.setLabel(outputListNames[i]);
out.newLabelDirection("RIGHT");
}
for(var i=0;i<logixNodes.length;i++){
if(logixNodes[i].absY()!=currentPosY){
var v=new Node(logixNodes[i].absX(),currentPosY,2,scope.root);
logixNodes[i].connect(v)
}
}
}

1023
public/js/data.js Normal file

File diff suppressed because it is too large Load diff

77
public/js/eeprom.js Normal file
View file

@ -0,0 +1,77 @@
/**
* EEPROM Component.
*
* This is basically a RAM component that persists its contents.
*
* We consider EEPROMs more 'expensive' than RAMs, so we arbitrarily limit
* the addressWith to a maximum of 10 bits (1024 addresses) with a default of 8-bit (256).
*
* In the EEPROM all addresses are initialized to zero.
* This way we serialize unused values as "0" instead of "null".
*
* These two techniques help keep reduce the size of saved projects.
*/
function EEPROM(x, y, scope = globalScope, dir = "RIGHT", bitWidth = 8, addressWidth = 8, data = null) {
RAM.call(this, x, y, scope, dir, bitWidth, addressWidth);
this.data = data || this.data;
}
EEPROM.prototype = Object.create(RAM.prototype);
EEPROM.prototype.tooltipText = "Electrically Erasable Programmable Read-Only Memory";
EEPROM.prototype.constructor = EEPROM;
EEPROM.prototype.shortName = "EEPROM";
EEPROM.prototype.maxAddressWidth = 10;
EEPROM.prototype.helplink = "https://docs.circuitverse.org/#/memoryElements?id=eeprom";
EEPROM.prototype.mutableProperties = {
"addressWidth": {
name: "Address Width",
type: "number",
max: "10",
min: "1",
func: "changeAddressWidth",
},
"dump": RAM.prototype.mutableProperties.dump,
"reset": RAM.prototype.mutableProperties.reset,
}
EEPROM.prototype.customSave = function () {
var saveInfo = RAM.prototype.customSave.call(this);
// Normalize this.data to use zeroes instead of null when serialized.
var data = this.data;
for (var i = 0; i < data.length; i++) {
data[i] = data[i] || 0;
}
saveInfo.constructorParamaters.push(data);
return saveInfo;
}
//This is a EERAM without a clock - not normal
//reset is supported
EEPROM.moduleVerilog = function () {
return `
module EEPROM(dout, addr, din, we, dmp, rst);
parameter WIDTH = 8;
parameter ADDR = 10;
output [WIDTH-1:0] dout;
input [ADDR-1:0] addr;
input [WIDTH-1:0] din;
input we;
input dmp;
input rst;
reg [WIDTH-1:0] mem[2**ADDR-1:0];
integer j;
assign dout = mem[addr];
always @ (*) begin
if (!rst)
for (j=0; j < 2**ADDR-1; j=j+1) begin
mem[j] = 0;
end
if (!we)
mem[addr] = din;
dout = mem[addr];
end
endmodule
`;
}

84
public/js/embed.js Normal file
View file

@ -0,0 +1,84 @@
// Helper functions for when circuit is embedded
$(document).ready(function() {
// Clock features
$('#clockProperty').append("<label class=''> <input type='button' class='objectPropertyAttribute' name='toggleFullScreen' value='Fullscreen' style='font-size: 20px'> </input> </label></br>");
$('#clockProperty').append("Time: <input class='objectPropertyAttribute' min='50' type='number' style='width:48px' step='10' name='changeClockTime' value='" + (simulationArea.timePeriod) + "'><br>");
$('#clockProperty').append("Clock: <label class='switch'> <input type='checkbox' " + ["", "checked"][simulationArea.clockEnabled + 0] + " class='objectPropertyAttributeChecked' name='changeClockEnable' > <span class='slider'></span> </label><br>");
// Following codes need to be removed
$(".objectPropertyAttribute").on("change keyup paste click", function() {
scheduleUpdate();
updateCanvas = true;
wireToBeChecked = 1;
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name])
prevPropertyObj = simulationArea.lastSelected[this.name](this.value) || prevPropertyObj;
else
window[this.name](this.value);
})
// Following codes need to be removed
$(".objectPropertyAttributeChecked").on("change keyup paste click", function() {
scheduleUpdate();
updateCanvas = true;
wireToBeChecked = 1;
if (simulationArea.lastSelected && simulationArea.lastSelected[this.name])
prevPropertyObj = simulationArea.lastSelected[this.name](this.value) || prevPropertyObj;
else
window[this.name](this.checked);
})
})
// Full screen toggle helper function
function toggleFullScreen(value) {
if (!getfullscreenelement())
GoInFullscreen(document.documentElement)
else
GoOutFullscreen();
}
// Full screen Listeners
if (document.addEventListener) {
document.addEventListener('webkitfullscreenchange', exitHandler, false);
document.addEventListener('mozfullscreenchange', exitHandler, false);
document.addEventListener('fullscreenchange', exitHandler, false);
document.addEventListener('MSFullscreenChange', exitHandler, false);
}
// Center focus accordingly
function exitHandler() {
setTimeout(function() {
for (id in scopeList)
scopeList[id].centerFocus(true);
gridUpdate = true;
scheduleUpdate();
}, 100)
}
function GoInFullscreen(element) {
if (element.requestFullscreen)
element.requestFullscreen();
else if (element.mozRequestFullScreen)
element.mozRequestFullScreen();
else if (element.webkitRequestFullscreen)
element.webkitRequestFullscreen();
else if (element.msRequestFullscreen)
element.msRequestFullscreen();
}
function GoOutFullscreen() {
if (document.exitFullscreen)
document.exitFullscreen();
else if (document.mozCancelFullScreen)
document.mozCancelFullScreen();
else if (document.webkitExitFullscreen)
document.webkitExitFullscreen();
else if (document.msExitFullscreen)
document.msExitFullscreen();
}
function getfullscreenelement() {
return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement
}

246
public/js/embedListeners.js Normal file
View file

@ -0,0 +1,246 @@
// Listeners when circuit is embedded
// Refer listeners.js
function startListeners() {
window.addEventListener('keyup', function (e) {
scheduleUpdate(1);
if (e.keyCode == 16) {
simulationArea.shiftDown = false;
}
if (e.key == "Meta" || e.key == "Control") {
simulationArea.controlDown = false;
}
});
document.getElementById("simulationArea").addEventListener('mousedown', function (e) {
errorDetected = false;
updateSimulation = true;
updatePosition = true;
updateCanvas = true;
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseDownX = Math.round(((simulationArea.mouseDownRawX - globalScope.ox) / globalScope.scale) / unit) * unit;
simulationArea.mouseDownY = Math.round(((simulationArea.mouseDownRawY - globalScope.oy) / globalScope.scale) / unit) * unit;
simulationArea.mouseDown = true;
simulationArea.oldx = globalScope.ox;
simulationArea.oldy = globalScope.oy;
e.preventDefault();
scheduleUpdate(1);
});
document.getElementById('simulationArea').addEventListener('mousemove', () => {
var ele = document.getElementById('elementName');
if (globalScope && simulationArea && simulationArea.objectList) {
var { objectList } = simulationArea;
objectList = objectList.filter(val => val !== 'wires');
for (var i = 0; i < objectList.length; i++) {
for (var j = 0; j < globalScope[objectList[i]].length; j++) {
if (globalScope[objectList[i]][j].isHover()) {
ele.style.display = 'block';
if (objectList[i] === 'SubCircuit') {
ele.innerHTML = `Subcircuit: ${globalScope.SubCircuit[j].data.name}`;
} else {
ele.innerHTML = `CircuitElement: ${objectList[i]}`;
}
return;
}
}
}
}
ele.style.display = 'none';
document.getElementById('elementName').innerHTML = '';
});
window.addEventListener('mousemove', function (e) {
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseXf = (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale;
simulationArea.mouseYf = (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale;
simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit;
simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit;
updateCanvas = true;
if (simulationArea.lastSelected == globalScope.root) {
updateCanvas = true;
var fn;
fn = function () {
updateSelectionsAndPane();
}
scheduleUpdate(0, 20, fn);
} else {
scheduleUpdate(0, 200);
}
});
window.addEventListener('keydown', function (e) {
errorDetected = false;
updateSimulation = true;
updatePosition = true;
// zoom in (+)
if (e.key == "Meta" || e.key == "Control") {
simulationArea.controlDown = true;
}
if (simulationArea.controlDown && (e.keyCode == 187 || e.KeyCode == 171)) {
e.preventDefault();
if (globalScope.scale < 4 * DPR)
changeScale(.1 * DPR);
}
// zoom out (-)
if (simulationArea.controlDown && (e.keyCode == 189 || e.Keycode == 173)) {
e.preventDefault();
if (globalScope.scale > 0.5 * DPR)
changeScale(-.1 * DPR);
}
if (simulationArea.mouseRawX < 0 || simulationArea.mouseRawY < 0 || simulationArea.mouseRawX > width || simulationArea.mouseRawY > height) return;
scheduleUpdate(1);
updateCanvas = true;
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown) {
if (e.key.toString().length == 1 || e.key.toString() == "Backspace") {
simulationArea.lastSelected.keyDown(e.key.toString());
return;
}
}
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown2) {
if (e.key.toString().length == 1) {
simulationArea.lastSelected.keyDown2(e.key.toString());
return;
}
}
// if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown3) {
// if (e.key.toString() != "Backspace" && e.key.toString() != "Delete") {
// simulationArea.lastSelected.keyDown3(e.key.toString());
// return;
// }
// }
if (e.key == "T" || e.key == "t") {
simulationArea.changeClockTime(prompt("Enter Time:"));
}
})
document.getElementById("simulationArea").addEventListener('dblclick', function (e) {
scheduleUpdate(2);
if (simulationArea.lastSelected && simulationArea.lastSelected.dblclick !== undefined) {
simulationArea.lastSelected.dblclick();
}
});
window.addEventListener('mouseup', function (e) {
simulationArea.mouseDown = false;
errorDetected = false;
updateSimulation = true;
updatePosition = true;
updateCanvas = true;
gridUpdate = true;
wireToBeChecked = true;
scheduleUpdate(1);
});
window.addEventListener('mousedown', function (e) {
this.focus();
});
document.getElementById("simulationArea").addEventListener('mousewheel', MouseScroll);
document.getElementById("simulationArea").addEventListener('DOMMouseScroll', MouseScroll);
function MouseScroll(event) {
updateCanvas = true;
event.preventDefault()
var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail;
var scrolledUp = deltaY < 0;
var scrolledDown = deltaY > 0;
if (event.ctrlKey) {
if (scrolledUp && globalScope.scale > 0.5 * DPR) {
changeScale(-.1 * DPR);
}
if (scrolledDown && globalScope.scale < 4 * DPR) {
changeScale(.1 * DPR);
}
} else {
if (scrolledUp && globalScope.scale < 4 * DPR) {
changeScale(.1 * DPR);
}
if (scrolledDown && globalScope.scale > 0.5 * DPR) {
changeScale(-.1 * DPR);
}
}
updateCanvas = true;
gridUpdate = true;
update(); // Schedule update not working, this is INEFFICENT
}
document.addEventListener('cut', function (e) {
simulationArea.copyList = simulationArea.multipleObjectSelections.slice();
if (simulationArea.lastSelected && simulationArea.lastSelected !== simulationArea.root && !simulationArea.copyList.contains(simulationArea.lastSelected)) {
simulationArea.copyList.push(simulationArea.lastSelected);
}
var textToPutOnClipboard = cut(simulationArea.copyList);
if (isIe) {
window.clipboardData.setData('Text', textToPutOnClipboard);
} else {
e.clipboardData.setData('text/plain', textToPutOnClipboard);
}
e.preventDefault();
});
document.addEventListener('copy', function (e) {
simulationArea.copyList = simulationArea.multipleObjectSelections.slice();
if (simulationArea.lastSelected && simulationArea.lastSelected !== simulationArea.root && !simulationArea.copyList.contains(simulationArea.lastSelected)) {
simulationArea.copyList.push(simulationArea.lastSelected);
}
var textToPutOnClipboard = copy(simulationArea.copyList);
if (isIe) {
window.clipboardData.setData('Text', textToPutOnClipboard);
} else {
e.clipboardData.setData('text/plain', textToPutOnClipboard);
}
e.preventDefault();
});
document.addEventListener('paste', function (e) {
var data;
if (isIe) {
data = window.clipboardData.getData('Text');
} else {
data = e.clipboardData.getData('text/plain');
}
paste(data);
e.preventDefault();
});
}
var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") != -1 ||
navigator.userAgent.toLowerCase().indexOf("trident") != -1);

361
public/js/engine.js Normal file
View file

@ -0,0 +1,361 @@
// Engine.js
// Core of the simulation and rendering algorithm
var totalObjects = 0;
// Function to check for any UI update, it is throttled by time
function scheduleUpdate(count = 0, time = 100, fn) {
if (lightMode) time *= 5;
if (count && !layoutMode) { // Force update
update();
for (var i = 0; i < count; i++)
setTimeout(update, 10 + 50 * i);
}
if (willBeUpdated) return; // Throttling
willBeUpdated = true;
if (layoutMode) {
setTimeout(layoutUpdate, time); // Update layout, different algorithm
return;
}
// Call a function before update ..
if (fn)
setTimeout(function() {
fn();
update();
}, time);
else setTimeout(update, time);
}
// fn that calls update on everything else. If any change is there, it resolves the circuit and draws it again
function update(scope = globalScope, updateEverything = false) {
willBeUpdated = false;
if (loading == true || layoutMode) return;
var updated = false;
simulationArea.hover = undefined;
// Update wires
if (wireToBeChecked || updateEverything) {
if (wireToBeChecked == 2) wireToBeChecked = 0; // this required due to timing issues
else wireToBeChecked++;
// WHY IS THIS REQUIRED ???? we are checking inside wire ALSO
var prevLength = scope.wires.length;
for (var i = 0; i < scope.wires.length; i++) {
scope.wires[i].checkConnections();
if (scope.wires.length != prevLength) {
prevLength--;
i--;
}
}
scheduleUpdate();
}
// Update subcircuits
if (updateSubcircuit || updateEverything) {
for (var i = 0; i < scope.SubCircuit.length; i++)
scope.SubCircuit[i].reset();
updateSubcircuit = false;
}
// Update UI position
if (updatePosition || updateEverything) {
for (var i = 0; i < updateOrder.length; i++)
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
updated |= scope[updateOrder[i]][j].update();
}
}
// Updates multiple objectselections and panes window
if (updatePosition || updateEverything) {
updateSelectionsAndPane(scope);
}
// Update MiniMap
if (!embed && simulationArea.mouseDown && simulationArea.lastSelected && simulationArea.lastSelected != globalScope.root) {
if (!lightMode)
$('#miniMap').fadeOut('fast');
}
// Run simulation
if (updateSimulation) {
play();
}
// Show properties of selected element
if (!embed && prevPropertyObj != simulationArea.lastSelected) {
if (simulationArea.lastSelected && simulationArea.lastSelected.objectType !== "Wire") {
showProperties(simulationArea.lastSelected);
} else {
// hideProperties();
}
}
//Draw, render everything
if (updateCanvas) {
renderCanvas(scope);
}
updateSimulation = updateCanvas = updatePosition = false;
}
// Function to find dimensions of the current circuit
function findDimensions(scope = globalScope) {
totalObjects = 0;
simulationArea.minWidth = undefined;
simulationArea.maxWidth = undefined;
simulationArea.minHeight = undefined;
simulationArea.maxHeight = undefined;
for (var i = 0; i < updateOrder.length; i++) {
if (updateOrder[i] !== 'wires')
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
totalObjects += 1;
var obj = scope[updateOrder[i]][j];
if (totalObjects == 1) {
simulationArea.minWidth = obj.absX();
simulationArea.minHeight = obj.absY();
simulationArea.maxWidth = obj.absX();
simulationArea.maxHeight = obj.absY();
}
if (obj.objectType != 'Node') {
if (obj.y - obj.upDimensionY < simulationArea.minHeight)
simulationArea.minHeight = obj.y - obj.upDimensionY;
if (obj.y + obj.downDimensionY > simulationArea.maxHeight)
simulationArea.maxHeight = obj.y + obj.downDimensionY;
if (obj.x - obj.leftDimensionX < simulationArea.minWidth)
simulationArea.minWidth = obj.x - obj.leftDimensionX;
if (obj.x + obj.rightDimensionX > simulationArea.maxWidth)
simulationArea.maxWidth = obj.x + obj.rightDimensionX;
} else {
if (obj.absY() < simulationArea.minHeight)
simulationArea.minHeight = obj.absY();
if (obj.absY() > simulationArea.maxHeight)
simulationArea.maxHeight = obj.absY();
if (obj.absX() < simulationArea.minWidth)
simulationArea.minWidth = obj.absX();
if (obj.absX() > simulationArea.maxWidth)
simulationArea.maxWidth = obj.absX();
}
}
}
simulationArea.objectList = updateOrder;
}
// Function to move multiple objects and panes window
function updateSelectionsAndPane(scope = globalScope) {
if (!simulationArea.selected && simulationArea.mouseDown) {
simulationArea.selected = true;
simulationArea.lastSelected = scope.root;
simulationArea.hover = scope.root;
// Selecting multiple objects
if (simulationArea.shiftDown) {
objectSelection = true;
} else {
if (!embed) {
findDimensions(scope);
miniMapArea.setup();
$('#miniMap').show();
}
}
} else if (simulationArea.lastSelected == scope.root && simulationArea.mouseDown) {
//pane canvas
if (!objectSelection) {
globalScope.ox = (simulationArea.mouseRawX - simulationArea.mouseDownRawX) + simulationArea.oldx;
globalScope.oy = (simulationArea.mouseRawY - simulationArea.mouseDownRawY) + simulationArea.oldy;
globalScope.ox = Math.round(globalScope.ox);
globalScope.oy = Math.round(globalScope.oy);
gridUpdate = true;
if (!embed && !lightMode) miniMapArea.setup();
} else {
}
} else if (simulationArea.lastSelected == scope.root) {
// Select multiple objects
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
if (objectSelection) {
objectSelection = false;
var x1 = simulationArea.mouseDownX;
var x2 = simulationArea.mouseX;
var y1 = simulationArea.mouseDownY;
var y2 = simulationArea.mouseY;
// Sort points
if (x1 > x2) {
var temp = x1;
x1 = x2;
x2 = temp;
}
if (y1 > y2) {
var temp = y1;
y1 = y2;
y2 = temp;
}
// Select the objects, push them into a list
for (var i = 0; i < updateOrder.length; i++) {
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
var obj = scope[updateOrder[i]][j];
if (simulationArea.multipleObjectSelections.contains(obj)) continue;
var x, y;
if (obj.objectType == "Node") {
x = obj.absX();
y = obj.absY();
} else if (obj.objectType != "Wire") {
x = obj.x;
y = obj.y;
} else {
continue;
}
if (x > x1 && x < x2 && y > y1 && y < y2) {
simulationArea.multipleObjectSelections.push(obj);
}
}
}
}
}
}
// Function to render Canvas according th renderupdate order
function renderCanvas(scope) {
if (layoutMode) { // Different Algorithm
return;
}
var ctx = simulationArea.context;
// Reset canvas
simulationArea.clear();
// Update Grid
if (gridUpdate) {
gridUpdate = false;
dots();
}
canvasMessageData = undefined; // Globally set in draw fn ()
// Render objects
for (var i = 0; i < renderOrder.length; i++)
for (var j = 0; j < scope[renderOrder[i]].length; j++)
scope[renderOrder[i]][j].draw();
// Show any message
if (canvasMessageData) {
canvasMessage(ctx, canvasMessageData.string, canvasMessageData.x, canvasMessageData.y)
}
// If multiple object selections are going on, show selected area
if (objectSelection) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillStyle = "rgba(0,0,0,0.1)"
rect2(ctx, simulationArea.mouseDownX, simulationArea.mouseDownY, simulationArea.mouseX - simulationArea.mouseDownX, simulationArea.mouseY - simulationArea.mouseDownY, 0, 0, "RIGHT");
ctx.stroke();
ctx.fill();
}
if (simulationArea.hover != undefined) {
simulationArea.canvas.style.cursor = "pointer";
} else if (createNode) {
simulationArea.canvas.style.cursor = 'grabbing';
} else {
simulationArea.canvas.style.cursor = 'default';
}
}
//Main fn that resolves circuit using event driven simulation
function play(scope = globalScope, resetNodes = false) {
if (errorDetected) return; // Don't simulate until error is fixed
if (loading == true) return; // Don't simulate until loaded
if (!embed) plotArea.stopWatch.Stop(); // Waveform thing
// Reset Nodes if required
if (resetNodes || forceResetNodes) {
scope.reset();
simulationArea.simulationQueue.reset();
forceResetNodes = false;
}
// Temporarily kept for for future uses
// else{
// // clearBuses(scope);
// for(var i=0;i<scope.TriState.length;i++) {
// // console.log("HIT2",i);
// scope.TriState[i].removePropagation();
// }
// }
// Add subcircuits if they can be resolved -- needs to be removed/ deprecated
for (var i = 0; i < scope.SubCircuit.length; i++) {
if (scope.SubCircuit[i].isResolvable()) simulationArea.simulationQueue.add(scope.SubCircuit[i]);
}
// To store list of circuitselements that have shown contention but kept temporarily
// Mainly to resolve tristate bus issues
simulationArea.contentionPending = [];
// add inputs to the simulation queue
scope.addInputs();
var stepCount = 0;
var elem = undefined
while (!simulationArea.simulationQueue.isEmpty()) {
if (errorDetected) {
simulationArea.simulationQueue.reset();
return;
}
elem = simulationArea.simulationQueue.pop();
elem.resolve();
stepCount++;
if (stepCount > 1000000) { // Cyclic or infinite Circuit Detection
showError("Simulation Stack limit exceeded: maybe due to cyclic paths or contention");
errorDetected = true;
forceResetNodes = true
}
}
// Check for TriState Contentions
if (simulationArea.contentionPending.length) {
console.log(simulationArea.contentionPending)
showError("Contention at TriState");
forceResetNodes = true
errorDetected = true;
}
// Setting Flag Values
for (var i = 0; i < scope.Flag.length; i++)
scope.Flag[i].setPlotValue();
}

432
public/js/engine_old.js Normal file
View file

@ -0,0 +1,432 @@
// Temporarily kept - to be deleted
var totalObjects = 0;
function scheduleUpdate(count = 0, time = 100, fn) {
// return;
// //////console.log(willBeUpdated,updateSimulation,);
if (lightMode) time *= 5;
if (count && !layoutMode) {
for (var i = 0; i < count; i++)
setTimeout(update, 10 + 50 * i);
}
if (willBeUpdated) return;
willBeUpdated = true;
// if (simulationArea.mouseDown)
if (layoutMode) {
setTimeout(layoutUpdate, time);
return;
}
if (fn)
setTimeout(function() {
fn();
update();
}, time);
else setTimeout(update, time);
// else
// setTimeout(update, 100);
}
// fn that calls update on everything else. If any change is there, it resolves the circuit and draws it again
// fn to change scale (zoom) - It also shifts origin so that the position
//of the object in focus doent changeB
function update(scope = globalScope, updateEverything = false) {
willBeUpdated = false;
if (loading == true || layoutMode) return;
// //////console.log("UPDATE");
var updated = false;
simulationArea.hover = undefined;
// wireToBeChecked=true;
if (wireToBeChecked || updateEverything) {
if (wireToBeChecked == 2) wireToBeChecked = 0; // this required due to timing issues
else wireToBeChecked++;
// WHY IS THIS REQUIRED ???? we are checking inside wire ALSO
var prevLength = scope.wires.length;
for (var i = 0; i < scope.wires.length; i++) {
scope.wires[i].checkConnections();
if (scope.wires.length != prevLength) {
prevLength--;
i--;
}
}
scheduleUpdate();
}
if (updateSubcircuit || updateEverything) {
for (var i = 0; i < scope.SubCircuit.length; i++)
scope.SubCircuit[i].reset();
updateSubcircuit = false;
}
if (updatePosition || updateEverything) {
//////console.log("updatePosition");
for (var i = 0; i < updateOrder.length; i++)
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
updated |= scope[updateOrder[i]][j].update();
}
}
// updateSimulation |= updated;
if (updatePosition) {
}
if (updatePosition || updateEverything) {
updateSelectionsAndPane(scope);
}
if (!embed && simulationArea.mouseDown && simulationArea.lastSelected && simulationArea.lastSelected != globalScope.root) {
if (!lightMode)
$('#miniMap').fadeOut('fast');
}
if (updateSimulation) {
play();
}
if (!embed && prevPropertyObj != simulationArea.lastSelected) {
if (simulationArea.lastSelected && simulationArea.lastSelected.objectType !== "Wire") {
showProperties(simulationArea.lastSelected);
//////console.log("yo");
} else {
// hideProperties();
}
}
//Draw
if (updateCanvas) {
renderCanvas(scope);
}
// if (updateSimulation||update) scheduleUpdate();
updateSimulation = updateCanvas = updatePosition = false;
// if(updated){
// updatePosition=true;
// updateCanvas=true;
// scheduleUpdate();
// }
}
function findDimensions(scope = globalScope) {
totalObjects = 0;
simulationArea.minWidth = undefined;
simulationArea.maxWidth = undefined;
simulationArea.minHeight = undefined;
simulationArea.maxHeight = undefined;
for (var i = 0; i < updateOrder.length; i++) {
if (updateOrder[i] !== 'wires')
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
totalObjects += 1;
var obj = scope[updateOrder[i]][j];
if (totalObjects == 1) {
simulationArea.minWidth = obj.absX();
simulationArea.minHeight = obj.absY();
simulationArea.maxWidth = obj.absX();
simulationArea.maxHeight = obj.absY();
}
if (obj.objectType != 'Node') {
if (obj.y - obj.upDimensionY < simulationArea.minHeight)
simulationArea.minHeight = obj.y - obj.upDimensionY;
if (obj.y + obj.downDimensionY > simulationArea.maxHeight)
simulationArea.maxHeight = obj.y + obj.downDimensionY;
if (obj.x - obj.leftDimensionX < simulationArea.minWidth)
simulationArea.minWidth = obj.x - obj.leftDimensionX;
if (obj.x + obj.rightDimensionX > simulationArea.maxWidth)
simulationArea.maxWidth = obj.x + obj.rightDimensionX;
} else {
if (obj.absY() < simulationArea.minHeight)
simulationArea.minHeight = obj.absY();
if (obj.absY() > simulationArea.maxHeight)
simulationArea.maxHeight = obj.absY();
if (obj.absX() < simulationArea.minWidth)
simulationArea.minWidth = obj.absX();
if (obj.absX() > simulationArea.maxWidth)
simulationArea.maxWidth = obj.absX();
}
}
}
simulationArea.objectList = updateOrder;
}
function updateSelectionsAndPane(scope = globalScope) {
//////console.log("updateSelectionsAndPane");
if (!simulationArea.selected && simulationArea.mouseDown) {
//mouse click NOT on object
simulationArea.selected = true;
simulationArea.lastSelected = scope.root;
simulationArea.hover = scope.root;
if (simulationArea.shiftDown) {
objectSelection = true;
} else {
if (!embed) {
findDimensions(scope);
miniMapArea.setup();
$('#miniMap').show();
}
}
} else if (simulationArea.lastSelected == scope.root && simulationArea.mouseDown) {
//pane canvas
if (!objectSelection) {
globalScope.ox = (simulationArea.mouseRawX - simulationArea.mouseDownRawX) + simulationArea.oldx;
globalScope.oy = (simulationArea.mouseRawY - simulationArea.mouseDownRawY) + simulationArea.oldy;
globalScope.ox = Math.round(globalScope.ox);
globalScope.oy = Math.round(globalScope.oy);
gridUpdate = true;
if (!embed && !lightMode) miniMapArea.setup();
} else {
}
} else if (simulationArea.lastSelected == scope.root) {
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
if (objectSelection) {
objectSelection = false;
var x1 = simulationArea.mouseDownX;
var x2 = simulationArea.mouseX;
var y1 = simulationArea.mouseDownY;
var y2 = simulationArea.mouseY;
// simulationArea.multipleObjectSelections=[];
// //////console.log(x1,x2,y1,y2);
// [x1,x2]=[x1,x2].sort();
// [y1,y2]=[y1,y2].sort();
if (x1 > x2) {
var temp = x1;
x1 = x2;
x2 = temp;
}
if (y1 > y2) {
var temp = y1;
y1 = y2;
y2 = temp;
}
// //////console.log(x1,x2,y1,y2);
for (var i = 0; i < updateOrder.length; i++) {
for (var j = 0; j < scope[updateOrder[i]].length; j++) {
var obj = scope[updateOrder[i]][j];
// //////console.log(obj);
if (simulationArea.multipleObjectSelections.contains(obj)) continue;
var x, y;
if (obj.objectType == "Node") {
x = obj.absX();
y = obj.absY();
} else if (obj.objectType != "Wire") {
x = obj.x;
y = obj.y;
} else {
// //////console.log(obj);
continue;
}
if (x > x1 && x < x2 && y > y1 && y < y2) {
simulationArea.multipleObjectSelections.push(obj);
}
}
}
}
}
}
function renderCanvas(scope) {
if (layoutMode) {
return;
}
var ctx = simulationArea.context;
simulationArea.clear();
if (gridUpdate) {
gridUpdate = false;
dots();
}
// dots(); // draw dots
canvasMessageData = undefined;
for (var i = 0; i < renderOrder.length; i++)
for (var j = 0; j < scope[renderOrder[i]].length; j++)
scope[renderOrder[i]][j].draw();
if (canvasMessageData) {
canvasMessage(ctx, canvasMessageData.string, canvasMessageData.x, canvasMessageData.y)
}
if (objectSelection) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillStyle = "rgba(0,0,0,0.1)"
rect2(ctx, simulationArea.mouseDownX, simulationArea.mouseDownY, simulationArea.mouseX - simulationArea.mouseDownX, simulationArea.mouseY - simulationArea.mouseDownY, 0, 0, "RIGHT");
ctx.stroke();
ctx.fill();
}
}
//Main fn that resolves circuit
function play(scope = globalScope, resetNodes = false) {
// throw("ERROR");
//////console.log("play");
if (errorDetected) return;
// //////console.log("simulation");
if (loading == true) return;
if (!embed) plotArea.stopWatch.Stop();
// scope.stack=[];
scope.pending = [];
scope.queue = new PriorityQueue({
comparator: function(a, b) {
return a.priority - b.priority;
}
});
if (resetNodes || forceResetNodes) {
forceResetNodes = false;
for (var i = 0; i < scope.allNodes.length; i++)
scope.allNodes[i].reset();
} else {
// clearBuses(scope);
for (var i = 0; i < scope.TriState.length; i++) {
// console.log("HIT2",i);
scope.TriState[i].removePropagation();
}
}
for (var i = 0; i < scope.SubCircuit.length; i++) {
if (scope.SubCircuit[i].isResolvable()) scope.queue.queue(scope.SubCircuit[i]);
}
// for (var i = 0; i < scope.FlipFlop.length; i++) {
// scope.queue.queue(scope.FlipFlop[i]);
// }
for (var i = 0; i < inputList.length; i++) {
for (var j = 0; j < scope[inputList[i]].length; j++) {
scope.queue.queue(scope[inputList[i]][j]);
}
}
// console.log(scope.stack);
var stepCount = 0;
var elem = undefined
while (scope.queue.length || scope.pending.length) {
// if(stepCount%100==0)console.log(scope.stack);
if (errorDetected) return;
if (scope.queue.length) {
elem = scope.queue.dequeue();
// if(elem.objectType=="TriState"){
// // elem.removePropagation();
// scope.pending.push(elem);
// continue;
// }
} else
elem = scope.pending.pop();
// console.log("resolving:",elem)
// scope.pending.clean(this);
// scope.stack.clean(this);
elem.resolve();
stepCount++;
if (stepCount > 1000) {
// console.log(elem)
showError("Simulation Stack limit exceeded: maybe due to cyclic paths or contention");
return;
}
}
for (var i = 0; i < scope.Flag.length; i++)
scope.Flag[i].setPlotValue();
// for (var i = 0; i < scope.SubCircuit.length; i++) {
// if(!scope.SubCircuit[i].isResolvable())
// {
// scope.queue.queue(scope.SubCircuit[i]);
// while (scope.stack.length) {
// if(errorDetected)return;
// var elem = scope.stack.pop();
// elem.resolve();
// stepCount++;
// if (stepCount > 1000) {
// showError("Simulation Stack limit exceeded: maybe due to cyclic paths or contention");
// return;
// }
// }
// }
// }
}
function clearBuses(scope = globalScope) {
scope.stack = [];
scope.pending = [];
for (var i = 0; i < scope.TriState.length; i++) {
console.log("HIT2", i);
scope.TriState[i].removePropagation();
}
for (var i = 0; i < scope.Clock.length; i++) {
console.log("HIT2", i);
scope.Clock[i].removePropagation();
}
// console.log(scope.stack);
var stepCount = 0;
var elem = undefined
while (scope.stack.length || scope.pending.length) {
// if(stepCount%100==0)console.log(scope.stack);
if (errorDetected) return;
if (scope.stack.length) {
elem = scope.stack.pop();
// if(elem.objectType=="TriState"){
// // elem.removePropagation();
// scope.pending.push(elem);
// continue;
// }
} else
elem = scope.pending.pop();
// scope.pending.clean(this);
// scope.stack.clean(this);
elem.resolve();
stepCount++;
if (stepCount > 1000) {
// console.log(elem)
showError("BUS ERROR: Contention");
return;
}
}
}

83
public/js/eventQueue.js Normal file
View file

@ -0,0 +1,83 @@
// Event Queue is simply a priority Queue, basic implementation O(n^2)
class EventQueue {
constructor(size) {
this.size=size;
this.queue=new Array(size);
this.frontIndex=0;
this.time=0;
}
add(obj,delay){
if(obj.queueProperties.inQueue){
obj.queueProperties.time=this.time+(delay||obj.propagationDelay);
let i=obj.queueProperties.index;
while(i>0 && obj.queueProperties.time>this.queue[i-1].queueProperties.time){
this.swap(i,i-1)
i--;
}
i=obj.queueProperties.index;
while(i<this.frontIndex-1 && obj.queueProperties.time<this.queue[i+1].queueProperties.time){
this.swap(i,i+1);
i++;
}
return;
}
if(this.frontIndex==this.size) throw "EventQueue size exceeded";
this.queue[this.frontIndex]=obj;
// console.log(this.time)
// obj.queueProperties.time=obj.propagationDelay;
obj.queueProperties.time=this.time+(delay||obj.propagationDelay);
obj.queueProperties.index=this.frontIndex;
this.frontIndex++;
obj.queueProperties.inQueue=true;
let i=obj.queueProperties.index;
while(i>0 && obj.queueProperties.time>this.queue[i-1].queueProperties.time){
this.swap(i,i-1)
i--;
}
}
addImmediate(obj){
this.queue[this.frontIndex]=obj;
obj.queueProperties.time=this.time;
obj.queueProperties.index=this.frontIndex;
obj.queueProperties.inQueue=true;
this.frontIndex++;
}
swap(v1,v2){
let obj1= this.queue[v1];
obj1.queueProperties.index=v2;
let obj2= this.queue[v2];
obj2.queueProperties.index=v1;
this.queue[v1]=obj2;
this.queue[v2]=obj1;
}
pop(){
if(this.isEmpty())throw "Queue Empty"
this.frontIndex--;
let obj=this.queue[this.frontIndex]
this.time=obj.queueProperties.time;
obj.queueProperties.inQueue=false;
return obj;
}
reset(){
for(let i=0;i<this.frontIndex;i++)
this.queue[i].queueProperties.inQueue=false
this.time=0
this.frontIndex=0;
}
isEmpty(){
return this.frontIndex==0;
}
}

80
public/js/grading.js Normal file
View file

@ -0,0 +1,80 @@
/*
Grading
*/
$('.list-group-item-action').on('click', (e) => {
e.preventDefault();
$('#assignment-project-id').val(e.currentTarget.id);
$('#project-grade').html($(e.currentTarget).attr('data-grade'));
$('#project-remarks').html($(e.currentTarget).attr('data-remarks'));
$('#project-grade-error').html('');
});
$('#grade-form-remove').click((e) => {
e.preventDefault();
var form = $('#assignment-grade-form');
var url = form.attr('action');
$.ajax({
type: 'DELETE',
url,
data: form.serialize(),
success: (data) => {
if (data.project_id != null) {
$('#project-grade').html('N.A.');
$(`#${data.project_id}`).attr('data-grade', 'N.A.');
$('#project-remarks').html('N.A.');
$(`#${data.project_id}`).attr('data-remarks', 'N.A.');
}
},
});
});
$('#grade-form-finalized').click((e) => {
e.preventDefault();
var url = $(e.currentTarget).data('url');
if (confirm('Do you want to finalize grades?')) {
$.ajax({
type: 'PUT',
url: `${url}.json`,
data: { assignment: { grades_finalized: true } },
success: (data) => {
$('#assignment-grade-form').html('Grades have been finalized!');
},
});
}
});
$('#grade-form-submit').click((e) => {
e.preventDefault();
var form = $('#assignment-grade-form');
var type = 'POST';
var url = form.attr('action');
if ($('#assignment-grade-grade').val() !== '' || $('#assignment-grade-remarks').val() !== '') {
$.ajax({
type,
url,
data: form.serialize(),
datatype: 'json',
success: (data) => {
const gradeRemarks = data.remarks !== null ? data.remarks : 'N.A.';
$('#project-grade').html(data.grade);
$('#project-remarks').html(gradeRemarks);
$(`#${data.project_id}`).attr('data-grade', data.grade);
$(`#${data.project_id}`).attr('data-remarks', gradeRemarks);
$('#project-grade-error').html('');
$('#assignment-grade-grade').val('');
$('#assignment-grade-remarks').val('');
},
error: (data) => {
$('#project-grade-error').html(`* ${data.responseJSON.error}`);
$('#assignment-grade-grade').val('');
$('#assignment-grade-remarks').val('');
},
});
}
});

416
public/js/layout.js Normal file
View file

@ -0,0 +1,416 @@
// Layout.js - all subcircuit layout related code is here
// Function to toggle between layoutMode and normal Mode
function toggleLayoutMode() {
if (layoutMode) {
layoutMode = false;
temp_buffer = undefined;
$("#layoutDialog").fadeOut();
globalScope.centerFocus(false);
dots();
} else {
layoutMode = true;
$("#layoutDialog").fadeIn();
globalScope.ox = 0;
globalScope.oy = 0;
globalScope.scale = DPR * 1.3;
dots();
temp_buffer = new layout_buffer();
$("#toggleLayoutTitle")[0].checked=temp_buffer.layout.titleEnabled;
}
hideProperties();
update(globalScope, true)
scheduleUpdate();
}
// Update UI, positions of inputs and outputs
function layoutUpdate(scope = globalScope) {
if (!layoutMode) return;
willBeUpdated = false;
for (var i = 0; i < temp_buffer.Input.length; i++) {
temp_buffer.Input[i].update()
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
temp_buffer.Output[i].update()
}
paneLayout(scope);
renderLayout(scope);
}
function paneLayout(scope = globalScope){
if (!simulationArea.selected && simulationArea.mouseDown) {
simulationArea.selected = true;
simulationArea.lastSelected = scope.root;
simulationArea.hover = scope.root;
} else if (simulationArea.lastSelected == scope.root && simulationArea.mouseDown) {
//pane canvas
if (!objectSelection) {
globalScope.ox = (simulationArea.mouseRawX - simulationArea.mouseDownRawX) + simulationArea.oldx;
globalScope.oy = (simulationArea.mouseRawY - simulationArea.mouseDownRawY) + simulationArea.oldy;
globalScope.ox = Math.round(globalScope.ox);
globalScope.oy = Math.round(globalScope.oy);
gridUpdate = true;
if (!embed && !lightMode) miniMapArea.setup();
} else {
}
} else if (simulationArea.lastSelected == scope.root) {
// Select multiple objects
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
}
}
// Buffer object to store changes
function layout_buffer(scope = globalScope) {
// Position of screen in layoutMode -- needs to be deprecated, reset screen position instead
var x = -Math.round(globalScope.ox / 10) * 10;
var y = -Math.round(globalScope.oy / 10) * 10;
var w = Math.round((width / globalScope.scale) * 0.01) * 10; // 10% width of screen in layoutMode
var h = Math.round((height / globalScope.scale) * 0.01) * 10; // 10% height of screen in layoutMode
var xx = x + w;
var yy = y + h;
// Position of subcircuit
this.xx = xx;
this.yy = yy;
// Assign layout if exist or create new one
this.layout = Object.assign({}, scope.layout); //Object.create(scope.layout);
// Push Input Nodes
this.Input = [];
for (var i = 0; i < scope.Input.length; i++)
this.Input.push(new layoutNode(scope.Input[i].layoutProperties.x, scope.Input[i].layoutProperties.y, scope.Input[i].layoutProperties.id, scope.Input[i].label, xx, yy, scope.Input[i].type, scope.Input[i]))
// Push Output Nodes
this.Output = [];
for (var i = 0; i < scope.Output.length; i++)
this.Output.push(new layoutNode(scope.Output[i].layoutProperties.x, scope.Output[i].layoutProperties.y, scope.Output[i].layoutProperties.id, scope.Output[i].label, xx, yy, scope.Output[i].type, scope.Output[i]))
}
// Check if position is on the boundaries of subcircuit
layout_buffer.prototype.isAllowed = function(x, y) {
if (x < 0 || x > this.layout.width || y < 0 || y > this.layout.height) return false;
if (x > 0 && x < this.layout.width && y > 0 && y < this.layout.height) return false;
if ((x == 0 && y == 0) || (x == 0 && y == this.layout.height) || (x == this.layout.width && y == 0) || (x == this.layout.width && y == this.layout.height)) return false;
return true;
}
// Check if node is already at a position
layout_buffer.prototype.isNodeAt = function(x, y) {
for (var i = 0; i < this.Input.length; i++)
if (this.Input[i].x == x && this.Input[i].y == y) return true;
for (var i = 0; i < this.Output.length; i++)
if (this.Output[i].x == x && this.Output[i].y == y) return true;
return false;
}
// Function to render layout on screen
function renderLayout(scope = globalScope) {
if (!layoutMode) return;
var xx = temp_buffer.xx;
var yy = temp_buffer.yy;
var ctx = simulationArea.context;
simulationArea.clear();
ctx.strokeStyle = "black";
ctx.fillStyle = "white";
ctx.lineWidth = correctWidth(3);
// Draw base rectangle
ctx.beginPath();
rect2(ctx, 0, 0, temp_buffer.layout.width, temp_buffer.layout.height, xx, yy, "RIGHT");
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
if(temp_buffer.layout.titleEnabled){
fillText(ctx, scope.name, temp_buffer.layout.title_x + xx, yy + temp_buffer.layout.title_y, 11);
}
// Draw labels
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (!temp_buffer.Input[i].label) continue;
var info = determine_label(temp_buffer.Input[i].x, temp_buffer.Input[i].y, scope);
ctx.textAlign = info[0];
fillText(ctx, temp_buffer.Input[i].label, temp_buffer.Input[i].x + info[1] + xx, yy + temp_buffer.Input[i].y + info[2], 12);
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (!temp_buffer.Output[i].label) continue;
var info = determine_label(temp_buffer.Output[i].x, temp_buffer.Output[i].y, scope);
ctx.textAlign = info[0];
fillText(ctx, temp_buffer.Output[i].label, temp_buffer.Output[i].x + info[1] + xx, yy + temp_buffer.Output[i].y + info[2], 12);
}
ctx.fill();
// Draw points
for (var i = 0; i < temp_buffer.Input.length; i++) {
temp_buffer.Input[i].draw()
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
temp_buffer.Output[i].draw()
}
if (gridUpdate) {
gridUpdate = false;
dots();
}
}
// Helper function to reset all nodes to original default positions
function layoutResetNodes() {
temp_buffer.layout.width = 100;
temp_buffer.layout.height = Math.max(temp_buffer.Input.length, temp_buffer.Output.length) * 20 + 20;
for (var i = 0; i < temp_buffer.Input.length; i++) {
temp_buffer.Input[i].x = 0;
temp_buffer.Input[i].y = i * 20 + 20;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
temp_buffer.Output[i].x = temp_buffer.layout.width;
temp_buffer.Output[i].y = i * 20 + 20;
}
}
// Increase width, and move all nodes
function increaseLayoutWidth() {
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].x == temp_buffer.layout.width)
temp_buffer.Input[i].x += 10;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].x == temp_buffer.layout.width)
temp_buffer.Output[i].x += 10;
}
temp_buffer.layout.width += 10;
}
// Increase Height, and move all nodes
function increaseLayoutHeight() {
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].y == temp_buffer.layout.height)
temp_buffer.Input[i].y += 10;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].y == temp_buffer.layout.height)
temp_buffer.Output[i].y += 10;
}
temp_buffer.layout.height += 10;
}
// Decrease Width, and move all nodes, check if space is there
function decreaseLayoutWidth() {
if (temp_buffer.layout.width < 30) return;
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].x == temp_buffer.layout.width - 10) {
showMessage("No space. Move or delete some nodes to make space.");
return;
}
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].x == temp_buffer.layout.width - 10) {
showMessage("No space. Move or delete some nodes to make space.");
return;
}
}
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].x == temp_buffer.layout.width)
temp_buffer.Input[i].x -= 10;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].x == temp_buffer.layout.width)
temp_buffer.Output[i].x -= 10;
}
temp_buffer.layout.width -= 10;
}
// Decrease Height, and move all nodes, check if space is there
function decreaseLayoutHeight() {
if (temp_buffer.layout.height < 30) return;
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].y == temp_buffer.layout.height - 10) {
showMessage("No space. Move or delete some nodes to make space.");
return;
}
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].y == temp_buffer.layout.height - 10) {
showMessage("No space. Move or delete some nodes to make space.");
return;
}
}
for (var i = 0; i < temp_buffer.Input.length; i++) {
if (temp_buffer.Input[i].y == temp_buffer.layout.height)
temp_buffer.Input[i].y -= 10;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
if (temp_buffer.Output[i].y == temp_buffer.layout.height)
temp_buffer.Output[i].y -= 10;
}
temp_buffer.layout.height -= 10;
}
// Helper functions to move the titles
function layoutTitleUp() {
temp_buffer.layout.title_y -= 5;
}
function layoutTitleDown() {
temp_buffer.layout.title_y += 5;
}
function layoutTitleRight() {
temp_buffer.layout.title_x += 5;
}
function layoutTitleLeft() {
temp_buffer.layout.title_x -= 5;
}
function toggleLayoutTitle(){
temp_buffer.layout.titleEnabled=!temp_buffer.layout.titleEnabled;
}
function layoutNode(x, y, id, label = "", xx, yy, type, parent) {
this.type = type;
this.id = id
this.xx = xx; // Position of parent
this.yy = yy; // Position of parent
this.label = label;
this.prevx = undefined;
this.prevy = undefined;
this.x = x; // Position of node wrt to parent
this.y = y; // Position of node wrt to parent
this.radius = 5;
this.clicked = false;
this.hover = false;
this.wasClicked = false;
this.prev = 'a';
this.count = 0;
this.parent = parent;
}
layoutNode.prototype.absX = function() {
return this.x + this.xx;
}
layoutNode.prototype.absY = function() {
return this.y + this.yy
}
layoutNode.prototype.update = function() {
// Code copied from node.update() - Some code is redundant - needs to be removed
if (this == simulationArea.hover) simulationArea.hover = undefined;
this.hover = this.isHover();
if (!simulationArea.mouseDown) {
if (this.absX() != this.prevx || this.absY() != this.prevy) {
// Store position before clicked
this.prevx = this.absX();
this.prevy = this.absY();
}
}
if (this.hover) {
simulationArea.hover = this;
}
if (simulationArea.mouseDown && ((this.hover && !simulationArea.selected) || simulationArea.lastSelected == this)) {
simulationArea.selected = true;
simulationArea.lastSelected = this;
this.clicked = true;
} else {
this.clicked = false;
}
if (!this.wasClicked && this.clicked) {
this.wasClicked = true;
this.prev = 'a';
simulationArea.lastSelected = this;
} else if (this.wasClicked && this.clicked) {
// Check if valid position and update accordingly
if (temp_buffer.isAllowed(simulationArea.mouseX - this.xx, simulationArea.mouseY - this.yy) && !temp_buffer.isNodeAt(simulationArea.mouseX - this.xx, simulationArea.mouseY - this.yy)) {
this.x = simulationArea.mouseX - this.xx;
this.y = simulationArea.mouseY - this.yy;
}
}
}
layoutNode.prototype.draw = function() {
var ctx = simulationArea.context;
drawCircle(ctx, this.absX(), this.absY(), 3, ["green", "red"][+(simulationArea.lastSelected == this)]);
}
layoutNode.prototype.isHover = function() {
return this.absX() == simulationArea.mouseX && this.absY() == simulationArea.mouseY;
}
// Helper function to determine alignment and position of nodes for rendering
function determine_label(x, y) {
if (x == 0) return ["left", 5, 5]
if (x == temp_buffer.layout.width) return ["right", -5, 5]
if (y == 0) return ["center", 0, 13]
return ["center", 0, -6]
}
function cancelLayout() {
if (layoutMode) {
toggleLayoutMode();
}
}
// Store all data into layout and exit
function saveLayout() {
if (layoutMode) {
for (var i = 0; i < temp_buffer.Input.length; i++) {
temp_buffer.Input[i].parent.layoutProperties.x = temp_buffer.Input[i].x;
temp_buffer.Input[i].parent.layoutProperties.y = temp_buffer.Input[i].y;
}
for (var i = 0; i < temp_buffer.Output.length; i++) {
temp_buffer.Output[i].parent.layoutProperties.x = temp_buffer.Output[i].x;
temp_buffer.Output[i].parent.layoutProperties.y = temp_buffer.Output[i].y;
}
globalScope.layout = Object.assign({}, temp_buffer.layout);
toggleLayoutMode();
}
}

502
public/js/listeners.js Normal file
View file

@ -0,0 +1,502 @@
// Most Listeners are stored here
function startListeners() {
window.addEventListener('keyup', function(e) {
scheduleUpdate(1);
simulationArea.shiftDown = e.shiftKey;
if (e.keyCode == 16) {
simulationArea.shiftDown = false;
}
if (e.key == "Meta" || e.key == "Control") {
simulationArea.controlDown = false;
}
});
document.getElementById("simulationArea").addEventListener('mousedown', function(e) {
createNode = true
stopWire = false
simulationArea.mouseDown = true;
$("input").blur();
errorDetected = false;
updateSimulation = true;
updatePosition = true;
updateCanvas = true;
simulationArea.lastSelected = undefined;
simulationArea.selected = false;
simulationArea.hover = undefined;
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseDownX = Math.round(((simulationArea.mouseDownRawX - globalScope.ox) / globalScope.scale) / unit) * unit;
simulationArea.mouseDownY = Math.round(((simulationArea.mouseDownRawY - globalScope.oy) / globalScope.scale) / unit) * unit;
simulationArea.oldx = globalScope.ox;
simulationArea.oldy = globalScope.oy;
e.preventDefault();
scheduleBackup();
scheduleUpdate(1);
$('.dropdown.open').removeClass('open');
});
document.getElementById("simulationArea").addEventListener('mouseup', function(e) {
if (simulationArea.lastSelected) simulationArea.lastSelected.newElement = false;
/*
handling restricted circuit elements
*/
if(simulationArea.lastSelected && restrictedElements.includes(simulationArea.lastSelected.objectType)
&& !globalScope.restrictedCircuitElementsUsed.includes(simulationArea.lastSelected.objectType)) {
globalScope.restrictedCircuitElementsUsed.push(simulationArea.lastSelected.objectType);
updateRestrictedElementsList();
}
// deselect multible elements with click
if (!simulationArea.shiftDown && simulationArea.multipleObjectSelections.length > 0
) {
if (
!simulationArea.multipleObjectSelections.includes(
simulationArea.lastSelected
)
)
simulationArea.multipleObjectSelections = [];
}
});
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('keydown', function(e) {
if(document.activeElement.tagName == "INPUT") return;
if(listenToSimulator){
// If mouse is focusing on input element, then override any action
// if($(':focus').length){
// return;
// }
if (document.activeElement.tagName == "INPUT" || simulationArea.mouseRawX < 0 || simulationArea.mouseRawY < 0 || simulationArea.mouseRawX > width || simulationArea.mouseRawY > height) {
return;
} else {
// HACK TO REMOVE FOCUS ON PROPERTIES
if (document.activeElement.type == "number") {
hideProperties();
showProperties(simulationArea.lastSelected)
}
}
errorDetected = false;
updateSimulation = true;
updatePosition = true;
simulationArea.shiftDown = e.shiftKey;
// stop making wires when we connect the 2nd termial to a node
if(stopWire){
createNode=false
}
if (e.key == "Meta" || e.key == "Control") {
simulationArea.controlDown = true;
}
// zoom in (+)
if ((simulationArea.controlDown && (e.keyCode == 187 || e.keyCode == 171))||e.keyCode==107) {
e.preventDefault();
ZoomIn()
}
// zoom out (-)
if ((simulationArea.controlDown && (e.keyCode == 189 || e.keyCode == 173))||e.keyCode==109) {
e.preventDefault();
ZoomOut()
}
if (simulationArea.mouseRawX < 0 || simulationArea.mouseRawY < 0 || simulationArea.mouseRawX > width || simulationArea.mouseRawY > height) return;
scheduleUpdate(1);
updateCanvas = true;
wireToBeChecked = 1;
// Needs to be deprecated, moved to more recent listeners
if (simulationArea.controlDown && (e.key == "C" || e.key == "c")) {
// simulationArea.copyList=simulationArea.multipleObjectSelections.slice();
// if(simulationArea.lastSelected&&simulationArea.lastSelected!==simulationArea.root&&!simulationArea.copyList.contains(simulationArea.lastSelected)){
// simulationArea.copyList.push(simulationArea.lastSelected);
// }
// copy(simulationArea.copyList);
}
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown) {
if (e.key.toString().length == 1 || e.key.toString() == "Backspace") {
simulationArea.lastSelected.keyDown(e.key.toString());
return;
}
}
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown2) {
if (e.key.toString().length == 1) {
simulationArea.lastSelected.keyDown2(e.key.toString());
return;
}
}
if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown3) {
if (e.key.toString() != "Backspace" && e.key.toString() != "Delete") {
simulationArea.lastSelected.keyDown3(e.key.toString());
return;
}
}
if (e.keyCode == 16) {
simulationArea.shiftDown = true;
if (simulationArea.lastSelected && !simulationArea.lastSelected.keyDown && simulationArea.lastSelected.objectType != "Wire" && simulationArea.lastSelected.objectType != "CircuitElement" && !simulationArea.multipleObjectSelections.contains(simulationArea.lastSelected)) {
simulationArea.multipleObjectSelections.push(simulationArea.lastSelected);
}
}
if (e.keyCode == 8 || e.key == "Delete") {
delete_selected();
}
if (simulationArea.controlDown && e.key.charCodeAt(0) == 122) { // detect the special CTRL-Z code
undo();
}
// Detect online save shortcut (CTRL+S)
if (simulationArea.controlDown && e.keyCode == 83 && !simulationArea.shiftDown) {
save();
e.preventDefault();
}
// Detect offline save shortcut (CTRL+SHIFT+S)
if (simulationArea.controlDown && e.keyCode == 83 && simulationArea.shiftDown) {
saveOffline();
e.preventDefault();
}
// Detect Select all Shortcut
if (simulationArea.controlDown && (e.keyCode == 65 || e.keyCode == 97)) {
selectAll();
e.preventDefault();
}
//deselect all Shortcut
if (e.keyCode == 27) {
simulationArea.multipleObjectSelections = [];
simulationArea.lastSelected = undefined;
e.preventDefault();
}
//change direction fns
if (simulationArea.lastSelected != undefined) {
let direction = "";
switch (e.keyCode) {
case 37:
case 65:
direction = "LEFT";
break;
case 38:
case 87:
direction = "UP";
break;
case 39:
case 68:
direction = "RIGHT";
break;
case 40:
case 83:
direction = "DOWN";
break;
default:
break;
}
if (direction !== ""){
simulationArea.lastSelected.newDirection(direction);
}
}
if ((e.keyCode == 113 || e.keyCode == 81) && simulationArea.lastSelected != undefined) {
if (simulationArea.lastSelected.bitWidth !== undefined)
simulationArea.lastSelected.newBitWidth(parseInt(prompt("Enter new bitWidth"), 10));
}
if (simulationArea.controlDown && (e.key == "T" || e.key == "t")) {
// e.preventDefault(); //browsers normally open a new tab
simulationArea.changeClockTime(prompt("Enter Time:"));
}
// f1 key for opening the documentation page
if (e.keyCode === 112) {
e.preventDefault();
window.open('https://docs.circuitverse.org/', '_blank');
}
}
})
document.getElementById("simulationArea").addEventListener('dblclick', function(e) {
scheduleUpdate(2);
if (simulationArea.lastSelected && simulationArea.lastSelected.dblclick !== undefined) {
simulationArea.lastSelected.dblclick();
}
// not needed becasue we do that with one click , but leaving it as it is will not harm
if (!simulationArea.shiftDown) {
simulationArea.multipleObjectSelections = [];
}
});
window.addEventListener('mouseup', onMouseUp);
document.getElementById("simulationArea").addEventListener('mousewheel', MouseScroll);
document.getElementById("simulationArea").addEventListener('DOMMouseScroll', MouseScroll);
function MouseScroll(event) {
event.preventDefault()
var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail;
  event.preventDefault();
  var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail;
  let direction = deltaY > 0 ? 1 : -1;
  handleZoom(direction);
updateCanvas = true;
if(layoutMode)layoutUpdate();
else update(); // Schedule update not working, this is INEFFICIENT
}
document.addEventListener('cut', function(e) {
if(document.activeElement.tagName == "INPUT") return;
if(listenToSimulator){
simulationArea.copyList = simulationArea.multipleObjectSelections.slice();
if (simulationArea.lastSelected && simulationArea.lastSelected !== simulationArea.root && !simulationArea.copyList.contains(simulationArea.lastSelected)) {
simulationArea.copyList.push(simulationArea.lastSelected);
}
var textToPutOnClipboard = copy(simulationArea.copyList, true);
// Updated restricted elements
updateRestrictedElementsInScope();
localStorage.setItem('clipboardData', textToPutOnClipboard);
e.preventDefault();
if(textToPutOnClipboard==undefined)
return;
if (isIe) {
window.clipboardData.setData('Text', textToPutOnClipboard);
} else {
e.clipboardData.setData('text/plain', textToPutOnClipboard);
}
}
});
document.addEventListener('copy', function(e) {
if(document.activeElement.tagName == "INPUT") return;
if(listenToSimulator){
simulationArea.copyList = simulationArea.multipleObjectSelections.slice();
if (simulationArea.lastSelected && simulationArea.lastSelected !== simulationArea.root && !simulationArea.copyList.contains(simulationArea.lastSelected)) {
simulationArea.copyList.push(simulationArea.lastSelected);
}
var textToPutOnClipboard = copy(simulationArea.copyList);
// Updated restricted elements
updateRestrictedElementsInScope();
localStorage.setItem('clipboardData', textToPutOnClipboard);
e.preventDefault();
if(textToPutOnClipboard==undefined)
return;
if (isIe) {
window.clipboardData.setData('Text', textToPutOnClipboard);
} else {
e.clipboardData.setData('text/plain', textToPutOnClipboard);
}
}
});
document.addEventListener('paste', function(e) {
if(document.activeElement.tagName == "INPUT") return;
if(listenToSimulator){
var data;
if (isIe) {
data = window.clipboardData.getData('Text');
} else {
data = e.clipboardData.getData('text/plain');
}
paste(data);
// Updated restricted elements
updateRestrictedElementsInScope();
e.preventDefault();
}
});
restrictedElements.forEach((element) => {
$(`#${element}`).mouseover(() => {
showRestricted();
});
$(`#${element}`).mouseout(() => {
hideRestricted();
})
});
}
var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") != -1 ||
navigator.userAgent.toLowerCase().indexOf("trident") != -1);
function removeMiniMap() {
if (lightMode) return;
if (simulationArea.lastSelected == globalScope.root && simulationArea.mouseDown) return;
if (lastMiniMapShown + 2000 >= new Date().getTime()) {
setTimeout(removeMiniMap, lastMiniMapShown + 2000 - new Date().getTime());
return;
}
$('#miniMap').fadeOut('fast');
}
function onMouseMove(e) {
var rect = simulationArea.canvas.getBoundingClientRect();
simulationArea.mouseRawX = (e.clientX - rect.left) * DPR;
simulationArea.mouseRawY = (e.clientY - rect.top) * DPR;
simulationArea.mouseXf = (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale;
simulationArea.mouseYf = (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale;
simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit;
simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit;
updateCanvas = true;
if (simulationArea.lastSelected && (simulationArea.mouseDown || simulationArea.lastSelected.newElement)) {
updateCanvas = true;
var fn;
if (simulationArea.lastSelected == globalScope.root) {
fn = function() {
updateSelectionsAndPane();
}
} else {
fn = function() {
if (simulationArea.lastSelected)
simulationArea.lastSelected.update();
};
}
scheduleUpdate(0, 20, fn);
} else {
scheduleUpdate(0, 200);
}
}
function onMouseUp(e) {
createNode =simulationArea.controlDown
simulationArea.mouseDown = false;
if (!lightMode) {
lastMiniMapShown = new Date().getTime();
setTimeout(removeMiniMap, 2000);
}
errorDetected = false;
updateSimulation = true;
updatePosition = true;
updateCanvas = true;
gridUpdate = true;
wireToBeChecked = true;
scheduleUpdate(1);
for (var i = 0; i < 2; i++) {
updatePosition = true;
wireToBeChecked = true;
update();
}
errorDetected = false;
updateSimulation = true;
updatePosition = true;
updateCanvas = true;
gridUpdate = true;
wireToBeChecked = true;
scheduleUpdate(1);
var rect = simulationArea.canvas.getBoundingClientRect();
if (!(simulationArea.mouseRawX < 0 || simulationArea.mouseRawY < 0 || simulationArea.mouseRawX > width || simulationArea.mouseRawY > height)) {
smartDropXX = simulationArea.mouseX + 100; //Math.round(((simulationArea.mouseRawX - globalScope.ox+100) / globalScope.scale) / unit) * unit;
smartDropYY = simulationArea.mouseY - 50; //Math.round(((simulationArea.mouseRawY - globalScope.oy+100) / globalScope.scale) / unit) * unit;
}
}
function delete_selected(){
$("input").blur();
hideProperties();
if (simulationArea.lastSelected && !(simulationArea.lastSelected.objectType == "Node" && simulationArea.lastSelected.type != 2)) simulationArea.lastSelected.delete();
for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) {
if (!(simulationArea.multipleObjectSelections[i].objectType == "Node" && simulationArea.multipleObjectSelections[i].type != 2)) simulationArea.multipleObjectSelections[i].cleanDelete();
}
simulationArea.multipleObjectSelections = [];
// Updated restricted elements
updateRestrictedElementsInScope();
}
function resizeTabs(){
var $windowsize = $('body').width();
var $sideBarsize = $('.side').width();
var $maxwidth = ($windowsize - $sideBarsize);
$("#tabsBar div").each(function(e){
$(this).css({ 'max-width': $maxwidth - 30 });
});
}
window.addEventListener("resize", resizeTabs);
resizeTabs();
$(function () {
$('[data-toggle="tooltip"]').tooltip()
});
// direction is only 1 or -1
function handleZoom(direction) {
  if (globalScope.scale > 0.5 * DPR) {
    changeScale(direction * 0.1 * DPR);
  } else if (globalScope.scale < 4 * DPR) {
    changeScale(direction * 0.1 * DPR);
  }
  gridUpdate = true;
}
function ZoomIn() {
  handleZoom(1);
}
function ZoomOut() {
  handleZoom(-1);
}

1536
public/js/logix.js Normal file

File diff suppressed because it is too large Load diff

88
public/js/metadata.json Normal file
View file

@ -0,0 +1,88 @@
var metadata = {
"circuitElementList" : [
"Input", "Output", "NotGate", "OrGate", "AndGate", "NorGate", "NandGate", "XorGate", "XnorGate", "SevenSegDisplay", "SixteenSegDisplay", "HexDisplay",
"Multiplexer", "BitSelector", "Splitter", "Power", "Ground", "ConstantVal", "ControlledInverter", "TriState", "Adder","TwoComplement", "Rom", "RAM", "EEPROM", "TflipFlop",
"JKflipFlop", "SRflipFlop", "DflipFlop", "TTY", "Keyboard", "Clock", "DigitalLed", "Stepper", "VariableLed", "RGBLed", "SquareRGBLed", "RGBLedMatrix", "Button", "Demultiplexer",
"Buffer", "SubCircuit", "Flag", "MSB", "LSB", "PriorityEncoder", "Tunnel", "ALU", "Decoder", "Random", "Counter", "Dlatch", "TB_Input", "TB_Output", "ForceGate",
],
"annotationList" : ["Text", "Rectangle", "Arrow"],
"inputList" : ["Random", "Dlatch", "JKflipFlop", "TflipFlop", "SRflipFlop", "DflipFlop", "Buffer", "Stepper", "Ground", "Power", "ConstantVal", "Input", "Clock", "Button", "Counter"],
"subCircuitInputList" : ["Random", "Dlatch", "JKflipFlop", "TflipFlop", "SRflipFlop", "DflipFlop", "Buffer", "Stepper", "Ground", "Power", "ConstantVal", "Clock", "Button", "Counter"],
"elementHierarchy": {
"Input":[
"Input",
"Button",
"Power",
"Ground",
"ConstantVal",
"Stepper",
"Random",
"Counter"
],
"Output":[
"Output",
"RGBLed",
"DigitalLed",
"VariableLed",
"HexDisplay",
"SevenSegDisplay",
"SixteenSegDisplay",
"SquareRGBLed",
"RGBLedMatrix"
],
"Gates":[
"AndGate",
"OrGate",
"NotGate",
"XorGate",
"NandGate",
"NorGate",
"XnorGate"
],
"Decoders & Plexers":[
"Multiplexer",
"Demultiplexer",
"BitSelector",
"MSB",
"LSB",
"PriorityEncoder",
"Decoder"
],
"Sequential Elements":[
"DflipFlop",
"Dlatch",
"TflipFlop",
"JKflipFlop",
"SRflipFlop",
"TTY",
"Keyboard",
"Clock",
],
"Memory Elements":[
"Rom",
"RAM",
"EEPROM"
],
"Test Bench":[
"TB_Input",
"TB_Output",
"ForceGate"
],
"Misc":[
"Flag",
"Splitter",
"Adder",
"TriState",
"Buffer",
"ControlledInverter",
"ALU",
"Rectangle",
"Arrow",
"Text",
"Tunnel",
"TwoComplement"
]
}
}

107
public/js/miniMap.js Normal file
View file

@ -0,0 +1,107 @@
var miniMapArea = {
canvas: document.getElementById("miniMapArea"),
setup: function() {
if (lightMode) return;
this.pageHeight = height //Math.round(((parseInt($("#simulationArea").height())))/ratio)*ratio-50; // -50 for tool bar? Check again
this.pageWidth = width //Math.round(((parseInt($("#simulationArea").width())))/ratio)*ratio;
this.pageY = (this.pageHeight - globalScope.oy);
this.pageX = (this.pageWidth - globalScope.ox);
if (simulationArea.minHeight != undefined)
this.minY = Math.min(simulationArea.minHeight, (-globalScope.oy) / globalScope.scale);
else
this.minY = (-globalScope.oy) / globalScope.scale;
if (simulationArea.maxHeight != undefined)
this.maxY = Math.max(simulationArea.maxHeight, this.pageY / globalScope.scale);
else
this.maxY = this.pageY / globalScope.scale
if (simulationArea.minWidth != undefined)
this.minX = Math.min(simulationArea.minWidth, (-globalScope.ox) / globalScope.scale);
else
this.minX = (-globalScope.ox) / globalScope.scale;
if (simulationArea.maxWidth != undefined)
this.maxX = Math.max(simulationArea.maxWidth, (this.pageX) / globalScope.scale);
else
this.maxX = (this.pageX) / globalScope.scale;
var h = this.maxY - this.minY;
var w = this.maxX - this.minX;
var ratio = Math.min(250 / h, 250 / w);
if (h > w) {
this.canvas.height = 250.0;
this.canvas.width = (250.0 * w) / h;
} else {
this.canvas.width = 250.0;
this.canvas.height = (250.0 * h) / w;
}
this.canvas.height += 5;
this.canvas.width += 5;
document.getElementById("miniMap").style.height = this.canvas.height;
document.getElementById("miniMap").style.width = this.canvas.width;
this.ctx = this.canvas.getContext("2d");
this.play(ratio);
},
play: function(ratio) {
if (lightMode) return;
this.ctx.fillStyle = "#bbb";
this.ctx.rect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fill();
this.resolve(ratio);
},
resolve: function(ratio) {
if (lightMode) return;
this.ctx.fillStyle = "#ddd";
this.ctx.beginPath();
this.ctx.rect(2.5 + ((this.pageX - this.pageWidth) / globalScope.scale - this.minX) * ratio, 2.5 + ((this.pageY - this.pageHeight) / globalScope.scale - this.minY) * ratio, this.pageWidth * ratio / globalScope.scale, this.pageHeight * ratio / globalScope.scale);
this.ctx.fill();
// to show the area of current canvas
var lst = updateOrder;
this.ctx.strokeStyle = "green";
this.ctx.fillStyle = "DarkGreen";
for (var i = 0; i < lst.length; i++) {
if (lst[i] === 'wires') {
for (var j = 0; j < globalScope[lst[i]].length; j++) {
this.ctx.beginPath();
this.ctx.moveTo(2.5 + (globalScope[lst[i]][j].node1.absX() - this.minX) * ratio, 2.5 + (globalScope[lst[i]][j].node1.absY() - this.minY) * ratio);
this.ctx.lineTo(2.5 + (globalScope[lst[i]][j].node2.absX() - this.minX) * ratio, 2.5 + (globalScope[lst[i]][j].node2.absY() - this.minY) * ratio);
this.ctx.stroke();
}
} else if (lst[i] != 'nodes') {
// Don't include SquareRGBLed here; it has correct size.
var ledY = 0;
if (lst[i] == "DigitalLed" || lst[i] == "VariableLed" || lst[i] == "RGBLed")
ledY = 20;
for (var j = 0; j < globalScope[lst[i]].length; j++) {
var xx = (globalScope[lst[i]][j].x - simulationArea.minWidth);
var yy = (globalScope[lst[i]][j].y - simulationArea.minHeight);
this.ctx.beginPath();
var obj = globalScope[lst[i]][j];
this.ctx.rect(2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio, 2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio, (obj.rightDimensionX + obj.leftDimensionX) * ratio, (obj.downDimensionY + obj.upDimensionY + ledY) * ratio);
this.ctx.fill();
this.ctx.stroke();
}
}
}
},
clear: function() {
if (lightMode) return;
$("#miniMapArea").css("z-index", "-1");
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};

4461
public/js/modules.js Normal file

File diff suppressed because it is too large Load diff

2
public/js/pace.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,333 @@
/**
* Author and copyright: Stefan Haack (https://shaack.com)
* Repository: https://github.com/shaack/bootstrap-input-spinner
* License: MIT, see file 'LICENSE'
*/
//** The file has been modified
(function ($) {
"use strict";
var triggerKeyPressed = false;
var originalVal = $.fn.val;
$.fn.val = function (value) {
if (arguments.length >= 1) {
if (this[0] && this[0]["bootstrap-input-spinner"] && this[0].setValue) {
var element = this[0];
setTimeout(function () {
element.setValue(value);
});
}
}
return originalVal.apply(this, arguments);
};
$.fn.InputSpinner = $.fn.inputSpinner = function (options) {
var config = {
decrementButton: "<strong>-</strong>", // button text
incrementButton: "<strong>+</strong>", // ..
groupClass: "", // css class of the resulting input-group
buttonsClass: "btn-outline-secondary",
buttonsWidth: "2.5rem",
textAlign: "center",
autoDelay: 500, // ms holding before auto value change
autoInterval: 100, // speed of auto value change
boostThreshold: 10, // boost after these steps
boostMultiplier: "auto", // you can also set a constant number as multiplier
};
for (var option in options) {
config[option] = options[option];
}
var html =
'<div class="input-group ' +
config.groupClass +
'">' +
'<div class="input-group-prepend">' +
'<button style=" border:none; min-width: ' +
config.buttonsWidth +
'" class="btn btn-decrement ' +
config.buttonsClass +
'" type="button">' +
config.decrementButton +
"</button>" +
"</div>" +
'<input type="text" inputmode="decimal" style="text-align: ' +
config.textAlign +
'" class="form-control"/>' +
'<div class="input-group-append">' +
'<button style=" border:none; min-width: ' +
config.buttonsWidth +
'" class="btn btn-increment ' +
config.buttonsClass +
'" type="button">' +
config.incrementButton +
"</button>" +
"</div>" +
"</div>";
var locale = navigator.language || "en-US";
this.each(function () {
var $original = $(this);
$original[0]["bootstrap-input-spinner"] = true;
$original.hide();
var autoDelayHandler = null;
var autoIntervalHandler = null;
var autoMultiplier = config.boostMultiplier === "auto";
var boostMultiplier = autoMultiplier ? 1 : config.boostMultiplier;
var $inputGroup = $(html);
var $buttonDecrement = $inputGroup.find(".btn-decrement");
var $buttonIncrement = $inputGroup.find(".btn-increment");
var $input = $inputGroup.find("input");
var min = null;
var max = null;
var step = null;
var stepMax = null;
var decimals = null;
var digitGrouping = null;
var numberFormat = null;
updateAttributes();
var value = parseFloat($original[0].value);
var boostStepsCount = 0;
var prefix = $original.attr("data-prefix") || "";
var suffix = $original.attr("data-suffix") || "";
if (prefix) {
var prefixElement = $(
'<span class="input-group-text">' + prefix + "</span>"
);
$inputGroup.find(".input-group-prepend").append(prefixElement);
}
if (suffix) {
var suffixElement = $(
'<span class="input-group-text">' + suffix + "</span>"
);
$inputGroup.find(".input-group-append").prepend(suffixElement);
}
$original[0].setValue = function (newValue) {
setValue(newValue);
};
var observer = new MutationObserver(function () {
updateAttributes();
setValue(value, true);
});
observer.observe($original[0], { attributes: true });
$original.after($inputGroup);
setValue(value);
$input.on("paste input change focusout", function (event) {
var newValue = $input[0].value;
var focusOut = event.type === "focusout";
newValue = parseLocaleNumber(newValue);
setValue(newValue, focusOut);
dispatchEvent($original, event.type);
});
onPointerDown($buttonDecrement[0], function () {
stepHandling(-step);
});
onPointerDown($buttonIncrement[0], function () {
stepHandling(step);
});
onPointerUp(document.body, function () {
resetTimer();
});
function setValue(newValue, updateInput) {
if (updateInput === undefined) {
updateInput = true;
}
if (isNaN(newValue) || newValue === "") {
$original[0].value = "";
if (updateInput) {
$input[0].value = "";
}
value = NaN;
} else {
newValue = parseFloat(newValue);
newValue = Math.min(Math.max(newValue, min), max);
newValue =
Math.round(newValue * Math.pow(10, decimals)) /
Math.pow(10, decimals);
$original[0].value = newValue;
if (updateInput) {
$input[0].value = numberFormat.format(newValue);
}
value = newValue;
}
}
function dispatchEvent($element, type) {
if (type) {
setTimeout(function () {
var event;
if (typeof Event === "function") {
event = new Event(type, { bubbles: true });
} else {
// IE
event = document.createEvent("Event");
event.initEvent(type, true, true);
}
$element[0].dispatchEvent(event);
});
}
}
function stepHandling(step) {
if (!$input[0].disabled && !$input[0].readOnly) {
calcStep(step);
resetTimer();
autoDelayHandler = setTimeout(function () {
autoIntervalHandler = setInterval(function () {
if (boostStepsCount > config.boostThreshold) {
if (autoMultiplier) {
calcStep(step * parseInt(boostMultiplier, 10));
if (boostMultiplier < 100000000) {
boostMultiplier = boostMultiplier * 1.1;
}
if (stepMax) {
boostMultiplier = Math.min(stepMax, boostMultiplier);
}
} else {
calcStep(step * boostMultiplier);
}
} else {
calcStep(step);
}
boostStepsCount++;
}, config.autoInterval);
}, config.autoDelay);
}
}
function calcStep(step) {
if (isNaN(value)) {
value = 0;
}
setValue(Math.round(value / step) * step + step);
dispatchEvent($original, "input");
dispatchEvent($original, "change");
}
function resetTimer() {
boostStepsCount = 0;
boostMultiplier = boostMultiplier = autoMultiplier
? 1
: config.boostMultiplier;
clearTimeout(autoDelayHandler);
clearTimeout(autoIntervalHandler);
}
function updateAttributes() {
// copy properties from original to the new input
$input.prop("required", $original.prop("required"));
$input.prop("placeholder", $original.prop("placeholder"));
$input.attr("inputmode", $original.attr("inputmode") || "decimal");
var disabled = $original.prop("disabled");
var readonly = $original.prop("readonly");
$input.prop("disabled", disabled);
$input.prop("readonly", readonly);
$buttonIncrement.prop("disabled", disabled || readonly);
$buttonDecrement.prop("disabled", disabled || readonly);
if (disabled || readonly) {
resetTimer();
}
var originalClass = $original.prop("class");
var groupClass = "";
// sizing
if (/form-control-sm/g.test(originalClass)) {
groupClass = "input-group-sm";
} else if (/form-control-lg/g.test(originalClass)) {
groupClass = "input-group-lg";
}
var inputClass = originalClass.replace(/form-control(-(sm|lg))?/g, "");
$inputGroup.prop(
"class",
"input-group " + groupClass + " " + config.groupClass
);
$input.prop("class", "form-control " + inputClass);
// update the main attributes
min = parseFloat($original.prop("min")) || 0;
max =
isNaN($original.prop("max")) || $original.prop("max") === ""
? Infinity
: parseFloat($original.prop("max"));
step = parseFloat($original.prop("step")) || 1;
stepMax = parseInt($original.attr("data-step-max")) || 0;
var newDecimals = parseInt($original.attr("data-decimals")) || 0;
var newDigitGrouping = !(
$original.attr("data-digit-grouping") === "false"
);
if (decimals !== newDecimals || digitGrouping !== newDigitGrouping) {
decimals = newDecimals;
digitGrouping = newDigitGrouping;
numberFormat = new Intl.NumberFormat(locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
useGrouping: digitGrouping,
});
}
}
function parseLocaleNumber(stringNumber) {
var numberFormat = new Intl.NumberFormat(locale);
var thousandSeparator = numberFormat.format(1111).replace(/1/g, "");
var decimalSeparator = numberFormat.format(1.1).replace(/1/g, "");
return parseFloat(
stringNumber
.replace(new RegExp("\\" + thousandSeparator, "g"), "")
.replace(new RegExp("\\" + decimalSeparator), ".")
);
}
});
return this;
};
function onPointerUp(element, callback) {
element.addEventListener("mouseup", function (e) {
callback(e);
});
element.addEventListener("touchend", function (e) {
callback(e);
});
element.addEventListener("keyup", function (e) {
if (e.keyCode === 32 || e.keyCode === 13) {
triggerKeyPressed = false;
callback(e);
}
});
}
function onPointerDown(element, callback) {
element.addEventListener("mousedown", function (e) {
e.preventDefault();
callback(e);
});
element.addEventListener("touchstart", function (e) {
if (e.cancelable) {
e.preventDefault();
}
callback(e);
});
element.addEventListener("keydown", function (e) {
if ((e.keyCode === 32 || e.keyCode === 13) && !triggerKeyPressed) {
triggerKeyPressed = true;
callback(e);
}
});
}
})(jQuery);

168
public/js/plugins/checkBo.min.js vendored Normal file
View file

@ -0,0 +1,168 @@
/*
* checkBo lightweight jQuery plugin v0.1.4 by @ElmahdiMahmoud
* Licensed under the MIT license - https://github.com/elmahdim/checkbo/blob/master/LICENSE
*
* Custom checkbox and radio
* Author URL: elmahdim.com
*/
!(function (e) {
e.fn.checkBo = function (c) {
return (
(c = e.extend(
{},
{
checkAllButton: null,
checkAllTarget: null,
checkAllTextDefault: null,
checkAllTextToggle: null,
},
c
)),
this.each(function () {
function t(e) {
this.input = e;
}
function n() {
var c = e(this).is(":checked");
e(this).closest("label").toggleClass("checked", c);
}
function i(e, c, t) {
e.text(e.parent(a).hasClass("checked") ? t : c);
}
function h(c) {
var t = c.attr("data-show");
(c = c.attr("data-hide")),
e(t).removeClass("is-hidden"),
e(c).addClass("is-hidden");
}
var l = e(this),
a = l.find(".cb-checkbox"),
d = l.find(".cb-radio"),
o = l.find(".cb-switcher"),
s = a.find("input:checkbox"),
f = d.find("input:radio");
s.wrap('<span class="cb-inner"><i></i></span>'),
f.wrap('<span class="cb-inner"><i></i></span>');
var k = new t("input:checkbox"),
r = new t("input:radio");
if (
((t.prototype.checkbox = function (e) {
var c = e.find(this.input).is(":checked");
e.find(this.input)
.prop("checked", !c)
.trigger("change");
}),
(t.prototype.radiobtn = function (c, t) {
var n = e('input:radio[name="' + t + '"]');
n.prop("checked", !1),
n.closest(n.closest(d)).removeClass("checked"),
c.addClass("checked"),
(c.find(this.input).get(0).checked = c.hasClass(
"checked"
)),
c.find(this.input).change();
}),
s.on("change", n),
f.on("change", n),
a.find("a").on("click", function (e) {
e.stopPropagation();
}),
a.on("click", function (c) {
c.preventDefault(),
k.checkbox(e(this)),
(c = e(this).attr("data-toggle")),
e(c).toggleClass("is-hidden"),
h(e(this));
}),
d.on("click", function (c) {
c.preventDefault(),
r.radiobtn(
e(this),
e(this).find("input:radio").attr("name")
),
h(e(this));
}),
(e.fn.toggleCheckbox = function () {
this.prop("checked", !this.is(":checked"));
}),
(e.fn.switcherChecker = function () {
var c = e(this),
t = c.find("input"),
n = c.find(".cb-state");
t.is(":checked")
? (c.addClass("checked"),
n.html(t.attr("data-state-on")))
: (c.removeClass("checked"),
n.html(t.attr("data-state-off")));
}),
o.on("click", function (c) {
c.preventDefault(),
(c = e(this)),
c.find("input").toggleCheckbox(),
c.switcherChecker(),
e(c.attr("data-toggle")).toggleClass("is-hidden");
}),
o.each(function () {
e(this).switcherChecker();
}),
c.checkAllButton && c.checkAllTarget)
) {
var u = e(this);
u.find(e(c.checkAllButton)).on("click", function () {
u
.find(c.checkAllTarget)
.find("input:checkbox")
.each(function () {
u.find(e(c.checkAllButton)).hasClass("checked")
? u
.find(c.checkAllTarget)
.find("input:checkbox")
.prop("checked", !0)
.change()
: u
.find(c.checkAllTarget)
.find("input:checkbox")
.prop("checked", !1)
.change();
}),
i(
u
.find(e(c.checkAllButton))
.find(".toggle-text"),
c.checkAllTextDefault,
c.checkAllTextToggle
);
}),
u
.find(c.checkAllTarget)
.find(a)
.on("click", function () {
u
.find(c.checkAllButton)
.find("input:checkbox")
.prop("checked", !1)
.change()
.removeClass("checked"),
i(
u
.find(e(c.checkAllButton))
.find(".toggle-text"),
c.checkAllTextDefault,
c.checkAllTextToggle
);
});
}
l
.find('[checked="checked"]')
.closest("label")
.addClass("checked"),
l.find("input").is("input:disabled") &&
l
.find("input:disabled")
.closest("label")
.off()
.addClass("disabled");
})
);
};
})(jQuery, window, document);

View file

@ -0,0 +1,251 @@
/*
Twitter Bootstrap Toogle Buttons 0.0.3
Available under the MIT license.
See https://github.com/prokki/twbs-toggle-buttons for more information.
*/
//** The file has been modified
var $jscomp = $jscomp || {};
$jscomp.scope = {};
$jscomp.findInternal = function (a, b, c) {
a instanceof String && (a = String(a));
for (var d = a.length, e = 0; e < d; e++) {
var f = a[e];
if (b.call(c, f, e, a)) return { i: e, v: f };
}
return { i: -1, v: void 0 };
};
$jscomp.ASSUME_ES5 = !1;
$jscomp.ASSUME_NO_NATIVE_MAP = !1;
$jscomp.ASSUME_NO_NATIVE_SET = !1;
$jscomp.SIMPLE_FROUND_POLYFILL = !1;
$jscomp.defineProperty =
$jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties
? Object.defineProperty
: function (a, b, c) {
a != Array.prototype && a != Object.prototype && (a[b] = c.value);
};
$jscomp.getGlobal = function (a) {
return "undefined" != typeof window && window === a
? a
: "undefined" != typeof global && null != global
? global
: a;
};
$jscomp.global = $jscomp.getGlobal(this);
$jscomp.polyfill = function (a, b, c, d) {
if (b) {
c = $jscomp.global;
a = a.split(".");
for (d = 0; d < a.length - 1; d++) {
var e = a[d];
e in c || (c[e] = {});
c = c[e];
}
a = a[a.length - 1];
d = c[a];
b = b(d);
b != d &&
null != b &&
$jscomp.defineProperty(c, a, {
configurable: !0,
writable: !0,
value: b,
});
}
};
$jscomp.polyfill(
"Array.prototype.find",
function (a) {
return a
? a
: function (a, c) {
return $jscomp.findInternal(this, a, c).v;
};
},
"es6",
"es3"
);
var TwbsToggleButtons = function (a, b) {
this.$_element = a;
this._initializeOptions(b);
this._initializeDOM();
this.$_element
.find(this._options.twbsBtnSelector)
.on("click", this._eventClick.bind(this));
this.$_element.data("twbsToggleButtons", this);
};
TwbsToggleButtons.TYPE_RADIO = function () {
return 1;
};
TwbsToggleButtons.TYPE_CHECKBOX = function () {
return 2;
};
TwbsToggleButtons.DEFAULTS = function () {
return {
twbsBtnSelector: "[role='button']",
classActive: "active-btn",
classInactive: "inactive-btn",
};
};
TwbsToggleButtons.ACTIVE_CLASS = function () {
return "active";
};
TwbsToggleButtons.prototype._getInputType = function () {
var a = 0,
b = 0;
this.$_element.find(":input").each(function () {
if ("radio" === this.getAttribute("type")) a++;
else if ("checkbox" === this.getAttribute("type")) b++;
else
throw (
"All input fields must be either of type 'radio' or of type 'checkbox, found '" +
this.getAttribute("type") +
"'"
);
});
if (0 !== a && 0 !== b)
throw "All input fields must be either of type 'radio' or of type 'checkbox, found both.";
return 0 < b
? TwbsToggleButtons.TYPE_CHECKBOX()
: TwbsToggleButtons.TYPE_RADIO();
};
TwbsToggleButtons.prototype._initializeOptions = function (a) {
this._options = $.extend({}, TwbsToggleButtons.DEFAULTS(), a || {});
"string" === typeof this._options.classActive &&
(this._options.classActive = [this._options.classActive]);
"string" === typeof this._options.classInactive &&
(this._options.classInactive = [this._options.classInactive]);
};
TwbsToggleButtons.prototype._resetDOM = function (a) {
this.$_element.find(this._options.twbsBtnSelector).each(
function (b, c) {
-1 !== a.indexOf(c) ? this._activateButton(c) : this._deactivateButton(c);
}.bind(this)
);
};
TwbsToggleButtons.prototype._initializeDOM = function () {
var a = this.$_element.find(this._options.twbsBtnSelector),
b = a.filter("." + TwbsToggleButtons.ACTIVE_CLASS()).toArray();
1 < b.length &&
this._getInputType() === TwbsToggleButtons.TYPE_RADIO() &&
(b = [b.pop()]);
a.each(
function (a, d) {
-1 !== b.indexOf(d)
? d.setAttribute("aria-pressed", "true")
: d.setAttribute("aria-pressed", "false");
}.bind(this)
);
this._resetDOM(b);
};
TwbsToggleButtons.prototype._eventClick = function (a) {
var b = this.$_element
.find(this._options.twbsBtnSelector)
.filter(function () {
return "true" === this.getAttribute("aria-pressed");
})
.toArray(),
c = a.currentTarget;
this._getInputType() === TwbsToggleButtons.TYPE_RADIO()
? ((b = [c]),
"true" === c.getAttribute("aria-pressed") &&
(0 ===
this.$_element
.find(this._options.twbsBtnSelector)
.find(":input[required]").length
? (b = [])
: a.stopPropagation()))
: "true" === c.getAttribute("aria-pressed") && -1 !== b.indexOf(c)
? b.splice(b.indexOf(c), 1)
: b.push(c);
this._resetDOM(b);
window.setTimeout(function () {
a.target.dispatchEvent(new Event("twbsToggleButtons:activate"));
}, 0);
return !0;
};
TwbsToggleButtons.prototype._activateButton = function (a) {
void 0 !== a.dataset.twbsToggleButtonsClassActive &&
0 < a.dataset.twbsToggleButtonsClassActive.length
? a.classList.add(a.dataset.twbsToggleButtonsClassActive)
: this._options.classActive.forEach(function (b) {
a.classList.add(b);
});
this._options.classInactive.forEach(function (b) {
a.classList.remove(b);
});
$(a).find(":input").prop("checked", !0);
$(a).find(":input").attr("checked", "checked");
(this._getInputType() !== TwbsToggleButtons.TYPE_RADIO() &&
this._getInputType() !== TwbsToggleButtons.TYPE_CHECKBOX()) ||
"false" !== a.getAttribute("aria-pressed") ||
window.setTimeout(function () {
a.classList.remove(TwbsToggleButtons.ACTIVE_CLASS());
a.setAttribute("aria-pressed", "true");
}, 0);
};
TwbsToggleButtons.prototype._deactivateButton = function (a) {
void 0 !== a.dataset.twbsToggleButtonsClassActive &&
0 < a.dataset.twbsToggleButtonsClassActive.length &&
a.classList.remove(a.dataset.twbsToggleButtonsClassActive);
this._options.classActive.forEach(function (b) {
a.classList.remove(b);
});
this._options.classInactive.forEach(function (b) {
a.classList.add(b);
});
$(a).find(":input").prop("checked", !1);
$(a).find(":input").attr("checked", null);
(this._getInputType() !== TwbsToggleButtons.TYPE_RADIO() &&
this._getInputType() !== TwbsToggleButtons.TYPE_CHECKBOX()) ||
"true" !== a.getAttribute("aria-pressed") ||
window.setTimeout(function () {
a.classList.remove(TwbsToggleButtons.ACTIVE_CLASS());
a.setAttribute("aria-pressed", "false");
}, 0);
};
(function (a) {
null == a.fn.twbsToggleButtons &&
(a.fn.twbsToggleButtons = function (b) {
var c = ["clear", "destroy"];
b = b || {};
if ("object" === typeof b)
return (
this.each(function () {
var c = a.extend(!0, {}, b);
void 0 === a(this).data("twbsToggleButtons")
? new TwbsToggleButtons(a(this), c)
: ((c = a(this).data("twbsToggleButtons")),
null === c &&
window.console &&
console.error &&
console.warn(
"Error on Initialization as TwbsToggleButtons: " + this
));
}),
this
);
if ("string" === typeof b) {
var d,
e = Array.prototype.slice.call(arguments, 1);
this.each(function () {
var c = a(this).data("twbsToggleButtons");
null == c && window.console && console.error
? console.error(
"The twbsToggleButtons('" +
b +
"') method was called on an element that is not using TwbsToggleButtons."
)
: "function" !== typeof c[b] &&
console.error(
"Method '" + b + "' is not implemented in TwbsToggleButtons."
);
d = c[b].apply(c, e);
});
return -1 < a.inArray(b, c) ? this : d;
}
throw Error("Invalid arguments for TwbsToggleButtons: " + b);
});
})(jQuery);

202
public/js/ram.js Normal file
View file

@ -0,0 +1,202 @@
/**
* RAM Component.
*
* Two settings are available:
* - addressWidth: 1 to 20, default=10. Controls the width of the address input.
* - bitWidth: 1 to 32, default=8. Controls the width of data pins.
*
* Amount of memory in the element is 2^addressWidth x bitWidth bits.
* Minimum RAM size is: 2^1 x 1 = 2 bits.
* Maximum RAM size is: 2^20 x 32 = 1M x 32 bits => 32 Mbits => 4MB.
* Maximum 8-bits size: 2^20 x 8 = 1M x 8 bits => 1MB.
* Default RAM size is: 2^10 x 8 = 1024 bytes => 1KB.
*
* RAMs are volatile therefore this component does not persist the memory contents.
*
* Changes to addressWidth and bitWidth also cause data to be lost.
* Think of these operations as being equivalent to taking a piece of RAM out of a
* circuit board and replacing it with another RAM of different size.
*
* The contents of the RAM can be reset to zero by setting the RESET pin 1 or
* or by selecting the component and pressing the "Reset" button in the properties window.
*
* The contents of the RAM can be dumped to the console by transitioning CORE DUMP pin to 1
* or by selecting the component and pressing the "Core Dump" button in the properties window.
* Address spaces that have not been written will show up as `undefined` in the core dump.
*
* NOTE: The maximum address width of 20 is arbitrary.
* Larger values are possible, but in practice circuits won't need this much
* memory and keeping the value small helps avoid allocating too much memory on the browser.
* Internally we use a sparse array, so only the addresses that are written are actually
* allocated. Nevertheless, it is better to prevent large allocations from happening
* by keeping the max addressWidth small. If needed, we can increase the max.
*/
function RAM(x, y, scope = globalScope, dir = "RIGHT", bitWidth = 8, addressWidth = 10) {
CircuitElement.call(this, x, y, scope, dir, Math.min(Math.max(1, bitWidth), 32));
this.setDimensions(60, 40);
this.directionFixed = true;
this.labelDirection = "UP";
this.addressWidth = Math.min(Math.max(1, addressWidth), this.maxAddressWidth);
this.address = new Node(-this.leftDimensionX, -20, 0, this, this.addressWidth, "ADDRESS");
this.dataIn = new Node(-this.leftDimensionX, 0, 0, this, this.bitWidth, "DATA IN");
this.write = new Node(-this.leftDimensionX, 20, 0, this, 1, "WRITE");
this.reset = new Node(0, this.downDimensionY, 0, this, 1, "RESET");
this.coreDump = new Node(-20, this.downDimensionY, 0, this, 1, "CORE DUMP");
this.dataOut = new Node(this.rightDimensionX, 0, 1, this, this.bitWidth, "DATA OUT");
this.prevCoreDumpValue = undefined;
this.clearData()
}
RAM.prototype = Object.create(CircuitElement.prototype);
RAM.prototype.tooltipText = "Random Access Memory";
RAM.prototype.shortName = "RAM";
RAM.prototype.maxAddressWidth = 20;
RAM.prototype.constructor = RAM;
RAM.prototype.helplink = "https://docs.circuitverse.org/#/memoryElements?id=ram";
RAM.prototype.mutableProperties = {
"addressWidth": {
name: "Address Width",
type: "number",
max: "20",
min: "1",
func: "changeAddressWidth",
},
"dump": {
name: "Core Dump",
type: "button",
func: "dump",
},
"reset": {
name: "Reset",
type: "button",
func: "clearData",
},
}
RAM.prototype.customSave = function () {
return {
// NOTE: data is not persisted since RAMs are volatile.
constructorParamaters: [this.direction, this.bitWidth, this.addressWidth],
nodes: {
address: findNode(this.address),
dataIn: findNode(this.dataIn),
write: findNode(this.write),
reset: findNode(this.reset),
coreDump: findNode(this.coreDump),
dataOut: findNode(this.dataOut),
},
}
}
RAM.prototype.newBitWidth = function (value) {
value = parseInt(value);
if (!isNaN(value) && this.bitWidth != value && value >= 1 && value <= 32) {
this.bitWidth = value;
this.dataIn.bitWidth = value;
this.dataOut.bitWidth = value;
this.clearData();
}
}
RAM.prototype.changeAddressWidth = function (value) {
value = parseInt(value);
if (!isNaN(value) && this.addressWidth != value && value >= 1 && value <= this.maxAddressWidth) {
this.addressWidth = value;
this.address.bitWidth = value;
this.clearData();
}
}
RAM.prototype.clearData = function () {
this.data = new Array(Math.pow(2, this.addressWidth));
this.tooltipText = this.memSizeString() + " " + this.shortName;
}
RAM.prototype.isResolvable = function () {
return this.address.value !== undefined || this.reset.value !== undefined || this.coreDump.value !== undefined;
}
RAM.prototype.resolve = function () {
if (this.write.value == 1) {
this.data[this.address.value] = this.dataIn.value;
}
if (this.reset.value == 1) {
this.clearData();
}
if (this.coreDump.value && this.prevCoreDumpValue != this.coreDump.value) {
this.dump();
}
this.prevCoreDumpValue = this.coreDump.value;
this.dataOut.value = this.data[this.address.value] || 0;
simulationArea.simulationQueue.add(this.dataOut);
}
RAM.prototype.customDraw = function () {
var ctx = simulationArea.context;
var xx = this.x;
var yy = this.y;
ctx.beginPath();
ctx.strokeStyle = "gray";
ctx.fillStyle = this.write.value ? "red" : "lightgreen";
ctx.lineWidth = correctWidth(1);
drawCircle2(ctx, 50, -30, 3, xx, yy, this.direction);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
fillText4(ctx, this.memSizeString(), 0, -10, xx, yy, this.direction, 12);
fillText4(ctx, this.shortName, 0, 10, xx, yy, this.direction, 12);
fillText2(ctx, "A", this.address.x + 12, this.address.y, xx, yy, this.direction);
fillText2(ctx, "DI", this.dataIn.x + 12, this.dataIn.y, xx, yy, this.direction);
fillText2(ctx, "W", this.write.x + 12, this.write.y, xx, yy, this.direction);
fillText2(ctx, "DO", this.dataOut.x - 15, this.dataOut.y, xx, yy, this.direction);
ctx.fill();
}
RAM.prototype.memSizeString = function () {
var mag = ['', 'K', 'M'];
var unit = this.bitWidth == 8 ? "B" : this.bitWidth == 1 ? "b" : " x " + this.bitWidth + 'b';
var v = Math.pow(2, this.addressWidth);
var m = 0;
while (v >= 1024 && m < mag.length - 1) {
v /= 1024;
m++;
}
return v + mag[m] + unit;
}
RAM.prototype.dump = function () {
var logLabel = console.group && this.label;
if (logLabel) {
console.group(this.label);
}
console.log(this.data)
if (logLabel) {
console.groupEnd();
}
}
//This is a RAM without a clock - not normal
//reset is not supported
RAM.moduleVerilog = function () {
return `
module RAM(dout, addr, din, we, dmp, rst);
parameter WIDTH = 8;
parameter ADDR = 10;
output [WIDTH-1:0] dout;
input [ADDR-1:0] addr;
input [WIDTH-1:0] din;
input we;
input dmp;
input rst;
reg [WIDTH-1:0] mem [2**ADDR-1:0];
assign dout = mem[addr];
always @ (*) begin
if (!we)
mem[addr] = din;
end
endmodule
`;
}

30
public/js/search.js Normal file
View file

@ -0,0 +1,30 @@
/* jshint esversion: 6 */
$(document).ready(() => {
// Highlight searched text
var searchText = $('.navbar-search-bar-input').val().trim();
if (searchText !== '') {
$('.search-container').find('.search-project-name, .search-project-description p').each(function highlight() {
// Loop over each result
var text = $(this).html();
var currentPos = 0;
loop = true;
while (loop && currentPos < text.length) { // Loop over all text and highlight any occurences of the search term
var occurence = text.toLowerCase().indexOf(searchText.toLowerCase(), currentPos);
if (occurence === -1) {
loop = false;
} else {
text = `${text.substring(0, occurence)}<span class="search-match">${text.substring(occurence, occurence + searchText.length)}</span>${text.substring(occurence + searchText.length)}`;
}
currentPos += 34 + searchText.length;
}
$(this).html(text);
});
}
});

1072
public/js/sequential.js Normal file

File diff suppressed because it is too large Load diff

411
public/js/subcircuit.js Normal file
View file

@ -0,0 +1,411 @@
function loadSubCircuit(savedData, scope) {
var v = new SubCircuit(savedData["x"], savedData["y"], scope, savedData["id"], savedData);
// if(v.version == "1.0"){
// v.version = "2.0";
// v.x-=v.width/2;
// v.y-=v.height/2;
// }
}
//subCircuit class
function SubCircuit(x, y, scope = globalScope, id = undefined, savedData = undefined) {
this.id = id || prompt("Enter Id: ");
var subcircuitScope = scopeList[this.id]; // Scope of the subcircuit
// Error handing
if (subcircuitScope == undefined) {
showError("SubCircuit : " + ((savedData && savedData.title) || this.id) + " Not found");
} else if (!checkIfBackup(subcircuitScope)) {
showError("SubCircuit : " + ((savedData && savedData.title) || subcircuitScope.name) + " is an empty circuit");
} else if (subcircuitScope.checkDependency(scope.id)) {
showError("Cyclic Circuit Error");
}
// Error handling, cleanup
if (subcircuitScope == undefined || subcircuitScope.checkDependency(scope.id) || !checkIfBackup(subcircuitScope)) {
if (savedData) {
for (var i = 0; i < savedData["inputNodes"].length; i++) {
scope.allNodes[savedData["inputNodes"][i]].deleted = true;
}
for (var i = 0; i < savedData["outputNodes"].length; i++) {
scope.allNodes[savedData["outputNodes"][i]].deleted = true;
}
}
return;
}
CircuitElement.call(this, x, y, scope, "RIGHT", 1);
this.directionFixed = true;
this.fixedBitWidth = true;
this.savedData = savedData;
this.inputNodes = [];
this.outputNodes = [];
this.localScope = new Scope();
if (this.savedData != undefined) {
updateSubcircuit = true;
scheduleUpdate();
this.version = this.savedData["version"] || "1.0";
this.id = this.savedData["id"];
for (var i = 0; i < this.savedData["inputNodes"].length; i++) {
this.inputNodes.push(this.scope.allNodes[this.savedData["inputNodes"][i]]);
this.inputNodes[i].parent = this;
this.inputNodes[i].layout_id = subcircuitScope.Input[i].layoutProperties.id
}
for (var i = 0; i < this.savedData["outputNodes"].length; i++) {
this.outputNodes.push(this.scope.allNodes[this.savedData["outputNodes"][i]]);
this.outputNodes[i].parent = this;
this.outputNodes[i].layout_id = subcircuitScope.Output[i].layoutProperties.id;
}
if (this.version == "1.0") { // For backward compatibility
this.version = "2.0";
this.x -= subcircuitScope.layout.width / 2;
this.y -= subcircuitScope.layout.height / 2;
for (var i = 0; i < this.inputNodes.length; i++) {
this.inputNodes[i].x = subcircuitScope.Input[i].layoutProperties.x;
this.inputNodes[i].y = subcircuitScope.Input[i].layoutProperties.y;
this.inputNodes[i].leftx = this.inputNodes[i].x;
this.inputNodes[i].lefty = this.inputNodes[i].y;
}
for (var i = 0; i < this.outputNodes.length; i++) {
this.outputNodes[i].x = subcircuitScope.Output[i].layoutProperties.x;
this.outputNodes[i].y = subcircuitScope.Output[i].layoutProperties.y;
this.outputNodes[i].leftx = this.outputNodes[i].x;
this.outputNodes[i].lefty = this.outputNodes[i].y;
}
}
if (this.version == "2.0") {
this.leftDimensionX = 0;
this.upDimensionY = 0;
this.rightDimensionX = subcircuitScope.layout.width;
this.downDimensionY = subcircuitScope.layout.height;
}
this.nodeList.extend(this.inputNodes);
this.nodeList.extend(this.outputNodes);
} else {
this.version = "2.0";
}
this.data = JSON.parse(scheduleBackup(subcircuitScope));
this.buildCircuit();
this.makeConnections();
}
SubCircuit.prototype = Object.create(CircuitElement.prototype);
SubCircuit.prototype.constructor = SubCircuit;
SubCircuit.prototype.makeConnections = function() {
for (let i = 0; i < this.inputNodes.length; i++) {
this.localScope.Input[i].output1.connectWireLess(this.inputNodes[i]);
this.localScope.Input[i].output1.subcircuitOverride = true;
}
for (let i = 0; i < this.outputNodes.length; i++) {
this.localScope.Output[i].inp1.connectWireLess(this.outputNodes[i]);
this.outputNodes[i].subcircuitOverride = true;
}
}
SubCircuit.prototype.removeConnections = function() {
for (let i = 0; i < this.inputNodes.length; i++) {
this.localScope.Input[i].output1.disconnectWireLess(this.inputNodes[i]);
}
for (let i = 0; i < this.outputNodes.length; i++)
this.localScope.Output[i].inp1.disconnectWireLess(this.outputNodes[i]);
}
SubCircuit.prototype.buildCircuit = function() {
var subcircuitScope = scopeList[this.id];
loadScope(this.localScope, this.data);
this.lastUpdated = this.localScope.timeStamp;
updateSimulation = true;
updateCanvas = true;
if (this.savedData == undefined) {
this.leftDimensionX = 0;
this.upDimensionY = 0;
this.rightDimensionX = subcircuitScope.layout.width;
this.downDimensionY = subcircuitScope.layout.height;
for (var i = 0; i < subcircuitScope.Output.length; i++) {
var a = new Node(subcircuitScope.Output[i].layoutProperties.x, subcircuitScope.Output[i].layoutProperties.y, 1, this, subcircuitScope.Output[i].bitWidth);
a.layout_id = subcircuitScope.Output[i].layoutProperties.id;
this.outputNodes.push(a);
}
for (var i = 0; i < subcircuitScope.Input.length; i++) {
var a = new Node(subcircuitScope.Input[i].layoutProperties.x, subcircuitScope.Input[i].layoutProperties.y, 0, this, subcircuitScope.Input[i].bitWidth);
a.layout_id = subcircuitScope.Input[i].layoutProperties.id;
this.inputNodes.push(a);
}
}
}
// Needs to be deprecated, removed
SubCircuit.prototype.reBuild = function() {
return;
new SubCircuit(x = this.x, y = this.y, scope = this.scope, this.id)
this.scope.backups = []; // Because all previous states are invalid now
this.delete();
showMessage("Subcircuit: " + subcircuitScope.name + " has been reloaded.")
}
SubCircuit.prototype.reBuildCircuit = function() {
this.data = JSON.parse(scheduleBackup(scopeList[this.id]));
this.localScope = new Scope();
loadScope(this.localScope, this.data);
this.lastUpdated = this.localScope.timeStamp;
this.scope.timeStamp = this.localScope.timeStamp;
}
SubCircuit.prototype.reset = function() {
this.removeConnections();
var subcircuitScope = scopeList[this.id];
for (var i = 0; i < subcircuitScope.SubCircuit.length; i++) {
subcircuitScope.SubCircuit[i].reset();
}
if (subcircuitScope.Input.length == 0 && subcircuitScope.Output.length == 0) {
showError("SubCircuit : " + subcircuitScope.name + " is an empty circuit");
this.delete();
this.scope.backups = [];
return;
}
subcircuitScope.layout.height = subcircuitScope.layout.height;
subcircuitScope.layout.width = subcircuitScope.layout.width;
this.leftDimensionX = 0;
this.upDimensionY = 0;
this.rightDimensionX = subcircuitScope.layout.width;
this.downDimensionY = subcircuitScope.layout.height;
var temp_map_inp = {}
for (var i = 0; i < subcircuitScope.Input.length; i++) {
temp_map_inp[subcircuitScope.Input[i].layoutProperties.id] = [subcircuitScope.Input[i], undefined];
}
for (var i = 0; i < this.inputNodes.length; i++) {
if (temp_map_inp.hasOwnProperty(this.inputNodes[i].layout_id)) {
temp_map_inp[this.inputNodes[i].layout_id][1] = this.inputNodes[i];
} else {
this.scope.backups = [];
this.inputNodes[i].delete();
this.nodeList.clean(this.inputNodes[i])
}
}
for (id in temp_map_inp) {
if (temp_map_inp[id][1]) {
if (temp_map_inp[id][0].layoutProperties.x == temp_map_inp[id][1].x && temp_map_inp[id][0].layoutProperties.y == temp_map_inp[id][1].y)
temp_map_inp[id][1].bitWidth = temp_map_inp[id][0].bitWidth;
else {
this.scope.backups = [];
temp_map_inp[id][1].delete();
this.nodeList.clean(temp_map_inp[id][1]);
temp_map_inp[id][1] = new Node(temp_map_inp[id][0].layoutProperties.x, temp_map_inp[id][0].layoutProperties.y, 0, this, temp_map_inp[id][0].bitWidth)
temp_map_inp[id][1].layout_id = id;
}
}
}
this.inputNodes = []
for (var i = 0; i < subcircuitScope.Input.length; i++) {
var input = temp_map_inp[subcircuitScope.Input[i].layoutProperties.id][0]
if (temp_map_inp[input.layoutProperties.id][1])
this.inputNodes.push(temp_map_inp[input.layoutProperties.id][1])
else {
var a = new Node(input.layoutProperties.x, input.layoutProperties.y, 0, this, input.bitWidth);
a.layout_id = input.layoutProperties.id;
this.inputNodes.push(a);
}
}
var temp_map_out = {}
for (var i = 0; i < subcircuitScope.Output.length; i++) {
temp_map_out[subcircuitScope.Output[i].layoutProperties.id] = [subcircuitScope.Output[i], undefined];
}
for (var i = 0; i < this.outputNodes.length; i++) {
if (temp_map_out.hasOwnProperty(this.outputNodes[i].layout_id)) {
temp_map_out[this.outputNodes[i].layout_id][1] = this.outputNodes[i];
} else {
this.outputNodes[i].delete();
this.nodeList.clean(this.outputNodes[i])
}
}
for (id in temp_map_out) {
if (temp_map_out[id][1]) {
if (temp_map_out[id][0].layoutProperties.x == temp_map_out[id][1].x && temp_map_out[id][0].layoutProperties.y == temp_map_out[id][1].y)
temp_map_out[id][1].bitWidth = temp_map_out[id][0].bitWidth;
else {
temp_map_out[id][1].delete();
this.nodeList.clean(temp_map_out[id][1])
temp_map_out[id][1] = new Node(temp_map_out[id][0].layoutProperties.x, temp_map_out[id][0].layoutProperties.y, 1, this, temp_map_out[id][0].bitWidth)
temp_map_out[id][1].layout_id = id
}
}
}
this.outputNodes = []
for (var i = 0; i < subcircuitScope.Output.length; i++) {
var output = temp_map_out[subcircuitScope.Output[i].layoutProperties.id][0]
if (temp_map_out[output.layoutProperties.id][1])
this.outputNodes.push(temp_map_out[output.layoutProperties.id][1])
else {
var a = new Node(output.layoutProperties.x, output.layoutProperties.y, 1, this, output.bitWidth);
a.layout_id = output.layoutProperties.id;
this.outputNodes.push(a);
}
}
if (subcircuitScope.timeStamp > this.lastUpdated) {
this.reBuildCircuit();
}
this.localScope.reset();
this.makeConnections();
}
SubCircuit.prototype.click = function() {
// this.id=prompt();
}
SubCircuit.prototype.addInputs = function() {
for (let i = 0; i < subCircuitInputList.length; i++)
for (let j = 0; j < this.localScope[subCircuitInputList[i]].length; j++)
simulationArea.simulationQueue.add(this.localScope[subCircuitInputList[i]][j], 0)
for (let j = 0; j < this.localScope.SubCircuit.length; j++)
this.localScope.SubCircuit[j].addInputs();
}
SubCircuit.prototype.isResolvable = function() {
if (CircuitElement.prototype.isResolvable.call(this))
return true;
return false;
}
SubCircuit.prototype.dblclick = function() {
switchCircuit(this.id)
}
SubCircuit.prototype.saveObject = function() {
var data = {
x: this.x,
y: this.y,
id: this.id,
inputNodes: this.inputNodes.map(findNode),
outputNodes: this.outputNodes.map(findNode),
version: this.version,
}
return data;
}
SubCircuit.prototype.resolve = function() {
// deprecated
// var subcircuitScope = this.localScope;//scopeList[this.id];
// // this.scope.pending.clean(this); // To remove any pending instances
// // return;
//
// for (i = 0; i < subcircuitScope.Input.length; i++) {
// subcircuitScope.Input[i].state = this.inputNodes[i].value;
// }
//
// for (i = 0; i < subcircuitScope.Input.length; i++) {
// simulationArea.simulationQueue.add(subcircuitScope.Input[i]);
// }
// play(subcircuitScope);
//
// for (i = 0; i < subcircuitScope.Output.length; i++) {
// this.outputNodes[i].value = subcircuitScope.Output[i].inp1.value;
// }
// for (i = 0; i < subcircuitScope.Output.length; i++) {
// this.scope.stack.push(this.outputNodes[i]);
// }
}
SubCircuit.prototype.isResolvable = function() {
return false
}
SubCircuit.prototype.verilogName = function() {
return verilog.sanitizeLabel(scopeList[this.id].name);
}
SubCircuit.prototype.customDraw = function() {
var subcircuitScope = scopeList[this.id];
ctx = simulationArea.context;
ctx.lineWidth = globalScope.scale * 3;
ctx.strokeStyle = "black"; //("rgba(0,0,0,1)");
ctx.fillStyle = "white";
var xx = this.x;
var yy = this.y;
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
if (this.version == "1.0")
fillText(ctx, subcircuitScope.name, xx, yy - subcircuitScope.layout.height / 2 + 13, 11);
else if (this.version == "2.0"){
if(subcircuitScope.layout.titleEnabled){
fillText(ctx, subcircuitScope.name, subcircuitScope.layout.title_x + xx, yy + subcircuitScope.layout.title_y, 11);
}
}
else
console.log(this.version)
for (var i = 0; i < subcircuitScope.Input.length; i++) {
if (!subcircuitScope.Input[i].label) continue;
var info = this.determine_label(this.inputNodes[i].x, this.inputNodes[i].y);
ctx.textAlign = info[0];
fillText(ctx, subcircuitScope.Input[i].label, this.inputNodes[i].x + info[1] + xx, yy + this.inputNodes[i].y + info[2], 12);
}
for (var i = 0; i < subcircuitScope.Output.length; i++) {
if (!subcircuitScope.Output[i].label) continue;
var info = this.determine_label(this.outputNodes[i].x, this.outputNodes[i].y);
ctx.textAlign = info[0];
fillText(ctx, subcircuitScope.Output[i].label, this.outputNodes[i].x + info[1] + xx, yy + this.outputNodes[i].y + info[2], 12);
}
ctx.fill();
for (var i = 0; i < this.inputNodes.length; i++)
this.inputNodes[i].draw();
for (var i = 0; i < this.outputNodes.length; i++)
this.outputNodes[i].draw();
}
SubCircuit.prototype.centerElement = true; // To center subcircuit when new
SubCircuit.prototype.determine_label = function(x, y) {
if (x == 0) return ["left", 5, 5]
if (x == scopeList[this.id].layout.width) return ["right", -5, 5]
if (y == 0) return ["center", 0, 13]
return ["center", 0, -6]
}

76
public/js/test.js Normal file

File diff suppressed because one or more lines are too long

547
public/js/testBench.js Normal file
View file

@ -0,0 +1,547 @@
function TB_Input(x, y, scope = globalScope, dir = "RIGHT", identifier, testData) {
CircuitElement.call(this, x, y, scope, dir, 1);
// this.setDimensions(60,20);
// this.xSize=10;
// this.plotValues = [];
// this.inp1 = new Node(0, 0, 0, this);
// this.inp1 = new Node(100, 100, 0, this);
this.setIdentifier (identifier|| "Test1");
this.testData=testData || {"inputs":[],"outputs":[],"n":0}
this.clockInp = new Node(0,20, 0,this,1);
this.outputs = []
this.running = false;
this.iteration=0;
this.setup();
}
TB_Input.prototype = Object.create(CircuitElement.prototype);
TB_Input.prototype.constructor = TB_Input;
TB_Input.prototype.tooltipText = "Test Bench Input Selected";
TB_Input.prototype.centerElement = true;
TB_Input.prototype.helplink = "https://docs.circuitverse.org/#/testbench";
TB_Input.prototype.dblclick=function(){
this.testData=JSON.parse(prompt("Enter TestBench Json"));
this.setup();
}
TB_Input.prototype.setDimensions=function() {
this.leftDimensionX = 0;
this.rightDimensionX = 120;
this.upDimensionY = 0;
this.downDimensionY = 40 + this.testData.inputs.length * 20;
}
TB_Input.prototype.setup=function(){
this.iteration = 0;
this.running = false;
this.nodeList.clean(this.clockInp);
this.deleteNodes();
this.nodeList = []
this.nodeList.push(this.clockInp);
this.testData = this.testData || { "inputs": [], "outputs": [], "n": 0 };
// this.clockInp = new Node(0,20, 0,this,1);
this.setDimensions();
this.prevClockState = 0;
this.outputs = [];
for(var i = 0; i < this.testData.inputs.length; i++ ){
this.outputs.push(new Node(this.rightDimensionX, 30+i*20, 1, this, bitWidth = this.testData.inputs[i].bitWidth,label=this.testData.inputs[i].label));
}
for(var i =0; i<this.scope.TB_Output.length;i++){
if(this.scope.TB_Output[i].identifier == this.identifier)
this.scope.TB_Output[i].setup();
}
}
TB_Input.prototype.toggleState=function(){
this.running=!this.running;
this.prevClockState=0;
}
TB_Input.prototype.resetIterations=function(){
this.iteration=0;
this.prevClockState=0;
}
TB_Input.prototype.resolve=function(){
if(this.clockInp.value != this.prevClockState){
this.prevClockState = this.clockInp.value;
if(this.clockInp.value == 1 && this.running){
if(this.iteration<this.testData.n){
this.iteration++;
}
else{
this.running = false;
}
}
}
if(this.running && this.iteration)
for(var i=0;i<this.testData.inputs.length;i++){
console.log(this.testData.inputs[i].values[this.iteration-1]);
this.outputs[i].value = parseInt(this.testData.inputs[i].values[this.iteration-1],2);
simulationArea.simulationQueue.add(this.outputs[i]);
}
}
TB_Input.prototype.setPlotValue = function() {
var time = plotArea.stopWatch.ElapsedMilliseconds;
if (this.plotValues.length && this.plotValues[this.plotValues.length - 1][0] == time)
this.plotValues.pop();
if (this.plotValues.length == 0) {
this.plotValues.push([time, this.inp1.value]);
return;
}
if (this.plotValues[this.plotValues.length - 1][1] == this.inp1.value)
return;
else
this.plotValues.push([time, this.inp1.value]);
}
TB_Input.prototype.customSave = function() {
var data = {
constructorParamaters: [this.direction, this.identifier, this.testData],
nodes: {
outputs: this.outputs.map(findNode),
clockInp: findNode(this.clockInp)
},
}
return data;
}
TB_Input.prototype.setIdentifier = function(id = "") {
if (id.length == 0 || id==this.identifier) return;
for(var i =0; i<this.scope.TB_Output.length;i++){
this.scope.TB_Output[i].checkPairing();
}
for(var i =0; i<this.scope.TB_Output.length;i++){
if(this.scope.TB_Output[i].identifier == this.identifier)
this.scope.TB_Output[i].identifier = id;
}
this.identifier = id;
this.checkPaired();
}
TB_Input.prototype.checkPaired=function(){
for(var i =0; i<this.scope.TB_Output.length;i++){
if(this.scope.TB_Output[i].identifier == this.identifier)
this.scope.TB_Output[i].checkPairing();
}
}
TB_Input.prototype.delete = function () {
CircuitElement.prototype.delete.call(this)
this.checkPaired();
}
TB_Input.prototype.mutableProperties = {
"identifier": {
name: "TestBench Name:",
type: "text",
maxlength: "10",
func: "setIdentifier",
},
"iteration": {
name: "Reset Iterations",
type: "button",
func: "resetIterations",
},
"toggleState": {
name: "Toggle State",
type: "button",
func: "toggleState",
},
}
TB_Input.prototype.customDraw = function() {
ctx = simulationArea.context;
ctx.beginPath();
ctx.strokeStyle = "grey";
ctx.fillStyle = "#fcfcfc";
ctx.lineWidth = correctWidth(1);
var xx = this.x;
var yy = this.y;
var xRotate=0;
var yRotate=0;
if(this.direction=="LEFT") {
xRotate=0;
yRotate=0;
}else if(this.direction=="RIGHT") {
xRotate=120-this.xSize;
yRotate=0;
}else if(this.direction=="UP") {
xRotate=60-this.xSize/2;
yRotate=-20;
}else{
xRotate=60-this.xSize/2;
yRotate=20;
}
// rect2(ctx, -120+xRotate+this.xSize, -20+yRotate, 120-this.xSize, 40, xx, yy, "RIGHT");
// if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this))
// ctx.fillStyle = "rgba(255, 255, 32,0.8)";
// ctx.fill();
// ctx.stroke();
//
// ctx.font = "14px Georgia";
// this.xOff = ctx.measureText(this.identifier).width;
// ctx.beginPath();
// rect2(ctx, -105+xRotate+this.xSize, -11+yRotate,this.xOff + 10, 23, xx, yy, "RIGHT");
// ctx.fillStyle = "#eee"
// ctx.strokeStyle = "#ccc";
// ctx.fill();
// ctx.stroke();
//
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
fillText(ctx, this.identifier + " [INPUT]", xx + this.rightDimensionX/ 2 , yy + 14 , 10);
fillText(ctx, ["Not Running","Running"][+this.running], xx + this.rightDimensionX/ 2 , yy + 14 + 10 + 20*this.testData.inputs.length, 10);
fillText(ctx, "Case: "+(this.iteration), xx + this.rightDimensionX/ 2 , yy + 14 + 20 + 20*this.testData.inputs.length, 10);
// fillText(ctx, "Case: "+this.iteration, xx , yy + 20+14, 10);
ctx.fill();
ctx.font = "30px Georgia";
ctx.textAlign = "right";
ctx.fillStyle = "blue";
ctx.beginPath();
for(var i=0;i<this.testData.inputs.length;i++){
// ctx.beginPath();
fillText(ctx, this.testData.inputs[i].label , this.rightDimensionX - 5 + xx , 30+i*20 + yy+4, 10);
}
ctx.fill();
if(this.running && this.iteration){
ctx.font = "30px Georgia";
ctx.textAlign = "left";
ctx.fillStyle = "blue";
ctx.beginPath();
for(var i=0;i<this.testData.inputs.length;i++){
fillText(ctx, this.testData.inputs[i].values[this.iteration-1] , 5 + xx , 30+i*20 + yy+4, 10);
}
ctx.fill();
}
//
//
// ctx.font = "30px Georgia";
// ctx.textAlign = "center";
// ctx.fillStyle = ["blue", "red"][+(this.inp1.value == undefined)];
// if (this.inp1.value !== undefined)
// fillText(ctx, this.inp1.value.toString(16), xx - 23 + xRotate, yy + 8 + yRotate, 25);
// else
// fillText(ctx, "x", xx - 23 + xRotate, yy + 8 + yRotate, 25);
// ctx.fill();
ctx.beginPath();
ctx.strokeStyle = ("rgba(0,0,0,1)");
ctx.lineWidth = correctWidth(3);
var xx = this.x;
var yy = this.y;
// rect(ctx, xx - 20, yy - 20, 40, 40);
moveTo(ctx, 0, 15, xx, yy, this.direction);
lineTo(ctx, 5, 20, xx, yy, this.direction);
lineTo(ctx, 0, 25, xx, yy, this.direction);
ctx.stroke();
}
function TB_Output(x, y, scope = globalScope, dir = "RIGHT", identifier) {
CircuitElement.call(this, x, y, scope, dir, 1);
// this.setDimensions(60,20);
// this.xSize=10;
// this.plotValues = [];
// this.inp1 = new Node(0, 0, 0, this);
// this.inp1 = new Node(100, 100, 0, this);
this.setIdentifier (identifier|| "Test1");
this.inputs = []
this.testBenchInput = undefined;
this.setup();
}
TB_Output.prototype = Object.create(CircuitElement.prototype);
TB_Output.prototype.constructor = TB_Output;
TB_Output.prototype.tooltipText = "Test Bench Output Selected";
TB_Output.prototype.helplink = "https://docs.circuitverse.org/#/testbench";
TB_Output.prototype.centerElement = true;
// TB_Output.prototype.dblclick=function(){
// this.testData=JSON.parse(prompt("Enter TestBench Json"));
// this.setup();
// }
TB_Output.prototype.setDimensions=function() {
this.leftDimensionX = 0;
this.rightDimensionX = 160;
this.upDimensionY = 0;
this.downDimensionY = 40;
if(this.testBenchInput)
this.downDimensionY = 40 + this.testBenchInput.testData.outputs.length * 20;
}
TB_Output.prototype.setup = function(){
// this.iteration = 0;
// this.running = false;
// this.nodeList.clean(this.clockInp);
this.deleteNodes();
this.nodeList = []
this.inputs = [];
this.testBenchInput = undefined;
for(var i=0;i<this.scope.TB_Input.length;i++){
if(this.scope.TB_Input[i].identifier == this.identifier) {
this.testBenchInput = this.scope.TB_Input[i];
break;
}
}
this.setDimensions();
if(this.testBenchInput){
for(var i = 0; i < this.testBenchInput.testData.outputs.length; i++ ){
this.inputs.push(new Node(0, 30+i*20, NODE_INPUT, this, bitWidth = this.testBenchInput.testData.outputs[i].bitWidth,label=this.testBenchInput.testData.outputs[i].label));
}
}
}
// TB_Output.prototype.toggleState=function(){
// this.running=!this.running;
// this.prevClockState=0;
// }
// TB_Output.prototype.resetIterations=function(){
// this.iteration=0;
// this.prevClockState=0;
// }
// TB_Output.prototype.resolve=function(){
// if(this.clockInp.value != this.prevClockState){
// this.prevClockState = this.clockInp.value;
// if(this.clockInp.value == 1 && this.running){
// if(this.iteration<this.testData.n){
// this.iteration++;
// }
// else{
// this.running = false;
// }
// }
// }
// if(this.running && this.iteration)
// for(var i=0;i<this.testData.inputs.length;i++){
// console.log(this.testData.inputs[i].values[this.iteration-1]);
// this.outputs[i].value = parseInt(this.testData.inputs[i].values[this.iteration-1],2);
// simulationArea.simulationQueue.add(this.outputs[i]);
// }
//
// }
// TB_Output.prototype.setPlotValue = function() {
// var time = plotArea.stopWatch.ElapsedMilliseconds;
// if (this.plotValues.length && this.plotValues[this.plotValues.length - 1][0] == time)
// this.plotValues.pop();
//
// if (this.plotValues.length == 0) {
// this.plotValues.push([time, this.inp1.value]);
// return;
// }
//
// if (this.plotValues[this.plotValues.length - 1][1] == this.inp1.value)
// return;
// else
// this.plotValues.push([time, this.inp1.value]);
// }
TB_Output.prototype.customSave = function() {
var data = {
constructorParamaters: [this.direction, this.identifier],
nodes: {
inputs: this.inputs.map(findNode),
},
}
return data;
}
TB_Output.prototype.setIdentifier = function(id = "") {
if (id.length == 0 || id==this.identifier) return;
this.identifier = id;
this.setup();
}
TB_Output.prototype.checkPairing = function(id = "") {
if(this.testBenchInput){
if(this.testBenchInput.deleted || this.testBenchInput.identifier!=this.identifier){
this.setup();
}
}
else {
this.setup();
}
}
TB_Output.prototype.mutableProperties = {
"identifier": {
name: "TestBench Name:",
type: "text",
maxlength: "10",
func: "setIdentifier",
},
}
TB_Output.prototype.customDraw = function() {
ctx = simulationArea.context;
ctx.beginPath();
ctx.strokeStyle = "grey";
ctx.fillStyle = "#fcfcfc";
ctx.lineWidth = correctWidth(1);
var xx = this.x;
var yy = this.y;
var xRotate=0;
var yRotate=0;
if(this.direction=="LEFT") {
xRotate=0;
yRotate=0;
}else if(this.direction=="RIGHT") {
xRotate=120-this.xSize;
yRotate=0;
}else if(this.direction=="UP") {
xRotate=60-this.xSize/2;
yRotate=-20;
}else{
xRotate=60-this.xSize/2;
yRotate=20;
}
// rect2(ctx, -120+xRotate+this.xSize, -20+yRotate, 120-this.xSize, 40, xx, yy, "RIGHT");
// if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this))
// ctx.fillStyle = "rgba(255, 255, 32,0.8)";
// ctx.fill();
// ctx.stroke();
//
// ctx.font = "14px Georgia";
// this.xOff = ctx.measureText(this.identifier).width;
// ctx.beginPath();
// rect2(ctx, -105+xRotate+this.xSize, -11+yRotate,this.xOff + 10, 23, xx, yy, "RIGHT");
// ctx.fillStyle = "#eee"
// ctx.strokeStyle = "#ccc";
// ctx.fill();
// ctx.stroke();
//
ctx.beginPath();
ctx.textAlign = "center";
ctx.fillStyle = "black";
fillText(ctx, this.identifier + " [OUTPUT]", xx + this.rightDimensionX/ 2 , yy + 14 , 10);
// fillText(ctx, ["Not Running","Running"][+this.running], xx + this.rightDimensionX/ 2 , yy + 14 + 10 + 20*this.testData.inputs.length, 10);
// fillText(ctx, "Case: "+(this.iteration), xx + this.rightDimensionX/ 2 , yy + 14 + 20 + 20*this.testData.inputs.length, 10);
fillText(ctx, ["Unpaired","Paired"][ +(this.testBenchInput!=undefined)], xx + this.rightDimensionX/ 2 , yy + this.downDimensionY - 5, 10);
ctx.fill();
if(this.testBenchInput){
ctx.beginPath();
ctx.font = "30px Georgia";
ctx.textAlign = "left";
ctx.fillStyle = "blue";
for(var i=0;i<this.testBenchInput.testData.outputs.length;i++){
// ctx.beginPath();
fillText(ctx, this.testBenchInput.testData.outputs[i].label , 5 + xx , 30+i*20 + yy+4, 10);
}
ctx.fill();
if(this.testBenchInput.running && this.testBenchInput.iteration){
ctx.beginPath();
ctx.font = "30px Georgia";
ctx.textAlign = "right";
ctx.fillStyle = "blue";
ctx.beginPath();
for(var i=0;i<this.testBenchInput.testData.outputs.length;i++){
fillText(ctx, this.testBenchInput.testData.outputs[i].values[this.testBenchInput.iteration-1] , xx +this.rightDimensionX-5 , 30+i*20 + yy+4, 10);
}
ctx.fill();
}
if(this.testBenchInput.running && this.testBenchInput.iteration){
ctx.beginPath();
ctx.font = "30px Georgia";
ctx.textAlign = "center";
ctx.fillStyle = "blue";
for(var i=0;i<this.testBenchInput.testData.outputs.length;i++){
if(this.inputs[i].value!=undefined){
ctx.beginPath();
if(this.testBenchInput.testData.outputs[i].values[this.testBenchInput.iteration-1]=="x"||parseInt(this.testBenchInput.testData.outputs[i].values[this.testBenchInput.iteration-1],2)==this.inputs[i].value)
ctx.fillStyle="green"
else
ctx.fillStyle="red"
fillText(ctx, dec2bin(this.inputs[i].value,this.inputs[i].bitWidth) , xx +this.rightDimensionX/2 , 30+i*20 + yy+4, 10);
ctx.fill();
}
else{
ctx.beginPath();
if(this.testBenchInput.testData.outputs[i].values[this.testBenchInput.iteration-1]=="x")
ctx.fillStyle="green"
else
ctx.fillStyle="red"
fillText(ctx, "X" , xx +this.rightDimensionX/2 , 30+i*20 + yy+4, 10);
ctx.fill();
}
}
}
}
//
//
// ctx.font = "30px Georgia";
// ctx.textAlign = "center";
// ctx.fillStyle = ["blue", "red"][+(this.inp1.value == undefined)];
// if (this.inp1.value !== undefined)
// fillText(ctx, this.inp1.value.toString(16), xx - 23 + xRotate, yy + 8 + yRotate, 25);
// else
// fillText(ctx, "x", xx - 23 + xRotate, yy + 8 + yRotate, 25);
// ctx.fill();
}

View file

@ -0,0 +1,11 @@
function togglePassword() {
if ($('.users-password-input').attr('type') === 'text') {
$('.users-password-input').attr('type', 'password');
$('#toggle-icon').addClass('fa-eye-slash');
$('#toggle-icon').removeClass('fa-eye');
} else {
$('.users-password-input').attr('type', 'text');
$('#toggle-icon').addClass('fa-eye');
$('#toggle-icon').removeClass('fa-eye-slash');
}
}

4
public/js/utils.js Normal file
View file

@ -0,0 +1,4 @@
// To strip tags from input
function stripTags(string="") {
return string.replace(/(<([^>]+)>)/ig, '').trim();
}

506
public/js/verilog.js Normal file
View file

@ -0,0 +1,506 @@
/*
# Primary Developers
1) James H-J Yeh, Ph.D.
2) Satvik Ramaprasad
refer verilog_documentation.md
*/
function generateVerilog() {
var data = verilog.exportVerilog();
console.log(data);
download(projectName + ".v", data);
}
verilog = {
// Entry point to verilog generation
// scope = undefined means export all circuits
exportVerilog:function(scope = undefined){
var dependencyList = {};
// Reset Verilog Element State
for (var i = 0; i < circuitElementList.length; i++) {
if (window[circuitElementList[i]].resetVerilog) {
window[circuitElementList[i]].resetVerilog();
}
}
// List of devices under test for which testbench needs to be created
var DUTs = [];
var SubCircuitIds = new Set();
// Generate SubCircuit Dependency Graph
for (id in scopeList) {
dependencyList[id] = scopeList[id].getDependencies();
for (var i = 0; i < scopeList[id].SubCircuit.length; i++) {
SubCircuitIds.add(scopeList[id].SubCircuit[i].id);
}
}
for(id in scopeList) {
if(!SubCircuitIds.has(id))
DUTs.push(scopeList[id]);
}
// DFS on SubCircuit Dependency Graph
var visited = {};
var elementTypesUsed = {};
var output = "";
if(scope) {
// generate verilog only for scope
output += this.exportVerilogScope(scope.id,visited,dependencyList, elementTypesUsed);
}
else {
// generate verilog for everything
for (id in scopeList) {
output += this.exportVerilogScope(id,visited,dependencyList, elementTypesUsed);
}
}
// Add Circuit Element - Module Specific Verilog Code
for(var element in elementTypesUsed) {
// If element has custom verilog
if (window[element].moduleVerilog) {
output += window[element].moduleVerilog();
}
}
var report = this.generateReport(elementTypesUsed) + "\n";
var testbench = this.generateTestBenchCode(DUTs);
return report + testbench + output;
},
generateReport: function(elementTypesUsed) {
var output = "";
output += "/**\n"
output += " * This is an autogenerated netlist code from CircuitVerse. Verilog Code can be\n"
output += " * tested on https://www.edaplayground.com/ using Icarus Verilog 0.9.7. This is an\n"
output += " * experimental module and some manual changes make need to be done in order for\n"
output += " * this to work.\n"
output += " *\n"
output += " * If you have any ideas/suggestions or bug fixes, raise an issue\n"
output += " * on https://github.com/CircuitVerse/CircuitVerse/issues/new/choose\n"
output += " */\n"
output += "\n";
output += '/*\n';
output += sp(1) + "Element Usage Report\n";
for(var elem in elementTypesUsed) {
if(elem == "Node")continue;
output += `${sp(2)}${elem} - ${elementTypesUsed[elem]} times\n`;
}
output += '*/\n';
output += "\n";
var instructions = "";
output += '/*\n';
output += sp(1) + "Usage Instructions and Tips\n";
instructions += sp(2) + "Labels - Ensure unique label names and avoid using verilog keywords\n";
instructions += sp(2) + "Warnings - Connect all optional inputs to remove warnings\n";
for(var elem in elementTypesUsed) {
// If element has custom instructions
if (window[elem].verilogInstructions) {
instructions += indent(2, window[elem].verilogInstructions());
}
}
output += instructions
output += '*/\n';
return output;
},
generateTestBenchCode: function(DUTs) {
if(DUTs.length == 0)return "";
var output = "// Sample Testbench Code - Uncomment to use\n";
output += "\n/*\n";
output += "module TestBench();\n";
var registers = {};
var wires = {};
for(var i = 1; i <= 32; i++)
registers[i] = new Set();
for(var i = 1; i <= 32; i++)
wires[i] = new Set();
var clocks = new Set();
var inputs = new Set();
var outputs = new Set();
var deviceInstantiations = "";
for(var i = 0; i < DUTs.length; i++) {
var DUT = DUTs[i];
for(var j = 0;j < DUT.Input.length; j++){
var inp = DUT.Input[j];
registers[inp.bitWidth].add(inp.label);
inputs.add(inp.label);
}
for(var j = 0;j < DUT.Output.length; j++){
var out = DUT.Output[j];
wires[out.bitWidth].add(out.label);
outputs.add(out.label);
}
for(var j=0;j<DUT.Clock.length;j++){
var inp = DUT.Clock[j];
registers[1].add(inp.label);
clocks.add(inp.label);
}
var circuitName = this.sanitizeLabel(DUT.name);
var dutHeader = this.generateHeaderHelper(DUT);
deviceInstantiations += `${sp(1)}${circuitName} DUT${i}${dutHeader}\n`;
}
output += "\n"
// Generate Reg Initialization Code
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
if(registers[bitWidth].size == 0)
continue;
var regArray = [...registers[bitWidth]];
if(bitWidth == 1)
output += `${sp(1)}reg ${regArray.join(", ")};\n`;
else
output += `${sp(1)}reg [${bitWidth-1}:0] ${regArray.join(", ")};\n`;
}
output += "\n"
// Generate Wire Initialization Code
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
if(wires[bitWidth].size == 0)
continue;
var wireArray = [...wires[bitWidth]];
if(bitWidth == 1)
output += `${sp(1)}wire ${wireArray.join(", ")};\n`;
else
output += `${sp(1)}wire [${bitWidth-1}:0] ${wireArray.join(", ")};\n`;
}
output += "\n"
output += deviceInstantiations;
if(clocks.size) {
output += `${sp(1)}always begin\n`;
output += `${sp(2)}#10\n`;
for(var clk of clocks)
output += `${sp(2)}${clk} = 0;\n`;
output += `${sp(2)}#10\n`;
for(var clk of clocks)
output += `${sp(2)}${clk} = 1;\n`;
output += `${sp(1)}end\n`;
output += '\n';
}
output += `${sp(1)}initial begin\n`;
// Reset inputs to 0
for(var inp of inputs){
output += `${sp(2)}${inp} = 0;\n`;
}
output += '\n';
output += `${sp(2)}#15\n`;
for(var out of outputs) {
output += `${sp(2)}$display("${out} = %b", ${out});\n`;
}
output += '\n';
output += `${sp(2)}#10\n`;
for(var out of outputs) {
output += `${sp(2)}$display("${out} = %b", ${out});\n`;
}
output += '\n';
output += `${sp(2)}$finish;\n\n`;
output += `${sp(1)}end\n`;
output += "endmodule\n";
output += "\n*/\n";
return output;
},
// Recursive DFS function
exportVerilogScope: function(id,visited,dependencyList, elementTypesUsed){
// Already Visited
if (visited[id]) return "";
// Mark as Visited
visited[id] = true;
var output = "";
// DFS on dependencies
for (var i = 0; i < dependencyList[id].length; i++)
output += this.exportVerilogScope(dependencyList[id][i],visited,dependencyList, elementTypesUsed)+"\n";
var scope = scopeList[id];
// Initialize labels for all elements
this.resetLabels(scope);
this.setLabels(scope);
output += this.generateHeader(scope);
output += this.generateOutputList(scope); // generate output first to be consistent
output += this.generateInputList(scope);
// Note: processGraph function populates scope.verilogWireList
var res = this.processGraph(scope, elementTypesUsed);
// Generate Wire Initialization Code
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
var wireList = scope.verilogWireList[bitWidth];
// Hack for splitter
wireList = wireList.filter(x => !x.includes("["))
if(wireList.length == 0)
continue;
if(bitWidth == 1)
output += " wire " + wireList.join(", ") + ";\n";
else
output += " wire [" +(bitWidth-1)+":0] " + wireList.join(", ") + ";\n";
}
// Append Wire connections and module instantiations
output += res;
// Append footer
output += "endmodule\n";
return output;
},
// Performs DFS on the graph and generates netlist of wires and connections
processGraph: function(scope, elementTypesUsed){
// Initializations
var res = "";
scope.stack = [];
scope.verilogWireList = [];
for(var i = 0; i <= 32; i++)
scope.verilogWireList.push(new Array());
var verilogResolvedSet = new Set();
// Start DFS from inputs
for (var i = 0; i < inputList.length; i++) {
for (var j = 0; j < scope[inputList[i]].length; j++) {
scope.stack.push(scope[inputList[i]][j]);
}
}
// Iterative DFS on circuit graph
while (scope.stack.length) {
if (errorDetected) return;
var elem = scope.stack.pop();
if(verilogResolvedSet.has(elem))
continue;
// Process verilog creates variable names and adds elements to DFS stack
elem.processVerilog();
// Record usage of element type
if(elem.objectType != "Node") {
if(elementTypesUsed[elem.objectType])elementTypesUsed[elem.objectType]++;
else elementTypesUsed[elem.objectType] = 1;
}
if(elem.objectType!="Node" && elem.objectType!="Input" && elem.objectType!="Clock") {
verilogResolvedSet.add(elem);
}
}
// Generate connection verilog code and module instantiations
for(var elem of verilogResolvedSet) {
res += " " + elem.generateVerilog() + "\n";
}
return res;
},
resetLabels: function(scope){
for(var i=0;i<scope.allNodes.length;i++){
scope.allNodes[i].verilogLabel="";
}
},
// Sets labels for all Circuit Elements elements
setLabels: function(scope=globalScope){
/**
* Sets a name for each element. If element is already labeled,
* the element is used directly, otherwise an automated label is provided
* sanitizeLabel is a helper function to escape white spaces
*/
for(var i=0;i<scope.Input.length;i++){
if(scope.Input[i].label=="")
scope.Input[i].label="inp_"+i;
else
scope.Input[i].label=this.sanitizeLabel(scope.Input[i].label)
// copy label to node
scope.Input[i].output1.verilogLabel = scope.Input[i].label;
}
for(var i=0;i<scope.ConstantVal.length;i++){
if(scope.ConstantVal[i].label=="")
scope.ConstantVal[i].label="const_"+i;
else
scope.ConstantVal[i].label=this.sanitizeLabel(scope.ConstantVal[i].label)
// copy label to node
scope.ConstantVal[i].output1.verilogLabel=scope.ConstantVal[i].label;
}
// copy label to clock
for(var i = 0; i < scope.Clock.length; i++) {
if (scope.Clock[i].label == "")
scope.Clock[i].label = "clk_"+i;
else
scope.Clock[i].label=this.sanitizeLabel(scope.Clock[i].label);
scope.Clock[i].output1.verilogLabel = scope.Clock[i].label;
}
for(var i=0;i<scope.Output.length;i++){
if(scope.Output[i].label=="")
scope.Output[i].label="out_"+i;
else
scope.Output[i].label=this.sanitizeLabel(scope.Output[i].label)
}
for(var i=0;i<scope.SubCircuit.length;i++){
if(scope.SubCircuit[i].label=="")
scope.SubCircuit[i].label=scope.SubCircuit[i].data.name+"_"+i;
else
scope.SubCircuit[i].label=this.sanitizeLabel(scope.SubCircuit[i].label)
}
for(var i=0;i<moduleList.length;i++){
var m = moduleList[i];
for(var j=0;j<scope[m].length;j++){
scope[m][j].verilogLabel = this.sanitizeLabel(scope[m][j].label) || (scope[m][j].verilogName()+"_"+j);
}
}
},
generateHeader:function(scope=globalScope){
// Example: module HalfAdder (a,b,s,c);
var res="\nmodule " + this.sanitizeLabel(scope.name);
res += this.generateHeaderHelper(scope);
return res;
},
generateHeaderHelper:function(scope=globalScope) {
// Example: (a,b,s,c);
var res="(";
var pins = [];
for(var i=0;i<scope.Output.length;i++){
pins.push(scope.Output[i].label);
}
for(var i=0;i<scope.Clock.length;i++){
pins.push(scope.Clock[i].label);
}
for(var i=0;i<scope.Input.length;i++){
pins.push(scope.Input[i].label);
}
res += pins.join(", ");
res += ");\n";
return res;
},
generateInputList:function(scope=globalScope){
var inputs={}
for(var i = 1; i <= 32; i++)
inputs[i] = [];
for(var i=0;i<scope.Input.length;i++){
inputs[scope.Input[i].bitWidth].push(scope.Input[i].label);
}
for(var i=0;i<scope.Clock.length;i++){
inputs[scope.Clock[i].bitWidth].push(scope.Clock[i].label);
}
var res="";
for (bitWidth in inputs){
if(inputs[bitWidth].length == 0) continue;
if(bitWidth==1)
res+=" input "+ inputs[1].join(", ") + ";\n";
else
res+=" input ["+(bitWidth-1)+":0] "+ inputs[bitWidth].join(", ") + ";\n";
}
return res;
},
generateOutputList:function(scope=globalScope){
// Example 1: output s,cout;
var outputs={}
for(var i=0;i<scope.Output.length;i++){
if(outputs[scope.Output[i].bitWidth])
outputs[scope.Output[i].bitWidth].push(scope.Output[i].label);
else
outputs[scope.Output[i].bitWidth] = [scope.Output[i].label];
}
var res="";
for (bitWidth in outputs){
if(bitWidth==1)
res+=" output "+ outputs[1].join(", ") + ";\n";
else
res+=" output ["+(bitWidth-1)+":0] "+ outputs[bitWidth].join(", ") + ";\n";
}
return res;
},
sanitizeLabel: function(name){
// return name.replace(/ Inverse/g, "_inv").replace(/ /g , "_");
var temp = name;
//if there is a space anywhere but the last place
//replace spaces by "_"
//last space is required for escaped id
if (temp.search(/ /g) < temp.length-1 && temp.search(/ /g) >= 0) {
temp = temp.replace(/ Inverse/g, "_inv");
temp = temp.replace(/ /g , "_");
}
//if first character is not \ already
if (temp.substring(0,1).search(/\\/g) < 0) {
//if there are non-alphanum_ character, or first character is num, add \
if (temp.search(/[\W]/g) > -1 || temp.substring(0,1).search(/[0-9]/g) > -1)
temp = "\\" + temp + " ";
}
return temp;
},
/*
sanitizeLabel: function(name){
// Replace spaces by "_"
name = name.replace(/ /g , "_");
// Replace Hyphens by "_"
name = name.replace(/-/g , "_");
// Replace Colons by "_"
name = name.replace(/:/g , "_");
// replace ~ with inv_
name = name.replace(/~/g , "inv_");
// Shorten Inverse to inv
name = name.replace(/Inverse/g , "inv");
// If first character is a number
if(name.substring(0, 1).search(/[0-9]/g) > -1) {
name = "w_" + name;
}
// if first character is not \ already
if (name[0] != '\\') {
//if there are non-alphanum_ character, add \
if (name.search(/[\W]/g) > -1)
name = "\\" + name;
}
return name;
},
*/
generateNodeName: function(node, currentCount, totalCount) {
if(node.verilogLabel) return node.verilogLabel;
var parentVerilogLabel = node.parent.verilogLabel;
var nodeName;
if(node.label) {
nodeName = verilog.sanitizeLabel(node.label);
}
else {
nodeName = (totalCount > 1) ? "out_" + currentCount: "out";
}
if (parentVerilogLabel.substring(0,1).search(/\\/g) < 0)
return (parentVerilogLabel) + "_" + nodeName;
else
return (parentVerilogLabel.substring(0,parentVerilogLabel.length-1)) + "_" + nodeName + " ";
}
}
/*
Helper function to generate spaces for indentation
*/
function sp(indentation) {
return " ".repeat(indentation * 2);
}
/*
Helper function to indent paragraph
*/
function indent(indentation, string) {
var result = string.split('\n');
if(result[result.length - 1] == '') {
result.pop();
result = result.map(x => sp(indentation) + x).join("\n");
result += '\n';
return result;
}
return result.map(x => sp(indentation) + x).join("\n");
}

View file

@ -0,0 +1,68 @@
## Circuit2Verilog Module
**Primary Contributors:**
1. James H - J Yeh, Ph.D.
2. Satvik Ramaprasad
## Introduction
This is an experimental module which generates verilog netlist (structural
verilog) given the circuit. Currently, the module generates fully functional
verilog code for basic circuits. For complex circuit, additional (manual) work
may need to be done in order to make it work. We are continuously improving this
module to work with more and more complex circuits.
# Algorithm
The basic algorithm is fairly straight forward. We have the circuit graph in
memory. We just need to convert this graph into verilog netlist. It is done by
performing a DFS on the circuit graph. The DFS involves the following steps
1. Creating verilog wires as and when required
2. Connecting verilog wires in element instantiations
## Some background information
The different sub circuits form a DAG (Directed Acyclic Graph) or dependency
graph. Each sub circuit itself (called scope internally) is actually a (cyclic)
graph on its own. Therefore the verilog generation is done in a 2 step DFS
approach. The first DFS is performed on the dependency graph. The second DFS is
done on individual sub circuit (scope).
## Code/Algorithm workflow
1. `exportVerilog()` - entry point
2. `exportVerilogScope()` - DFS(1) on Sub Circuits Dependency Graph
1. Set Verilog Labels for all elements
2. `generateHeader()` - Generates Module Header
3. `generateOutputList()` - Output Output List
4. `generateInputList()` - Generates Input List
5. `processGraph()` - DFS(2) on individual subcircuit/scope
1. DFS starts from inputs
2. Calls `processVerilog()` on all circuit elements (graph nodes) - resolves label names and adds neighbors to DFS stack.
3. Calls `generateVerilog()` on all circuit elements to get final verilog.
6. Generate Wire initializations
## Functions
**Verilog Module Functions:**
1. `verilog.exportVerilog()` - Entry point
1. `verilog.exportVerilogScope()` - Recursive DFS function on subcircuit graph
1. `verilog.processGraph()` - Iterative DFS function on subcircuit scope
1. `verilog.resetLabels()` - Resets labels in scope
1. `verilog.setLabels()` - Sets labels in scope
1. `verilog.generateHeader()` - Generates Verilog Module Header
1. `verilog.generateInputList()` - Generates Verilog Module Input List
1. `verilog.generateOutputList()` - Generates Verilog Module Output List
1. `verilog.sanitizeLabel()` - Sanitizes label for node/wire
1. `verilog.generateNodeName()` - Helper function to resolve node/wire name
**CircuitElement Functions:**
These functions can be overriden by derived classes.
1. `CircuitElement.prototype.processVerilog()` - Graph algorithm to resolve verilog wire labels
1. `CircuitElement.prototype.verilogName()` - Generate verilog name
1. `CircuitElement.prototype.generateVerilog()` - Generate final verilog code
1. `CircuitElement.prototype.verilogType` - Verilog type name
1. `CircuitElement.moduleVerilog` - Custom module verilog for elements

176
public/js/wire.js Normal file
View file

@ -0,0 +1,176 @@
//wire object
function Wire(node1, node2, scope) {
this.objectType = "Wire";
this.node1 = node1;
this.scope = scope;
this.node2 = node2;
this.type = "horizontal";
this.updateData();
this.scope.wires.push(this);
forceResetNodes=true;
}
//if data changes
// get the terminals of the wires
Wire.prototype.updateData = function() {
this.x1 = this.node1.absX();
this.y1 = this.node1.absY();
this.x2 = this.node2.absX();
this.y2 = this.node2.absY();
if (this.x1 == this.x2) this.type = "vertical";
}
Wire.prototype.updateScope=function(scope){
this.scope=scope;
this.checkConnections();
}
//to check if nodes are disconnected
Wire.prototype.checkConnections = function() {
var check = this.node1.deleted||this.node2.deleted||!this.node1.connections.contains(this.node2) || !this.node2.connections.contains(this.node1);
if (check) this.delete();
return check;
}
Wire.prototype.update = function() {
if(embed)return;
if(this.node1.absX()==this.node2.absX()){
this.x1=this.x2=this.node1.absX();
this.type="vertical";
}
else if(this.node1.absY()==this.node2.absY()){
this.y1=this.y2=this.node1.absY();
this.type="horizontal";
}
var updated = false;
if (wireToBeChecked && this.checkConnections()) {
this.delete();
return;
} // SLOW , REMOVE
// select a wire
if (simulationArea.shiftDown==false&&simulationArea.mouseDown == true && simulationArea.selected == false && this.checkWithin(simulationArea.mouseDownX, simulationArea.mouseDownY)) {
simulationArea.selected = true;
simulationArea.lastSelected = this;
updated = true;
}
// double click on a wire to place a node
else if(simulationArea.mouseDown && simulationArea.lastSelected==this&& !this.checkWithin(simulationArea.mouseX, simulationArea.mouseY)){
// lets move this wiree !
if(this.node1.type == NODE_INTERMEDIATE && this.node2.type == NODE_INTERMEDIATE) {
if(this.type=="horizontal"){
this.node1.y= simulationArea.mouseY
this.node2.y= simulationArea.mouseY
} else if(this.type=="vertical"){
this.node1.x= simulationArea.mouseX
this.node2.x= simulationArea.mouseX
}
}
// var n = new Node(simulationArea.mouseDownX, simulationArea.mouseDownY, 2, this.scope.root);
// n.clicked = true;
// n.wasClicked = true;
// simulationArea.lastSelected=n;
// this.converge(n);
}
if (simulationArea.lastSelected == this) {
}
if (this.node1.deleted || this.node2.deleted){
this.delete();
return;
} //if either of the nodes are deleted
//NODE POSITION when we move a COMPONENT to make lines drawen horzontally or vertically
if (simulationArea.mouseDown == false) {
if (this.type == "horizontal") {
if (this.node1.absY() != this.y1) {
// if(this.checkConnections()){this.delete();return;}
var n = new Node(this.node1.absX(), this.y1, 2, this.scope.root);
this.converge(n);
updated = true;
} else if (this.node2.absY() != this.y2) {
// if(this.checkConnections()){this.delete();return;}
var n = new Node(this.node2.absX(), this.y2, 2, this.scope.root);
this.converge(n);
updated = true;
}
} else if (this.type == "vertical") {
if (this.node1.absX() != this.x1) {
// if(this.checkConnections()){this.delete();return;}
var n = new Node(this.x1, this.node1.absY(), 2, this.scope.root);
this.converge(n);
updated = true;
} else if (this.node2.absX() != this.x2) {
// if(this.checkConnections()){this.delete();return;}
var n = new Node(this.x2, this.node2.absY(), 2, this.scope.root);
this.converge(n);
updated = true;
}
}
}
return updated;
}
Wire.prototype.draw = function() {
// for calculating min-max Width,min-max Height
ctx = simulationArea.context;
var color;
if (simulationArea.lastSelected == this)
color = "blue";
else if (this.node1.value == undefined||this.node2.value == undefined)
color = "red";
else if (this.node1.bitWidth == 1)
color = ["red", "DarkGreen", "Lime"][this.node1.value + 1];
else
color = "black";
drawLine(ctx, this.node1.absX(), this.node1.absY(), this.node2.absX(), this.node2.absY(), color, 3);
}
// checks if node lies on wire
Wire.prototype.checkConvergence = function(n) {
return this.checkWithin(n.absX(), n.absY());
}
// fn checks if coordinate lies on wire
Wire.prototype.checkWithin = function(x, y) {
if ((this.type == "horizontal") && (this.node1.absX() < this.node2.absX()) && (x > this.node1.absX()) && (x < this.node2.absX()) && (y === this.node2.absY()))
return true;
else if ((this.type == "horizontal") && (this.node1.absX() > this.node2.absX()) && (x < this.node1.absX()) && (x > this.node2.absX()) && (y === this.node2.absY()))
return true;
else if ((this.type == 'vertical') && (this.node1.absY() < this.node2.absY()) && (y > this.node1.absY()) && (y < this.node2.absY()) && (x === this.node2.absX()))
return true;
else if ((this.type == 'vertical') && (this.node1.absY() > this.node2.absY()) && (y < this.node1.absY()) && (y > this.node2.absY()) && (x === this.node2.absX()))
return true
return false;
}
//add intermediate node between these 2 nodes
Wire.prototype.converge = function(n) {
this.node1.connect(n);
this.node2.connect(n);
this.delete();
}
Wire.prototype.delete = function() {
forceResetNodes=true;
updateSimulation = true;
this.node1.connections.clean(this.node2);
this.node2.connections.clean(this.node1);
this.scope.wires.clean(this);
this.node1.checkDeleted();
this.node2.checkDeleted();
}