0.3.4: Flip Flops

This commit is contained in:
MatCat 2021-03-03 18:30:15 -08:00
parent 3ecaaf4f9c
commit cb852d5d28
4 changed files with 280 additions and 47 deletions

View File

@ -12,6 +12,10 @@ To be decided, but at this moment this code is open source and free to use for n
## Changelog
### 0.3.4
* Added Flip-Flops! There are 4 to choose from, JK, SR, D, and T!
### 0.3.3
* Fixed a bug that crashed the engine when attempting to place an IC with no inputs

View File

@ -33,26 +33,22 @@ class CanvasTools {
}
drawTextCentered(ctx,x,y,x2,y2,text,fontStyle="24px Console",fontColor = "#555") {
let old_fillStyle = ctx.fillStyle;
let old_font = ctx.font;
ctx.save();
ctx.font = fontStyle;
ctx.fillStyle = fontColor;
let tHeight = ctx.measureText(text).actualBoundingBoxAscent + ctx.measureText(text).actualBoundingBoxDescent;
let tX = x+((x2/2)-(ctx.measureText(text).width/2));
let tY = y+tHeight+((y2/2)-(tHeight/2));
ctx.fillText(text,tX,tY);
ctx.fillStyle = old_fillStyle;
ctx.font = old_font;
ctx.restore();
}
drawText(ctx,x,y,text,fontStyle="24px Console",fontColor = "#555") {
let old_fillStyle = ctx.fillStyle;
let old_font = ctx.font;
ctx.save();
ctx.font = fontStyle;
ctx.fillStyle = fontColor;
ctx.fillText(text,x,y);
ctx.fillStyle = old_fillStyle;
ctx.font = old_font;
ctx.restore();
}
}
@ -138,21 +134,20 @@ class elementContainer {
DrawAll(ctx,settings) {
let ICOuts = 0;
for (let a = 0; a < this.Elements.length; a++) {
ctx.save();
if (this.Elements[a] instanceof ICOutput) ICOuts++;
if (this.Elements[a] == this.Selected) this.Elements[a].drawBorderBox(ctx, this.Elements[a].X - 2, this.Elements[a].Y - 2, this.Elements[a].Width + 4, this.Elements[a].Height + 4, 1, "rgba(100,200,255,0.25)", "rgba(100,200,255,0.25)");
this.Elements[a].drawElement(this.Elements[a].X, this.Elements[a].Y, ctx);
let old_font = ctx.font;
let old_fillStyle = ctx.fillStyle;
ctx.font = "10px Console";
let x = this.Elements[a].X;
let y = this.Elements[a].Y + (this.Elements[a].Height - 12);
let x2 = this.Elements[a].Width;
let y2 = 10;
//this.Elements[a].drawTextCentered(ctx, x, y, x2, y2, this.Elements[a].Designator, ctx.font, "#000");
ctx.font = old_font;
ctx.fillStyle = old_fillStyle;
ctx.restore();
}
this.ICOutputs = ICOuts;
if (!this.Selected) {
let PropertiesBox = document.getElementById("PropertiesBox");

View File

@ -2,10 +2,12 @@ let ElementReferenceTable = new Array();
let ElementCategory_Inputs = new ElementCatalog_Category("Inputs","");
let ElementCategory_LOGIC = new ElementCatalog_Category("Logic" ,"");
let ElementCategory_FlipFlop = new ElementCatalog_Category("Flip-Flops" ,"");
let ElementCategory_Timing = new ElementCatalog_Category("Timing" ,"");
let ElementCategory_ICs = new ElementCatalog_Category("ICs" ,"");
let elementCatalog = new ElementCatalog([ElementCategory_Inputs,
ElementCategory_LOGIC,
ElementCategory_FlipFlop,
ElementCategory_Timing,
ElementCategory_ICs]);
class ElementProperty {
@ -75,6 +77,7 @@ class Element extends CanvasTools {
this.Name = "Element";
this.Designator = "";
this.Inputs = new Array(Inputs);
this.InputLabels = new Array(1);
this.Width = 100;
this.Height = 60;
this.inputCircleRadius = 10;
@ -87,6 +90,7 @@ class Element extends CanvasTools {
this.Properties = new Array();
this.LogicEngine = logicengine;
this.Outputs = new Array(1);
this.OutputLabels = new Array(1);
this.NoOutput = false;
this.OutputLink = 0;
@ -267,28 +271,36 @@ class Element extends CanvasTools {
if ((mouseDist <= (this.inputCircleRadius)) && this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
ctx.fill();
ctx.stroke();
if (this.InputLabels[a]) this.drawText(ctx,x+(this.inputCircleRadius*2)+ 5,(firstY + (a*24)) + 5,this.InputLabels[a],"10px Console","#000");
}
ctx.restore();
}
drawOutputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover="#00ffff") {
let old_strokeStyle = ctx.strokeStyle;
let old_fillStyle = ctx.fillStyle;
let mouseDist = length2D(x+(this.Width-10),y+(this.Height/2),this.MousePosition.x,this.MousePosition.y);
ctx.beginPath();
ctx.arc(x+(this.Width-10),y+(this.Height/2),this.outputCircleRadius,0,2*Math.PI);
ctx.strokeStyle = borderColor;
ctx.fillStyle = circleColorFalse;
if (this.getOutput()) ctx.fillStyle = circleColorTrue;
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
ctx.fill();
ctx.stroke();
ctx.strokeStyle = old_strokeStyle;
ctx.fillStyle = old_fillStyle;
drawOutputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff") {
ctx.save();
let centerY = y + Math.round(this.Height / 2);
let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
let firstY = (centerY - (totalHeight/2)) + 12;
for (let a = 0; a < this.Outputs.length;a++) {
let mouseDist = length2D(x+(this.Width - 10), firstY + (a*24),this.MousePosition.x,this.MousePosition.y);
ctx.beginPath();
ctx.arc(x+(this.Width-10),firstY + (a*24),this.outputCircleRadius,0,2*Math.PI);
ctx.strokeStyle = borderColor;
ctx.fillStyle = circleColorFalse;
if (this.getOutput(a)) ctx.fillStyle = circleColorTrue;
if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
ctx.fill();
ctx.stroke();
let textSize = false;
if (this.OutputLabels[a]) textSize = this.textSize(ctx,this.OutputLabels[a],"10px Console");
if (this.OutputLabels[a]) this.drawText(ctx,(x+(this.Width)) - (textSize.width + 5 + (this.outputCircleRadius*2)),(firstY + (a*24)) + 5,this.OutputLabels[a],"10px Console","#000");
}
ctx.restore();
}
drawConnections(ctx,settings) {
ctx.save();
let centerY = this.Y + Math.round(this.Height / 2);
let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
let firstY = (centerY - (totalHeight/2)) + 12;
@ -315,6 +327,7 @@ class Element extends CanvasTools {
let endMidX = startMidX;
let endMidY = endY;
ctx.save();
ctx.beginPath();
ctx.lineWidth = settings.LinkWidth;
ctx.setLineDash(settings.LinkDash);
@ -325,11 +338,28 @@ class Element extends CanvasTools {
ctx.strokeStyle = settings.ActiveConnectionColor;
if (!this.getOutput(this.OutputConnections[a].Output)) ctx.strokeStyle = settings.InactiveConnectionColor;
ctx.stroke();
ctx.restore();
}
}
ctx.restore();
}
setConnections() {
for (let a = 0; a < this.OutputConnections.length;a++) {
//console.log(this.Designator + " sending " + this.getOutput() + " to " + this.OutputConnections[a].Element.Designator + " I" + this.OutputConnections[a].Input);
this.LogicEngine.RecursionCount++;
//console.log("Recursion: " + this.LogicEngine.RecursionCount);
if (this.LogicEngine.RecursionCount > 1000) {
if (!this.LogicEngine.RecursionError) {
console.log("RECURSION ERROR");
this.LogicEngine.RecursionError = true;
}
return;
}
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput(this.OutputConnections[a].Output));
this.LogicEngine.RecursionCount--;
}
}
setInput(Input,Value) {
let oldOutput = this.getOutput();
if (Value) {
@ -342,23 +372,7 @@ class Element extends CanvasTools {
} else {
return;
}
if (this.getOutput() != oldOutput) {
// The output changed, we need to notify connected elements
for (let a = 0; a < this.OutputConnections.length;a++) {
//console.log(this.Designator + " sending " + this.getOutput() + " to " + this.OutputConnections[a].Element.Designator + " I" + this.OutputConnections[a].Input);
this.LogicEngine.RecursionCount++;
//console.log("Recursion: " + this.LogicEngine.RecursionCount);
if (this.LogicEngine.RecursionCount > 1000) {
if (!this.LogicEngine.RecursionError) {
console.log("RECURSION ERROR");
this.LogicEngine.RecursionError = true;
}
return;
}
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
this.LogicEngine.RecursionCount--;
}
}
this.setConnections();
}
getOutput(Output=0) {
@ -1084,7 +1098,7 @@ class InputSwitch extends inputElement {
MouseClick(mousePos) {
super.MouseClick(mousePos);
if ((mousePos.x >= (this.X + 5)) && (mousePos.x <= (this.X + 55)) && (mousePos.y >= (this.Y + 5)) && (mousePos.y <= (this.Y + 55))) {
this.Output = ~this.Output;
this.Output = !this.Output;
for (let a = 0; a < this.OutputConnections.length; a++) {
this.LogicEngine.RecursionCount = 0;
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
@ -1154,6 +1168,226 @@ let ElementCatalog_BUTTON = new ElementCatalog_Element("Button","The button only
ElementReferenceTable.push(ElementCatalog_BUTTON);
ElementCategory_Inputs.addElement(ElementCatalog_BUTTON);
class FlipFlopJK extends Element {
constructor(RestoreData = null, logicengine) {
super(RestoreData,logicengine,3);
this.Name = "JK-FF";
this.Outputs = new Array(2);
this.InputLabels = new Array("J","CLK","K");
this.OutputLabels = new Array("Q","~Q");
this.removeProperty("Inputs");
this.Height = 80;
if (RestoreData) {
this.Outputs = RestoreData.Outputs;
}
}
toJSON(key) {
let $superjson = super.toJSON(key);
$superjson.Outputs = this.Outputs;
return $superjson;
}
setInput(Input, Value) {
if (Input >= this.Inputs.length) return false;
let oldOutput = this.Outputs[0];
this.Inputs[Input] = Value;
if (this.Inputs[1]) {
if (!this.Inputs[0] && this.Inputs[2]) {
// set Q low
this.Outputs[0] = false;
this.Outputs[1] = true;
} else if (this.Inputs[0] && !this.Inputs[2]) {
// set Q low
this.Outputs[0] = true;
this.Outputs[1] = false;
} else if (this.Inputs[0] && this.Inputs[2]) {
// set Q low
this.Outputs[0] = !this.Outputs[0];
this.Outputs[1] = !this.Outputs[0];
}
}
if (oldOutput != this.getOutput(0)) {
this.setConnections();
}
}
getOutput(Output=0) {
return this.Outputs[Output];
}
drawElement(x,y,ctx) {
let xOffset = 20;
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
this.drawInputs(ctx,x,y);
this.drawOutputs(ctx,x,y);
}
}
let ElementCatalog_JKFlipFlop = new ElementCatalog_Element("JK-FF","The JK Flip-Flop is a common type of flip-flop that allows for either setting in a specific state, or toggling state.","JK",FlipFlopJK,[]);
ElementReferenceTable.push(ElementCatalog_JKFlipFlop);
ElementCategory_FlipFlop.addElement(ElementCatalog_JKFlipFlop);
class FlipFlopSR extends Element {
constructor(RestoreData = null, logicengine) {
super(RestoreData,logicengine,3);
this.Name = "SR-FF";
this.Outputs = new Array(2);
this.InputLabels = new Array("S","CLK","R");
this.OutputLabels = new Array("Q","~Q");
this.removeProperty("Inputs");
this.Height = 80;
if (RestoreData) {
this.Outputs = RestoreData.Outputs;
}
}
toJSON(key) {
let $superjson = super.toJSON(key);
$superjson.Outputs = this.Outputs;
return $superjson;
}
setInput(Input, Value) {
if (Input >= this.Inputs.length) return false;
let oldOutput = this.Outputs[0];
this.Inputs[Input] = Value;
if (this.Inputs[1]) {
if (!this.Inputs[0] && this.Inputs[2]) {
// set Q low
this.Outputs[0] = false;
this.Outputs[1] = true;
} else if (this.Inputs[0] && !this.Inputs[2]) {
// set Q low
this.Outputs[0] = true;
this.Outputs[1] = false;
}
}
if (oldOutput != this.getOutput(0)) {
this.setConnections();
}
}
getOutput(Output=0) {
return this.Outputs[Output];
}
drawElement(x,y,ctx) {
let xOffset = 20;
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
this.drawInputs(ctx,x,y);
this.drawOutputs(ctx,x,y);
}
}
let ElementCatalog_SRFlipFlop = new ElementCatalog_Element("SR-FF","The SR Flip-Flop is a common type of flip-flop that allows for either setting, or resetting the output state.","SR",FlipFlopSR,[]);
ElementReferenceTable.push(ElementCatalog_SRFlipFlop);
ElementCategory_FlipFlop.addElement(ElementCatalog_SRFlipFlop);
class FlipFlopT extends Element {
constructor(RestoreData = null, logicengine) {
super(RestoreData,logicengine,2);
this.Name = "T-FF";
this.Outputs = new Array(2);
this.InputLabels = new Array("T","CLK");
this.OutputLabels = new Array("Q","~Q");
this.removeProperty("Inputs");
this.Height = 80;
if (RestoreData) {
this.Outputs = RestoreData.Outputs;
}
}
toJSON(key) {
let $superjson = super.toJSON(key);
$superjson.Outputs = this.Outputs;
return $superjson;
}
setInput(Input, Value) {
if (Input >= this.Inputs.length) return false;
let oldOutput = this.Outputs[0];
this.Inputs[Input] = Value;
if (this.Inputs[0] && this.Inputs[1]) {
this.Outputs[0] = !this.Outputs[0];
this.Outputs[1] = !this.Outputs[0];
}
if (oldOutput != this.getOutput(0)) {
this.setConnections();
}
}
getOutput(Output=0) {
return this.Outputs[Output];
}
drawElement(x,y,ctx) {
let xOffset = 20;
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
this.drawInputs(ctx,x,y);
this.drawOutputs(ctx,x,y);
}
}
let ElementCatalog_TFlipFlop = new ElementCatalog_Element("T-FF","The T Flip-Flop is a common type of flip-flop that toggles the output when T is high and CLK goes high.","T",FlipFlopT,[]);
ElementReferenceTable.push(ElementCatalog_TFlipFlop);
ElementCategory_FlipFlop.addElement(ElementCatalog_TFlipFlop);
class FlipFlopD extends Element {
constructor(RestoreData = null, logicengine) {
super(RestoreData,logicengine,2);
this.Name = "D-FF";
this.Outputs = new Array(2);
this.InputLabels = new Array("D","CLK");
this.OutputLabels = new Array("Q","~Q");
this.removeProperty("Inputs");
this.Height = 80;
if (RestoreData) {
this.Outputs = RestoreData.Outputs;
}
}
toJSON(key) {
let $superjson = super.toJSON(key);
$superjson.Outputs = this.Outputs;
return $superjson;
}
setInput(Input, Value) {
if (Input >= this.Inputs.length) return false;
let oldOutput = this.Outputs[0];
let oldInput = this.Inputs[1];
this.Inputs[Input] = Value;
if (this.Inputs[1] && !oldInput) {
this.Outputs[0] = this.Inputs[0];
this.Outputs[1] = !this.Outputs[0];
}
if (oldOutput != this.getOutput(0)) {
this.setConnections();
}
}
getOutput(Output=0) {
return this.Outputs[Output];
}
drawElement(x,y,ctx) {
let xOffset = 20;
this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width,14,this.Designator,"10px Console","#000");
this.drawInputs(ctx,x,y);
this.drawOutputs(ctx,x,y);
}
}
let ElementCatalog_DFlipFlop = new ElementCatalog_Element("D-FF","The D Flip-Flop is a common type of flip-flop that sets the output to equal D if the clock goes high","D",FlipFlopD,[]);
ElementReferenceTable.push(ElementCatalog_DFlipFlop);
ElementCategory_FlipFlop.addElement(ElementCatalog_DFlipFlop);
class LogicAND extends Element {
constructor(RestoreData = null, logicengine,Inputs) {
super(RestoreData,logicengine,Inputs);

View File

@ -2,7 +2,7 @@
MatCat BrowserLogic Simulator
*/
let Version = "0.3.3";
let Version = "0.3.4";
let spanVersion = document.getElementById("version");
spanVersion.innerText = Version;
// get the canvas and get the engine object going