把前端从CircuitVerse中拆了出来
This commit is contained in:
commit
5bf1284599
2182 changed files with 189323 additions and 0 deletions
132
public/js/Counter.js
Normal file
132
public/js/Counter.js
Normal 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
854
public/js/Node.js
Normal 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
298
public/js/Plot.js
Normal 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
218
public/js/Quin_McCluskey.js
Normal 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
299
public/js/RGBLedMatrix.js
Normal 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
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
328
public/js/UX.js
Normal 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 ⓘ</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, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
$('#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
1214
public/js/canvas2svg.js
Normal file
File diff suppressed because it is too large
Load diff
418
public/js/canvasApi.js
Normal file
418
public/js/canvasApi.js
Normal 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",
|
||||
}
|
276
public/js/combinationalAnalysis.js
Normal file
276
public/js/combinationalAnalysis.js
Normal 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
1023
public/js/data.js
Normal file
File diff suppressed because it is too large
Load diff
77
public/js/eeprom.js
Normal file
77
public/js/eeprom.js
Normal 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
84
public/js/embed.js
Normal 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
246
public/js/embedListeners.js
Normal 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
361
public/js/engine.js
Normal 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
432
public/js/engine_old.js
Normal 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
83
public/js/eventQueue.js
Normal 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
80
public/js/grading.js
Normal 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
416
public/js/layout.js
Normal 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
502
public/js/listeners.js
Normal 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
1536
public/js/logix.js
Normal file
File diff suppressed because it is too large
Load diff
88
public/js/metadata.json
Normal file
88
public/js/metadata.json
Normal 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
107
public/js/miniMap.js
Normal 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
4461
public/js/modules.js
Normal file
File diff suppressed because it is too large
Load diff
2
public/js/pace.js
Normal file
2
public/js/pace.js
Normal file
File diff suppressed because one or more lines are too long
333
public/js/plugins/bootstrap-input-spinner.js
vendored
Normal file
333
public/js/plugins/bootstrap-input-spinner.js
vendored
Normal 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
168
public/js/plugins/checkBo.min.js
vendored
Normal 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);
|
251
public/js/plugins/jquery.twbs-toggle-buttons.min.js
vendored
Normal file
251
public/js/plugins/jquery.twbs-toggle-buttons.min.js
vendored
Normal 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
202
public/js/ram.js
Normal 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
30
public/js/search.js
Normal 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
1072
public/js/sequential.js
Normal file
File diff suppressed because it is too large
Load diff
411
public/js/subcircuit.js
Normal file
411
public/js/subcircuit.js
Normal 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
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
547
public/js/testBench.js
Normal 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();
|
||||
|
||||
}
|
11
public/js/togglePassword.js
Normal file
11
public/js/togglePassword.js
Normal 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
4
public/js/utils.js
Normal 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
506
public/js/verilog.js
Normal 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");
|
||||
}
|
68
public/js/verilog_documentation.md
Normal file
68
public/js/verilog_documentation.md
Normal 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
176
public/js/wire.js
Normal 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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue