diff --git a/README.md b/README.md
index 64f1913..d81b598 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,11 @@ LZ-String, Copyright 2013 pieroxy under MIT license https://github.com/pieroxy/l
## Changelog
+### 0.4.10
+
+* Draw optimizations, the drawing loop has been considerably optimized.
+* Edit button added to IC properties, note that there is currently no mechanism yet to go back to what you where doing, or to save any changes you make to whatever it was in. This is a WIP feature, I provided the button for those that need to change an IC, they can atleast go into it, make changes and save it, then delete the IC from their current design (presumably pre-saved and loaded after making edits / saves), and place it back in.
+
### 0.4.9
* Fixed a bug where the JS engine would hang if attempting to place a new element when the visual work area is too full of elements.
diff --git a/index.html b/index.html
index 4106bf9..82cabae 100644
--- a/index.html
+++ b/index.html
@@ -191,7 +191,13 @@
-
+
+
+
+
+
+
+
diff --git a/js/baseclasses.js b/js/baseclasses.js
index 83679d7..aaf69ed 100644
--- a/js/baseclasses.js
+++ b/js/baseclasses.js
@@ -77,9 +77,11 @@ class elementContainer {
this.Selected = false;
this.Inputs = new Array();
this.Outputs = new Array();
+ this.StaticImages = {};
this.ICOutputs = 0;
}
+
isSelected(element) {
if (this.Selected) {
for (let a = 0; a < this.Selected.length; a++) {
@@ -132,6 +134,10 @@ class elementContainer {
} else {
unused = true;
element.Designator = designatorTest;
+ if (logicEngine.ActiveContainer === this) {
+ element.clearStatic();
+ element.drawElement(0, 0, element.StaticCtx);
+ }
this.Elements.push(element);
}
}
@@ -205,7 +211,8 @@ class elementContainer {
if (this.Elements[a].isVisible()) {
ctx.save();
if (this.isSelected(this.Elements[a])) 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);
+ //this.Elements[a].drawElement(this.Elements[a].X, this.Elements[a].Y, ctx);
+ ctx.drawImage(this.Elements[a].StaticCanvas,this.Elements[a].X,this.Elements[a].Y);
ctx.restore();
}
}
@@ -245,6 +252,9 @@ class elementContainer {
for (let a = 0; a < this.Selected[0].Properties.length;a++) {
contentString += "
" + this.Selected[0].Properties[a].Name + "
";
switch (this.Selected[0].Properties[a].Type) {
+ case "button":
+ contentString += "";
+ break;
case "int":
contentString += "";
break;
diff --git a/js/elements.js b/js/elements.js
deleted file mode 100644
index 60d0066..0000000
--- a/js/elements.js
+++ /dev/null
@@ -1,2358 +0,0 @@
-let ElementReferenceTable = new Array();
-
-let ElementCategory_Inputs = new ElementCatalog_Category("Inputs","");
-let ElementCategory_Outputs = new ElementCatalog_Category("Outputs","");
-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 ElementCategory_Other = new ElementCatalog_Category("Other" ,"");
-let elementCatalog = new ElementCatalog([ElementCategory_Inputs,
- ElementCategory_Outputs,
- ElementCategory_LOGIC,
- ElementCategory_FlipFlop,
- ElementCategory_Timing,
- ElementCategory_ICs,
- ElementCategory_Other]);
-class ElementProperty {
- constructor(name,type,callback,defaultValue,currentValue = false,values=false,min=0,max=64) {
- /*
- Types
- ---------------------------------------
- bool Boolean Values
- int Integer Value
- string String Value
- list Dropdown box of values
-
- Callback is an object of:
- ---------------------------------------
- CBObject Object to call function on
- CBFunction The function
-
- */
- this.Name = name;
- this.Type = type;
- this.Callback = callback;
- this.DefaultValue = defaultValue;
- if (!currentValue) currentValue = defaultValue;
- this.CurrentValue = currentValue;
- this.Values = values;
- if (!values) this.Values = new Array();
- this.Minimium = min;
- this.Maximium = max;
- }
-
- toJSON(key) {
- return {
- Name: this.Name,
- DefaultValue: this.DefaultValue,
- CurrentValue: this.CurrentValue,
- Values: this.Values,
- Minimium: this.Minimium,
- Maximium: this.Maximium
- };
- }
-
- Call(value) {
- this.Callback.CBObject[this.Callback.CBFunction](value);
- }
-}
-
-class ElementConnection {
- constructor(elementContainer,element,input,output = 0) {
- this.Container = elementContainer;
- this.Element = element;
- this.Input = input;
- this.Output = output;
- }
- toJSON(key) {
- return {
- Element: this.Element.Designator,
- Input: parseInt(this.Input),
- Output: this.Output
- };
- }
-}
-
-class Element extends CanvasTools {
-
- constructor(_Container,RestoreData = null,logicengine,Inputs) {
- super();
- this.Name = "Element";
- this.Designator = "";
- this.Inputs = new Array(Inputs);
- this.InputLabels = new Array(1);
- this.Width = 100;
- this.Height = 60;
- this.inputCircleRadius = 10;
- this.outputCircleRadius = 10;
- this.X = 0;
- this.Y = 0;
- this.OutputConnections = new Array();
- this.MouseOver = false;
- this.MousePosition = {x: 0, y: 0};
- this.Properties = new Array();
- this.LogicEngine = logicengine;
- this.Outputs = new Array(1);
- this.OutputLabels = new Array(1);
- this.NoOutput = false;
- this.OutputLink = 0;
- this._Container = _Container;
- this.Disconnecting = false;
- this.redraw = true;
-
- let inputProperty = new ElementProperty("Inputs","int",{CBObject: this,CBFunction: "ChangeInputs"},2,Inputs,false,2);
- this.Properties.push(inputProperty);
- if (RestoreData) {
- this.Designator = RestoreData.Designator;
- this.Width = RestoreData.Width;
- this.Height = RestoreData.Height;
- this.X = RestoreData.X;
- this.Y = RestoreData.Y;
- if (RestoreData.Properties.length > 0) {
- if (RestoreData.Properties[0].Name == "Inputs") {
- this.ChangeInputs(RestoreData.Properties[0].CurrentValue);
- this.Properties[0].DefaultValue = RestoreData.Properties[0].DefaultValue;
- this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
- this.Properties[0].Values = RestoreData.Properties[0].Values;
- this.Properties[0].Minimium = RestoreData.Properties[0].Minimium;
- this.Properties[0].Maximium = RestoreData.Properties[0].Maximium;
- }
- }
- this.Inputs = RestoreData.Inputs;
- }
- }
-
- ConnectsTo(element,input = false) {
- for (let a = 0; a < this.OutputConnections.length; a++) {
- if (this.OutputConnections[a].Element == element || this.OutputConnections[a].Element.Designator == element) {
- if (input !== false) {
- if (this.OutputConnections[a].Input == input) return this.OutputConnections[a].Output;
- } else {
- return 0;
- }
- }
- }
- return false;
- }
-
- isVisible() {
- let isvisible = false;
- if (this.LogicEngine) {
- let LeftX = this.LogicEngine.Panning.OffsetX;
- if (LeftX < 0) {
- LeftX = Math.abs(LeftX);
- } else {
- LeftX = -Math.abs(LeftX);
- }
- let RightX = LeftX + this.LogicEngine.Canvas.width;
- let TopY = this.LogicEngine.Panning.OffsetY;
- if (TopY < 0) {
- TopY = Math.abs(TopY);
- } else {
- TopY = -Math.abs(TopY);
- }
- let BottomY = TopY + this.LogicEngine.Canvas.height;
- if (((this.X + this.Width) >= LeftX) && ((this.X) <= RightX) && ((this.Y + this.Height) >= TopY) && ((this.Y) <= BottomY)) isvisible = true;
- }
- return isvisible;
-
- }
-
- toJSON(key) {
- return {
- Name: this.Name,
- Designator: this.Designator,
- Inputs: this.Inputs,
- Outputs: this.OutputConnections,
- Output: this.getOutput(),
- Width: this.Width,
- Height: this.Height,
- X: this.X,
- Y: this.Y,
- Properties: this.Properties
- };
- }
-
- getProperty(property) {
- for (let a = 0; a < this.Properties.length;a++) {
- if (this.Properties[a].Name == property) return this.Properties[a];
- }
- return false;
- }
-
- removeProperty(property) {
- for (let a = 0; a < this.Properties.length;a++) {
- if (this.Properties[a].Name == property) {
- this.Properties.splice(a,1);
- return true;
- }
- }
- return false;
- }
-
- totalInputs() {
- return this.Inputs.length;
- }
-
- ChangeInputs(inputs) {
- inputs = parseInt(inputs,10);
- this.Inputs = new Array(inputs);
- this.getProperty("Inputs").CurrentValue = inputs;
- this.Height = inputs*25;
- if (this.Height < 60) this.Height = 60;
- this.redraw = true;
- }
-
- Delete() {
- // if we are active linking stop doing so
- if (this.LogicEngine.ActiveLink == this) this.LogicEngine.ActiveLink = false;
- // Just to clean up connections
- this.Disconnect();
- }
-
- Disconnect(element = false) {
- if (element) {
- for (let a = 0; a < this.OutputConnections.length; a++) {
- if (this.OutputConnections[a].Element == element) {
- this.LogicEngine.RecursionCount = 0;
- let element = this.OutputConnections[a].Element;
- let input = this.OutputConnections[a].Input;
- this.OutputConnections.splice(a,1);
- element.setInput(input, false);
- a--;
- }
- }
- } else {
- this.Disconnecting = true;
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, false);
- this.OutputConnections.splice(a,1);
- a--;
- }
- }
- this.redraw = true;
- }
-
- MouseDown(mousePos) {
- return;
- }
-
- MouseUp(mousePos) {
- return;
- }
-
- LinkOutLocation() {
- return this.OutputLink;
- }
-
- MouseClick(mousePos) {
-
- let ctxMousePos = this.MousePosition;
- let mouseDistOutput = length2D(this.X+(this.Width-10),
- this.Y+(this.Height/2),
- ctxMousePos.x,
- ctxMousePos.y);
- if (this.LogicEngine.ActiveLink) {
- // We need to see if an input is being clicked on to be linked to
- let foundInput = false;
- for (let a = 0; a < this.Inputs.length;a++) {
- let centerY = this.Y + Math.round(this.Height / 2);
- let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
- let firstY = (centerY - (totalHeight/2)) + 12;
-
- let mouseDist = length2D(this.X+10,
- firstY+ (a*24),
- ctxMousePos.x,
- ctxMousePos.y);
- if (mouseDist <= (this.inputCircleRadius)) {
- this.LogicEngine.Link(a);
- foundInput = true;
- break;
- }
- }
-
- } else {
- let foundOutput = false;
- for (let a = 0; a < this.Outputs.length;a++) {
- let centerY = this.Y + Math.round(this.Height / 2);
- let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
- let firstY = (centerY - (totalHeight/2)) + 12;
-
- let mouseDist = length2D(this.X+(this.Width-10),
- firstY+ (a*24),
- ctxMousePos.x,
- ctxMousePos.y);
- if (mouseDist <= (this.outputCircleRadius)) {
- this.OutputLink = {Output: a,x: this.X+(this.Width-10), y: firstY+ (a*24)};
- this.LogicEngine.Link();
- foundOutput = true;
- break;
- }
- }
- }
- }
-
- mouseInside(mousePos) {
- mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
- this.MouseOver = false;
- if (((mousePos.x >= this.X ) && (mousePos.x <= (this.X + this.Width))) && ((mousePos.y >= this.Y ) && (mousePos.y <= (this.Y + this.Height)))) this.MouseOver = true;
- this.MousePosition = mousePos;
- return this.MouseOver;
- }
-
- addConnection(container, element, input,output=0) {
- for (let a = 0; a < this.OutputConnections.length; a++) {
- if (this.OutputConnections[a].Element == element && this.OutputConnections[a].Input == input) {
- // Already existing link, we will remove it instead
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections.splice(a,1);
- element.setInput(input,false);
- return;
- }
- }
- let newConnection = new ElementConnection(container,element,input,output);
- this.OutputConnections.push(newConnection);
- this.LogicEngine.RecursionCount = 0;
- element.setInput(input,this.getOutput(output));
- return newConnection;
- }
-
- drawInputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff") {
- ctx.save();
- //this.inputCircleRadius = 10;
- let centerY = y + Math.round(this.Height / 2);
- let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
- let firstY = (centerY - (totalHeight/2)) + 12;
-
- for (let a = 0; a < this.totalInputs();a++) {
- let mouseDist = length2D(x+10, firstY + (a*24),this.MousePosition.x,this.MousePosition.y);
- ctx.beginPath();
- ctx.arc(x+10,firstY + (a*24),this.inputCircleRadius,0,2*Math.PI);
- ctx.strokeStyle = borderColor;
- ctx.fillStyle = circleColorFalse;
- if (this.Inputs[a]) ctx.fillStyle = circleColorTrue;
- 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") {
- 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) {
- let centerY = this.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.OutputConnections.length;a++) {
- let mouseDist = length2D(this.X+(this.Width - 10), firstY + (this.OutputConnections[a].Output*24),this.MousePosition.x,this.MousePosition.y);
- if (!this.OutputConnections[a].Container.HasElement(this.OutputConnections[a].Element)) {
- // This is a ghosted connection, lets get rid of it
- this.OutputConnections.splice(a,1);
- a--;
- } else {
- let endCenterY = this.OutputConnections[a].Element.Y + Math.round(this.OutputConnections[a].Element.Height / 2);
- let endTotalHeight = this.OutputConnections[a].Element.totalInputs() * ((this.OutputConnections[a].Element.inputCircleRadius*2)+4);
- let endFirstY = (endCenterY - (endTotalHeight/2)) + 12;
-
- let startX = this.X + this.Width;
- let startY = firstY+ (this.OutputConnections[a].Output*24);
- let endX = this.OutputConnections[a].Element.X;
- //let endY = this.OutputConnections[a].Element.Y+(this.OutputConnections[a].Element.inputCircleRadius + 2)+(((this.OutputConnections[a].Input*(4+(this.OutputConnections[a].Element.inputCircleRadius*2))))-2)+(this.OutputConnections[a].Element.inputCircleRadius/2);
- let endY = endFirstY + (this.OutputConnections[a].Input*24);
- let startMidX = startX + ((endX - startX)/2);
- let startMidY = startY;
- let midX = startMidX;
- let midY = startY + ((endY - startY)/2);
- let endMidX = startMidX;
- let endMidY = endY;
-
- ctx.save();
- ctx.beginPath();
- ctx.lineWidth = settings.LinkWidth;
- if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.lineWidth = (settings.LinkWidth * 2);
- ctx.setLineDash(settings.LinkDash);
- ctx.moveTo(startX, startY);
- //ctx.lineTo(endX, endY);
- ctx.quadraticCurveTo(startMidX,startMidY,midX,midY);
- ctx.quadraticCurveTo(endMidX,endMidY,endX,endY);
- ctx.strokeStyle = settings.ActiveConnectionColor;
- if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.ActiveConnectionHoverColor;
- if (!this.getOutput(this.OutputConnections[a].Output)) {
- ctx.strokeStyle = settings.InactiveConnectionColor;
- if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.InactiveConnectionHoverColor;
- }
-
- ctx.stroke();
- ctx.restore();
-
- }
- }
- }
-
- setConnections() {
- for (let a = 0; a < this.OutputConnections.length;a++) {
- 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) {
- if (Value) {
- Value = true;
- } else {
- Value = false;
- }
- let oldInput = this.Inputs[Input];
- if (Input < this.totalInputs()) {
- this.Inputs[Input] = Value;
- } else {
- return;
- }
- let isHigh = this._Container.isHigh(this,Input);
- if (isHigh !== false) this.Inputs[Input] = true;
- if (isHigh === false) this.Inputs[Input] = false;
- if (oldInput != this.Inputs[Input]) {
- this.redraw = true;
- this.setConnections();
- }
- }
-
- getOutput(Output=0) {
- /*
- Should return true or false
- */
- if (this.Disconnecting) return 0;
- return false;
- }
-
- drawElement(x,y,ctx) {
- /*
- Draw routine for the element
- */
- this.drawBorderBox(ctx,x+10,y,this.Width-20,this.Height);
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,"LOGIC");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-
-}
-
-class LabelElement extends Element {
- constructor(_Container, RestoreData = null,logicengine) {
- super(_Container, RestoreData,logicengine,0);
- this.removeProperty("Inputs");
- this.Name = "Label";
- this.Font = "48px Console";
- this.FontStyle = "black";
- this.Label = this.Name;
- this.NoOutput = true;
- let LabelProperty = new ElementProperty("Label","string",{CBObject: this,CBFunction: "setLabel"},"Label","Label",false,1,255);
- this.Properties.push(LabelProperty);
- let LabelSize = new ElementProperty("Size","int",{CBObject: this,CBFunction: "setSize"},"48","48",false,6,99999);
- this.Properties.push(LabelSize);
- let LabelColor = new ElementProperty("Color","color",{CBObject: this,CBFunction: "setColor"},"#000000","#000000",false,0,0);
- this.Properties.push(LabelColor);
-
- if (RestoreData) {
- this.Label = RestoreData.Properties[0].CurrentValue;
- this.Properties[0].CurrentValue = this.Label;
- this.Font = RestoreData.Properties[1].CurrentValue + "px Console";
- this.Properties[1].CurrentValue = RestoreData.Properties[1].CurrentValue;
- this.FontStyle = RestoreData.Properties[2].CurrentValue;
- this.Properties[2].CurrentValue = this.FontStyle;
- }
-
- let textWidth = this.textSize(logicengine.Ctx,this.Label,this.Font);
- this.Width = textWidth.width;
- this.Height = textWidth.height;
-
- }
- setLabel(label) {
- this.Label = label;
- this.Properties[0].CurrentValue = label;
- }
- setSize(size) {
- this.Font = size + "px Console";
- this.Properties[1].CurrentValue = size;
- }
-
- setColor(color) {
- this.FontStyle = color;
- this.Properties[2].CurrentValue = color;
- }
- drawElement(x, y, ctx) {
- let textWidth = this.textSize(ctx,this.Label,this.Font);
- this.Width = textWidth.width;
- this.Height = textWidth.height;
- this.drawTextCentered(ctx,x,y,textWidth.width,textWidth.height,this.Label,this.Font,this.FontStyle);
- }
-}
-let ElementCatalog_Label = new ElementCatalog_Element("Label","Allows you to place a label","🏷",LabelElement,[]);
-ElementReferenceTable.push(ElementCatalog_Label);
-ElementCategory_Other.addElement(ElementCatalog_Label);
-
-
-class ICInput extends Element {
- setPinName(pinname) {
- // Dont actually need it to do anything
- this.Properties[0].CurrentValue = pinname;
- this.Output = false;
- }
-
- constructor(_Container, RestoreData = null,logicengine) {
- super(_Container, RestoreData ,logicengine,0);
- this.removeProperty("Inputs");
- this.Task = new Task("ICInputTask","CLOCK",0,1,this.ClockTick.bind(this));
- this.Name = "ICInput";
- this.Input = false;
-
- let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
- this.Properties.push(pinNameProperty);
- this.LogicEngine.Scheduler.addTask(this.Task);
-
- if (RestoreData) {
- pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
- }
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
- setInput(notused = 0,value) {
-
- if (notused > 0) return;
-
- if (value === false) this.Input = false;
- if (value !== false) this.Input = true;
- this.Inputs[0] = this.Input;
- this.redraw = true;
-
- if (!this.Task.Enabled) {
- this.Task.LastCall = 0;
- this.Task.Enabled = true;
- }
- }
-
- ClockTick() {
- this.Output = this.Input;
- this.Task.Enabled = false;
- this.Task.LastCall = 0;
- this.redraw = true;
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- drawElement(x,y,ctx) {
- /*
- Draw routine for the element
- */
- this.drawBorderBox(ctx,x+10,y,this.Width-30,this.Height);
- this.drawTextCentered(ctx,x,y+2,this.Width-10,14,this.Designator,"12px Console");
- this.drawTextCentered(ctx,x,y,this.Width-10,this.Height,this.Properties[0].CurrentValue,"12px Console");
- this.drawOutputs(ctx,x,y);
- }
-
-}
-let ElementCatalog_ICInput = new ElementCatalog_Element("ICInput","Allows for external signal to be applied to the design when used as an IC.","-<|",ICInput,[]);
-ElementReferenceTable.push(ElementCatalog_ICInput);
-ElementCategory_ICs.addElement(ElementCatalog_ICInput);
-
-class ICOutput extends Element {
- setPinName(pinname) {
- // Dont actually need it to do anything
- this.Properties[0].CurrentValue = pinname;
- this.Output = false;
- }
-
- constructor(_Container, RestoreData = null,logicengine) {
- super(_Container, RestoreData ,logicengine,1);
- this.removeProperty("Inputs");
- this.Task = new Task("ICOutputTask","CLOCK",0,1,this.ClockTick.bind(this));
- this.Name = "ICOutput";
- this.Input = false;
- this.NoOutput = true;
-
- let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
- this.Properties.push(pinNameProperty);
- this.LogicEngine.Scheduler.addTask(this.Task);
- if (RestoreData) {
- pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
- }
-
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
- setInput(notused = 0,value) {
- if (notused > 0) return;
- value = this._Container.isHigh(this,0);
- if (value === false) this.Input = false;
- if (value !== false) this.Input = true;
- this.Inputs[0] = this.Input;
- this.redraw = true;
-
- if (!this.Task.Enabled) {
- this.Task.LastCall = 0;
- this.Task.Enabled = true;
- }
- }
-
- ClockTick() {
- this.Output = this.Input;
- this.Task.Enabled = false;
- this.Task.LastCall = 0;
- this.redraw = true;
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- drawConnections(ctx, settings) {
- // Don't draw connections from IC Outputs
- }
-
- drawElement(x,y,ctx) {
- /*
- Draw routine for the element
- */
- this.drawBorderBox(ctx,x+20,y,this.Width-10,this.Height);
- this.drawTextCentered(ctx,x+20,y+2,this.Width-10,14,this.Designator,"12px Console");
- this.drawTextCentered(ctx,x+20,y,this.Width-10,this.Height,this.Properties[0].CurrentValue,"12px Console");
- this.drawInputs(ctx,x,y);
- }
-}
-let ElementCatalog_ICOutput = new ElementCatalog_Element("ICOutput","Allows for routing a signal externally when the design is used as an IC.","|>-",ICOutput,[]);
-ElementReferenceTable.push(ElementCatalog_ICOutput);
-ElementCategory_ICs.addElement(ElementCatalog_ICOutput);
-
-class ICElement extends Element {
- constructor(_Container, RestoreData = null, logicengine,ICSettings = null) {
-
-
- let newContainer = false;
- let icContainer = false;
- try {
- newContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
- icContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
- } catch (ex) {
- newContainer = loadContainer(JSON.parse(ICSettings.Container));
- icContainer = loadContainer(JSON.parse(ICSettings.Container));
- }
- ICSettings.Inputs = new Array();
- ICSettings.Outputs = new Array();
-
- for (let a = 0; a < newContainer.Elements.length; a++) {
- if (newContainer.Elements[a] instanceof ICInput) {
- let cConn = new ContainerConnection(null,newContainer,null,newContainer.Elements[a],0);
- ICSettings.Inputs.push(cConn);
- }
- if (newContainer.Elements[a] instanceof ICOutput) {
- let cConn = new ContainerConnection(newContainer,null,newContainer.Elements[a],null,0);
- ICSettings.Outputs.push(cConn);
- }
- }
-
- let totalInputs = ICSettings.Inputs.length;
- let totalOutputs = ICSettings.Outputs.length;
-
- super(_Container, RestoreData, logicengine, totalInputs);
- this.removeProperty("Inputs");
- this.ICBlueprint = icContainer;
- this.Outputs = ICSettings.Outputs;
- this.InputConnections = ICSettings.Inputs;
- this.Container = newContainer;
- this.Name = ICSettings.Name;
- this.Icon = "≡[°]≡";
- this.Width = 140;
- this.Height = (totalInputs >= totalOutputs) ? totalInputs*25 : totalOutputs*25;
- if (this.Height < 60) this.Height = 60;
-
- if (RestoreData) {
- //console.log(RestoreData);
- }
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.IsIC = true;
- let eCat = getElementInfo(this.Name);
- superjson.Description = eCat.Description;
- superjson.ICOutputs = this.Outputs;
- superjson.InputConnections = this.InputConnections;
- superjson.ICBlueprint = this.ICBlueprint;
- superjson.ICContainer = this.Container;
- return superjson;
- }
-
- Disconnect(element = false) {
- super.Disconnect(element);
- if (element) {
- for (let a = 0; a < this.Outputs.length; a++) {
- if (this.Outputs[a].toElement == element || this.Outputs[a].fromElement == element) {
- if (this.Outputs[a].toElement == element) {
- // It's a too element, we need to tell it we are not high anymore
- } else {
- // Its a from element, we need to tell it's too element
- }
- }
- }
- } else {
- // We are disconnecting all of our own connections
- for (let a = 0; a < this.Outputs.length; a++) {
-
- }
- }
- }
-
- addConnection(container, element, input, output = 0) {
- super.addConnection(container, element, input, output);
- this.Outputs[output].toElementContainer = container;
- this.Outputs[output].toElement = element;
- this.Outputs[output].Input = input;
- this.Outputs[output].fromElement.addConnection(container,element,input,0);
- }
-
- setInput(Input, Value) {
- if (this.InputConnections.length >= Input) {
- // No need to worry about recursion as this goes to an input element which is buffered
- Value = this._Container.isHigh(this,Input);
- this.Inputs[Input] = (Value === false) ? false : true;
- this.redraw = true;
- this.InputConnections[Input].toElement.setInput(0,Value);
- }
- return false;
- }
-
- getOutput(Output = 0) {
- if (super.getOutput() === 0) return false;
- if (this.Outputs.length >= Output) {
- return this.Outputs[Output].fromElement.getOutput(this.Outputs[Output].Input);
- }
- return false;
- }
-
- drawElement(x,y,ctx) {
- /*
- Draw routine for the element
- */
- this.drawBorderBox(ctx,x+20,y,this.Width-40,this.Height);
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Icon,"12px Console");
- 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);
- }
-
- drawInputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff") {
- ctx.save();
- let centerY = y + Math.round(this.Height / 2);
- let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
- let firstY = (centerY - (totalHeight/2)) + 12;
-
- for (let a = 0; a < this.totalInputs();a++) {
- let mouseDist = length2D(x+10, firstY + (a*24),this.MousePosition.x,this.MousePosition.y);
- ctx.beginPath();
- ctx.arc(x+10,firstY + (a*24),this.inputCircleRadius,0,2*Math.PI);
- ctx.strokeStyle = borderColor;
- ctx.fillStyle = circleColorFalse;
- if (this.Inputs[a]) ctx.fillStyle = circleColorTrue;
- if ((mouseDist <= (this.inputCircleRadius)) && this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
- ctx.fill();
- ctx.stroke();
- this.drawText(ctx,x+(this.inputCircleRadius*2)+ 5,(firstY + (a*24)) + 5,this.InputConnections[a].toElement.Properties[0].CurrentValue,"10px Console","#000");
- }
- ctx.restore();
- }
- 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.Outputs[a].fromElement.getOutput(0)) ctx.fillStyle = circleColorTrue;
- if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
- ctx.fill();
- ctx.stroke();
- let textSize = false;
- if (ctx,this.Outputs[a]) textSize = this.textSize(ctx,this.Outputs[a].fromElement.Properties[0].CurrentValue,"10px Console");
- this.drawText(ctx,(x+(this.Width-10)) - (textSize.width + 5 + (this.outputCircleRadius*2)),(firstY + (a*24)) + 5,this.Outputs[a].fromElement.Properties[0].CurrentValue,"10px Console","#000");
- }
- ctx.restore();
- }
-
-}
-
-class ClockElement extends Element {
- ClockTick() {
- if (this.Inputs[0]) {
- this.Output = ~this.Output;
- if (this.Output) {
- this.Task.Time = Math.round(this.Period * this.Duty);
- } else {
- this.Task.Time = this.Period - Math.round(this.Period * this.Duty);
- }
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- setInput(Input, Value) {
-
- super.setInput(Input, Value);
-
- if (!this.Inputs[0]) {
- this.Output = false;
- this.Task.LastCall = 0;
- this.Task.Enabled = false;
- for (let a = 0; a < this.OutputConnections.length;a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
- }
- } else {
- this.Task.Enabled = true;
- }
- }
-
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,1);
- this.removeProperty("Inputs");
- this.Name = "Clock";
- this.Period = 1000;
- this.Duty = 0.5;
- this.Output = false;
- this.Width = 100;
- this.Task = new Task("ClockTask","CLOCK",0,Math.round(this.Period * this.Duty),this.ClockTick.bind(this));
- this.setInput(0,true);
- this.removeProperty("Inputs");
- let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},1000,false,false,4,999999);
- let dutyProperty = new ElementProperty("Duty","int",{CBObject: this,CBFunction: "setDuty"},50,false,false,0,100);
- if (RestoreData) {
- if (this.Height != RestoreData.Height) {
- // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
- this.Height = RestoreData.Height;
- }
- periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
- periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
- periodProperty.Values = RestoreData.Properties[0].Values;
- periodProperty.Minimium = RestoreData.Properties[0].Minimium;
- periodProperty.Maximium = RestoreData.Properties[0].Maximium;
- dutyProperty.DefaultValue = RestoreData.Properties[1].DefaultValue;
- dutyProperty.CurrentValue = RestoreData.Properties[1].CurrentValue;
- dutyProperty.Values = RestoreData.Properties[1].Values;
- dutyProperty.Minimium = RestoreData.Properties[1].Minimium;
- dutyProperty.Maximium = RestoreData.Properties[1].Maximium;
-
- this.Period = periodProperty.CurrentValue;
- this.Duty = dutyProperty.CurrentValue/100;
- this.Task.Enabled = RestoreData.Task.Enabled;
- this.Task.Time = RestoreData.Task.Time;
- this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
- this.Task.CallCount = RestoreData.Task.CallCount;
- }
- this.Properties.push(periodProperty);
- this.Properties.push(dutyProperty);
- this.LogicEngine.Scheduler.addTask(this.Task);
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
- setPeriod(period) {
- this.Period = period;
- this.Task.LastCall = 0;
- this.getProperty("Period").CurrentValue = period;
- }
-
- setDuty(duty) {
- this.Duty = duty/100;
- this.Task.LastCall = 0;
- this.getProperty("Duty").CurrentValue = duty;
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
- this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms " + (this.Duty * 100) + "%","10px Console");
- if (this.Task.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_CLOCK = new ElementCatalog_Element("Clock","The clock will output a pulse for the length of the duty cycle for a given period. I.E. if a 1000ms period with 50% duty cycle is given, the output will be high for 500ms and low for 500ms. The clock can be disabled with a low input, this also resets the clocks start time to allow for syncing.","🕑",ClockElement,[]);
-ElementReferenceTable.push(ElementCatalog_CLOCK);
-ElementCategory_Timing.addElement(ElementCatalog_CLOCK);
-
-class PulseElement extends Element {
- ClockTick() {
- this.Output = false;
- this.Task.Enabled = false;
- this.Task.LastCall = Date.now();
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- setInput(Input, Value) {
- if (Input > 0) return;
-
- Value = this._Container.isHigh(this,Input);
- //super.setInput(Input, Value);
- this.Inputs[Input] = (Value === false) ? false : true;
- this.redraw = true;
-
- if (this.Inputs[0] && !this.Task.Enabled) {
- this.Output = true;
- for (let a = 0; a < this.OutputConnections.length;a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
- }
- this.Task.LastCall = Date.now();
- this.Task.Enabled = true;
- }
- }
-
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,1);
- this.removeProperty("Inputs");
- this.Name = "Pulse";
- this.Period = 100;
- this.Output = false;
- this.Width = 100;
- this.Task = new Task("PulseTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
- this.removeProperty("Inputs");
- let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
- if (RestoreData) {
- if (this.Height != RestoreData.Height) {
- // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
- this.Height = RestoreData.Height;
- }
- periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
- periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
- periodProperty.Values = RestoreData.Properties[0].Values;
- periodProperty.Minimium = RestoreData.Properties[0].Minimium;
- periodProperty.Maximium = RestoreData.Properties[0].Maximium;
-
- this.Period = periodProperty.CurrentValue;
-
- this.Task.Enabled = RestoreData.Task.Enabled;
- this.Task.Time = RestoreData.Task.Time;
- this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
- this.Task.CallCount = RestoreData.Task.CallCount;
- }
-
-
- this.Properties.push(periodProperty);
- this.LogicEngine.Scheduler.addTask(this.Task);
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
-
- setPeriod(period) {
- this.Period = parseInt(period);
- this.Task.Time = parseInt(period);
- this.getProperty("Period").CurrentValue = parseInt(period);
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
- this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
- if (this.Task.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_PULSE = new ElementCatalog_Element("Pulse","The pulse upon input will output a high only for as long as the period set, I.E. if a period of 1000ms is set then any input no matter how long, will trigger a pulse 1000ms long.","|\\__",PulseElement,[]);
-ElementReferenceTable.push(ElementCatalog_PULSE);
-ElementCategory_Timing.addElement(ElementCatalog_PULSE);
-
-class DelayElement extends Element {
- ClockTick() {
- if (this.Output) {
- this.Output = false;
- this.Task.Enabled = false;
- this.Task.LastCall = Date.now();
- this.Task.Time = this.Period;
- if (this.InputPulses.length > 0) {
- this.Task.Enabled = true;
- }
- } else {
- this.Output = true;
- this.Task.Enabled = false;
- if (this.InputPulses.length > 0) {
- this.Task.Time = this.InputPulses[0];
- this.Task.Enabled = true;
- this.InputPulses.splice(0,1);
- } else {
- if (this.InputEnd > 0) {
- this.Task.Time = this.InputEnd - this.InputStart;
- this.Task.Enabled = true;
- }
- }
- this.Task.LastCall = Date.now();
- }
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- setInput(Input, Value) {
- if (Input > 0) return;
- Value = this._Container.isHigh(this,Input);
- if (this.Inputs[Input] == (Value === false) ? false : true) return;
- //super.setInput(Input, Value);
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- if (this.Inputs[0] && !this.Task.Enabled) {
- this.InputStart = Date.now();
- this.InputEnd = 0;
- this.Output = false;
- for (let a = 0; a < this.OutputConnections.length;a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
- }
- this.Task.LastCall = Date.now();
- this.Task.Enabled = true;
- } else {
- if (!this.Inputs[Input]) {
- this.InputEnd = Date.now();
- if (this.InputPulses.length > 0) {
- this.InputPulses.push(this.InputEnd - this.InputStart);
- this.InputStart = 0;
- this.InputEnd = 0;
- } else {
- if (!this.Task.Enabled) {
- this.Task.LastCall = Date.now();
- this.Task.Time = (this.InputEnd - this.InputStart) - (Date.now() - this.InputEnd);
- this.Task.Enabled = true;
- } else {
- this.InputPulses.push(this.InputEnd - this.InputStart);
- }
- }
- } else {
- // We are in a condition where input went true, but the task is already enabled
- this.InputStart = Date.now();
- }
- }
- }
-
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,1);
- this.removeProperty("Inputs");
- this.Name = "Delay";
- this.Period = 100;
- this.Output = false;
- this.Width = 100;
- this.Task = new Task("DelayTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
- this.Task.Enabled = false;
- this.removeProperty("Inputs");
- let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
- if (RestoreData) {
- if (this.Height != RestoreData.Height) {
- // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
- this.Height = RestoreData.Height;
- }
- periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
- periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
- periodProperty.Values = RestoreData.Properties[0].Values;
- periodProperty.Minimium = RestoreData.Properties[0].Minimium;
- periodProperty.Maximium = RestoreData.Properties[0].Maximium;
-
- this.Period = periodProperty.CurrentValue;
-
- this.Task.Enabled = RestoreData.Task.Enabled;
- this.Task.Time = RestoreData.Task.Time;
- this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
- this.Task.CallCount = RestoreData.Task.CallCount;
- }
- this.Properties.push(periodProperty);
- this.InputStart = 0;
- this.InputEnd = 0;
- this.InputPulses = new Array();
- this.Inputs[0] = false;
- this.LogicEngine.Scheduler.addTask(this.Task);
- }
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.InputStart = this.InputStart;
- superjson.InputEnd = this.InputEnd;
- superjson.InputPulses = this.InputPulses;
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
- setPeriod(period) {
- this.Period = parseInt(period);
- this.Task.Time = parseInt(period);
- this.getProperty("Period").CurrentValue = parseInt(period);
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
- this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
- if (this.Task.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms " + "(" + this.InputPulses.length + ")","10px Console");
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_DELAY = new ElementCatalog_Element("Delay","The delay element will delay any input signal for the given period then output it for the same length of time it came in. Inputs will be buffered and played back out at the period rate.","__|\\",DelayElement,[]);
-ElementReferenceTable.push(ElementCatalog_DELAY);
-ElementCategory_Timing.addElement(ElementCatalog_DELAY);
-
-class output4bitDisplay extends Element {
- constructor(_Container, RestoreData = null,logicengine) {
- super(_Container, RestoreData,logicengine,4);
- this.Name = "4B Display";
- this.Output = false;
- this.OutputChar = "0";
- this.NoOutput = true;
- this.Width = 100;
- this.Height = 100;
- this.Inputs = [0,0,0,0];
- this.InputLabels = ["I0","I1","I2","I3"];
- this.Type = 0;
- this.removeProperty("Inputs");
- let typeProperty = new ElementProperty("Type","list",{CBObject: this,CBFunction: "setType"},0,0,[{Value: "0", String: "Hex"},{Value: "1", String: "Decimal"},{Value: "2", String: "BCD"}],0,0);
- this.Properties.push(typeProperty);
-
- if (RestoreData) {
- if (RestoreData.Properties.length > 0) {
- this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
- this.Properties[0].Values = RestoreData.Properties[0].Values;
- this.setType(this.Properties[0].CurrentValue);
- }
- }
- }
-
- setType(type) {
- this.Properties[0].CurrentValue = type;
- switch (type) {
- case "0":
- this.Type = 0;
- break;
- case "1":
- this.Type = 1;
- break;
- case "2":
- this.Type = 2;
- break;
- }
- this.setInput(0,this.Inputs[0]); // Trigger a proper recalc of the display.
- }
-
- setInput(Input, Value) {
- Value = this._Container.isHigh(this,Input);
- this.Inputs[Input] = (Value !== false) ? 1 : 0;
- let outchar = (this.Inputs[3] << 3) + (this.Inputs[2] << 2) + (this.Inputs[1] << 1) + (this.Inputs[0]);
- this.redraw = true;
-
- this.OutputChar = (outchar);
- switch (outchar) {
- case 10:
- if (this.Type == 0) this.OutputChar = 'A';
- if (this.Type == 1) this.OutputChar = '10';
- if (this.Type == 2) this.OutputChar = '0';
- break;
- case 11:
- if (this.Type == 0) this.OutputChar = 'B';
- if (this.Type == 1) this.OutputChar = '11';
- if (this.Type == 2) this.OutputChar = '1';
- break;
- case 12:
- if (this.Type == 0) this.OutputChar = 'C';
- if (this.Type == 1) this.OutputChar = '12';
- if (this.Type == 2) this.OutputChar = '2';
- break;
- case 13:
- if (this.Type == 0) this.OutputChar = 'D';
- if (this.Type == 1) this.OutputChar = '13';
- if (this.Type == 2) this.OutputChar = '3';
- break;
- case 14:
- if (this.Type == 0) this.OutputChar = 'E';
- if (this.Type == 1) this.OutputChar = '14';
- if (this.Type == 2) this.OutputChar = '4';
- break;
- case 15:
- if (this.Type == 0) this.OutputChar = 'F';
- if (this.Type == 1) this.OutputChar = '15';
- if (this.Type == 2) this.OutputChar = '5';
- break;
- }
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height);
- let fontStyle = "72px Console";
- if (this.Type == 1) fontStyle = "36px Console";
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.OutputChar,fontStyle);
- this.drawInputs(ctx,x,y);
- }
-}
-
-let ElementCatalog_Output_4BDisplay = new ElementCatalog_Element("4B Display","A display! Takes a 4 bit input and shows hex value of the input.","[8]",output4bitDisplay,[]);
-ElementReferenceTable.push(ElementCatalog_Output_4BDisplay);
-ElementCategory_Outputs.addElement(ElementCatalog_Output_4BDisplay);
-
-
-class inputElement extends Element {
- constructor(_Container, RestoreData = null,logicengine) {
- super(_Container, RestoreData,logicengine,0);
- this.Name = "InputElement";
- this.Output = false;
- this.Width = 100;
- this.removeProperty("Inputs");
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x,y,this.Width,this.Height);
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,"IN");
- this.drawOutputs(ctx,x,y);
- }
-}
-
-
-
-class InputSwitch extends inputElement {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine);
- this.Name = "Switch";
- this.Height = 70;
- if (RestoreData) this.Output = RestoreData.Output;
- }
-
- MouseClick(mousePos) {
- mousePos = this.LogicEngine.getCanvasMousePos(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;
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
- //this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
- this.drawBorderBox(ctx,x+5,y+25,50,10,1,"#ccc","#777");
- if (this.getOutput()) {
- this.drawBorderBox(ctx,x+15,y+5,30,25,1,"#ccc","#777");
- this.drawTextCentered(ctx,x,y+this.Height - 30,this.Width-(this.outputCircleRadius*2)-20,12,"OFF","12px Console","#000");
- } else {
- this.drawBorderBox(ctx,x+15,y+30,30,25,1,"#ccc","#777");
- this.drawTextCentered(ctx,x,y+7,this.Width-(this.outputCircleRadius*2)-20,12,"ON","12px Console","#000");
- }
- this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_SWITCH = new ElementCatalog_Element("Switch","The switch allows for toggling an output low or high.","|-",InputSwitch,[]);
-ElementReferenceTable.push(ElementCatalog_SWITCH);
-ElementCategory_Inputs.addElement(ElementCatalog_SWITCH);
-
-class InputButton extends inputElement {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine);
- this.Name = "Button";
- this.Height = 70;
- }
-
- MouseDown(mousePos) {
- mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
- if ((mousePos.x >= (this.X + 5)) && (mousePos.x <= (this.X + 55)) && (mousePos.y >= (this.Y + 5)) && (mousePos.y <= (this.Y + 55))) {
- let old_output = this.Output;
- this.Output = true;
- this.LogicEngine.MouseDown = false; // Prevent movement on the button when its being pushed
- if (old_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());
- }
- }
- }
- }
-
- MouseUp(mousePos) {
- let old_output = this.Output;
- this.Output = false;
- if (old_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());
- }
- }
- }
-
- mouseInside(mousePos) {
- let mousestat = super.mouseInside(mousePos);
- if (this.Output && !this.MouseOver) {
- this.Output = false;
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
- return mousestat;
- }
-
- drawElement(x, y, ctx) {
- this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
- this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
- this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_BUTTON = new ElementCatalog_Element("Button","The button only outputs high when the button is pressed.","[o]",InputButton,[]);
-ElementReferenceTable.push(ElementCatalog_BUTTON);
-ElementCategory_Inputs.addElement(ElementCatalog_BUTTON);
-
-class InputKeypad extends inputElement {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData, logicengine);
- this.Name = "Keypad";
- this.Height = 210;
- this.Width = 200;
- this.Outputs = new Array(false,false,false,false,false,false,false,false);
- this.OutputLabels = new Array("O0","O1","O2","O3","F1","F2","F3","Clr");
- this.ButtonRows = 5;
- this.ButtonColumns = 4;
- this.Buttons = new Array(
- {x: 0, y: 0,Value: 7, Text: "7"}, {x: 1, y: 0,Value: 8, Text: "8"}, {x: 2, y: 0,Value: 9, Text: "9"}, {x: 3, y: 0,Value: 16, Text: "F1"},
- {x: 0, y: 1,Value: 4, Text: "4"}, {x: 1, y: 1,Value: 5, Text: "5"}, {x: 2, y: 1,Value: 6, Text: "6"}, {x: 3, y: 1,Value: 32, Text: "F2"},
- {x: 0, y: 2,Value: 1, Text: "1"}, {x: 1, y: 2,Value: 2, Text: "2"}, {x: 2, y: 2,Value: 3, Text: "3"}, {x: 3, y: 2,Value: 64, Text: "F3"},
- {x: 0, y: 3,Value: 10, Text: "A"}, {x: 1, y: 3,Value: 0, Text: "0"}, {x: 2, y: 3,Value: 11, Text: "B"}, {x: 3, y: 3,Value: 128, Text: "Clr"},
- {x: 0, y: 1,Value: 12, Text: "C"}, {x: 1, y: 1,Value: 13, Text: "D"}, {x: 2, y: 1,Value: 14, Text: "E"}, {x: 3, y: 1,Value: 15, Text: "F"}
- );
- let typeProperty = new ElementProperty("Function Type","list",{CBObject: this,CBFunction: "setType"},true,true,[{Value: true, String: "Switch"},{Value: false, String: "Button"}],0,0);
- this.Properties.push(typeProperty);
-
- if (RestoreData) {
- if (RestoreData.OutputValues) {
- this.Outputs = RestoreData.OutputValues;
- }
- if (RestoreData.Properties?.length > 0) {
- this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
- }
- }
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.OutputValues = this.Outputs;
- return superjson;
- }
-
- setType(type) {
- if (type == "false") type = false;
- if (type == "true") type = true;
- this.Properties[0].CurrentValue = type;
- if (!type) {
- this.Outputs[4] = false;
- this.Outputs[5] = false;
- this.Outputs[6] = false;
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
- }
- }
- }
-
- MouseDown(mousePos) {
- if (!this.Properties[0].CurrentValue) {
- mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
- let buttonWidth = 34;
- let buttonHeight = 34;
- let x = this.X;
- let y = this.Y;
-
- for (let pY = 0; pY < this.ButtonRows; pY++) {
- for (let pX = 0; pX < this.ButtonColumns; pX++) {
-
- if ((mousePos.x >= (x + (5 * (pX + 1)) + (buttonWidth * pX))) && (mousePos.x <= ((x + (5 * (pX + 1)) + (buttonWidth * pX)) + buttonWidth)) && (mousePos.y >= (y + (5 * (pY + 1)) + (buttonHeight * pY))) && (mousePos.y <= ((y + (5 * (pY + 1)) + (buttonHeight * pY)) + buttonHeight))) {
- let button = this.Buttons[(pY * this.ButtonColumns) + pX];
- if (button.Value & 0b0010000) this.Outputs[4] = true;
- if (button.Value & 0b0100000) this.Outputs[5] = true;
- if (button.Value & 0b1000000) this.Outputs[6] = true;
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
- }
- }
- }
- }
- }
- }
-
- MouseUp(mousePos) {
- if (!this.Properties[0].CurrentValue) {
- this.Outputs[4] = false;
- this.Outputs[5] = false;
- this.Outputs[6] = false;
- }
- }
-
- MouseClick(mousePos) {
- mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
- super.MouseClick(mousePos);
- let buttonWidth = 34;
- let buttonHeight = 34;
- let x = this.X;
- let y = this.Y;
-
- for (let pY = 0; pY < this.ButtonRows; pY++) {
- for (let pX = 0; pX < this.ButtonColumns; pX++) {
-
- if ((mousePos.x >= (x + (5*(pX+1))+(buttonWidth*pX))) && (mousePos.x <= ((x + (5*(pX+1))+(buttonWidth*pX)) + buttonWidth)) && (mousePos.y >= (y + (5*(pY+1))+(buttonHeight*pY))) && (mousePos.y <= ((y + (5*(pY+1))+(buttonHeight*pY))+buttonHeight))) {
- let button = this.Buttons[(pY*this.ButtonColumns) + pX];
-
- if (button.Value <= 15) {
- if (button.Value & 0b0000001) this.Outputs[0] = true; else this.Outputs[0] = false;
- if (button.Value & 0b0000010) this.Outputs[1] = true; else this.Outputs[1] = false;
- if (button.Value & 0b0000100) this.Outputs[2] = true; else this.Outputs[2] = false;
- if (button.Value & 0b0001000) this.Outputs[3] = true; else this.Outputs[3] = false;
- }
-
- if (this.Properties[0].CurrentValue) {
- if (button.Value & 0b0010000) this.Outputs[4] = !this.Outputs[4];
- if (button.Value & 0b0100000) this.Outputs[5] = !this.Outputs[5];
- if (button.Value & 0b1000000) this.Outputs[6] = !this.Outputs[6];
- }
- if (button.Value & 0b10000000) {
- for (let a = 0; a < this.Outputs.length; a++) {
- this.Outputs[a] = false;
- }
- this.Outputs[7] = true;
- let tempTimer = new Task("keypadClearTask","Keypad Clear",0,100,this.ClockTick.bind(this),true);
- tempTimer.LastCall = Date.now();
- tempTimer.Enabled = true;
- this.LogicEngine.Scheduler.addTask(tempTimer);
- }
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
- }
- }
- }
- }
- }
-
- ClockTick() {
- console.log("Keypad clear signal");
- this.Outputs[7] = false;
- this.redraw = true;
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
- }
- }
-
- getOutput(Output = 0) {
- if (super.getOutput() === 0) return false;
- return (this.Outputs[Output]) ? true : false;
- }
-
- drawElement(x, y, ctx) {
- let buttonWidth = 34;
- let buttonHeight = 34;
-
- this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
-
- for (let pY = 0; pY < this.ButtonRows; pY++) {
- for (let pX = 0; pX < this.ButtonColumns; pX++) {
- this.drawBorderBox(ctx, x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, 1, "#ccc", "#777");
- this.drawTextCentered(ctx,x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, this.Buttons[(pY*this.ButtonColumns) + pX].Text,"16px Console", "#fff");
-
- }
- }
-
- this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_Keypad = new ElementCatalog_Element("Keypad","A hex entry keypad with 4 bit output","[][]",InputKeypad,[]);
-ElementReferenceTable.push(ElementCatalog_Keypad);
-ElementCategory_Inputs.addElement(ElementCatalog_Keypad);
-
-
-class FlipFlopJK extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, 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.OutputStates;
- }
- }
-
- toJSON(key) {
- let $superjson = super.toJSON(key);
-
- $superjson.OutputStates = this.Outputs;
- return $superjson;
- }
-
- setInput(Input, Value) {
- if (Input >= this.Inputs.length) return false;
- Value = this._Container.isHigh(this,Input);
- if (Value !== false) Value = true;
- let oldOutput = this.Outputs[0];
- let oldOutput2 = this.Outputs[1];
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- 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) || oldOutput2 != this.getOutput(1)) {
- this.setConnections();
- }
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- 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.","",FlipFlopJK,[]);
-ElementReferenceTable.push(ElementCatalog_JKFlipFlop);
-ElementCategory_FlipFlop.addElement(ElementCatalog_JKFlipFlop);
-
-class FlipFlopSR extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, 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.OutputStates;
- }
- }
-
- toJSON(key) {
- let $superjson = super.toJSON(key);
-
- $superjson.OutputStates = this.Outputs;
- return $superjson;
- }
-
- setInput(Input, Value) {
- if (Input >= this.Inputs.length) return false;
- Value = this._Container.isHigh(this,Input);
- if (Value !== false) Value = true;
- let oldOutput = this.Outputs[0];
- let oldOutput2 = this.Outputs[1];
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- 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) || oldOutput2 != this.getOutput(1)) {
- this.setConnections();
- }
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- 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.","",FlipFlopSR,[]);
-ElementReferenceTable.push(ElementCatalog_SRFlipFlop);
-ElementCategory_FlipFlop.addElement(ElementCatalog_SRFlipFlop);
-
-class FlipFlopT extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, 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.OutputStates;
- }
- }
-
- toJSON(key) {
- let $superjson = super.toJSON(key);
-
- $superjson.OutputStates = this.Outputs;
- return $superjson;
- }
-
- setInput(Input, Value) {
- if (Input >= this.Inputs.length) return false;
- Value = this._Container.isHigh(this,Input);
- if (Value !== false) Value = true;
- let oldOutput = this.Outputs[0];
- let oldOutput2 = this.Outputs[1];
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- if (this.Inputs[0] && this.Inputs[1]) {
- this.Outputs[0] = !this.Outputs[0];
- this.Outputs[1] = !this.Outputs[0];
- }
- if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
- this.setConnections();
- }
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- 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.","",FlipFlopT,[]);
-ElementReferenceTable.push(ElementCatalog_TFlipFlop);
-ElementCategory_FlipFlop.addElement(ElementCatalog_TFlipFlop);
-
-class FlipFlopD extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, 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.OutputStates;
- }
- }
-
- toJSON(key) {
- let $superjson = super.toJSON(key);
-
- $superjson.OutputStates = this.Outputs;
- return $superjson;
- }
-
- setInput(Input, Value) {
- if (Input >= this.Inputs.length) return false;
- Value = this._Container.isHigh(this,Input);
- if (Value !== false) Value = true;
- let oldOutput = this.Outputs[0];
- let oldOutput2 = this.Outputs[1];
- let oldInput = this.Inputs[1];
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- if (this.Inputs[1] && !oldInput) {
- this.Outputs[0] = this.Inputs[0];
- this.Outputs[1] = !this.Outputs[0];
- }
- if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
- this.setConnections();
- }
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- 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","",FlipFlopD,[]);
-ElementReferenceTable.push(ElementCatalog_DFlipFlop);
-ElementCategory_FlipFlop.addElement(ElementCatalog_DFlipFlop);
-
-
-class LogicAND extends Element {
- constructor(_Container, RestoreData = null, logicengine,Inputs) {
- super(_Container, RestoreData,logicengine,Inputs);
- this.Name = "AND";
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- let ANDResult = true;
- for (let a = 0; a < this.totalInputs();a++) {
- if (!this.Inputs[a]) ANDResult = false;
- }
- return ANDResult;
- }
- drawElement(x,y,ctx) {
- //this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height);
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+xOffset,y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - xOffset),y,x+(this.Width-xOffset),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - xOffset),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+xOffset,y+this.Height);
- ctx.lineTo(x+xOffset,y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
- ctx.restore();
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_AND = new ElementCatalog_Element("AND","The AND gate only outputs high when all of the inputs are high.","&",LogicAND,[2]);
-ElementReferenceTable.push(ElementCatalog_AND);
-ElementCategory_LOGIC.addElement(ElementCatalog_AND);
-
-class LogicNAND extends LogicAND {
- constructor(_Container, RestoreData = null, logicengine,Inputs) {
- super(_Container, RestoreData,logicengine,Inputs);
- this.Name = "NAND";
- this.Width = this.Width + 10;
- }
-
- getOutput(Output=0) {
- if (this.Disconnecting) return false;
- if (super.getOutput()) {
- return false;
- } else {
- return true;
- }
- }
-
- drawElement(x,y,ctx) {
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+xOffset,y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+xOffset,y+this.Height);
- ctx.lineTo(x+xOffset,y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.strokeStyle = "#000000";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
-
- ctx.beginPath();
- ctx.fillStyle = "#000000";
- ctx.strokeStyle = "#000000";
- ctx.lineWidth = "1";
- ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
- ctx.stroke();
- ctx.fill();
-
- ctx.restore();
- this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_NAND = new ElementCatalog_Element("NAND","The NAND gate always outputs a high signal unless all the inputs are high then it goes low.","!&",LogicNAND,[2]);
-ElementReferenceTable.push(ElementCatalog_NAND);
-ElementCategory_LOGIC.addElement(ElementCatalog_NAND);
-
-class LogicOR extends Element {
- constructor(_Container, RestoreData = null, logicengine,Inputs) {
- super(_Container, RestoreData,logicengine,Inputs);
- this.Name = "OR";
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- let ORResult = false;
- for (let a = 0; a < this.totalInputs();a++) {
- if (this.Inputs[a]) ORResult = true;
- }
- return ORResult;
- }
- drawElement(x,y,ctx) {
- let drawWidth = this.Width;
- let drawHeight = this.Height;
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
- ctx.restore();
-
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_OR = new ElementCatalog_Element("OR","The OR gate outputs a high when any input is HIGH.","|",LogicOR,[2]);
-ElementReferenceTable.push(ElementCatalog_OR);
-ElementCategory_LOGIC.addElement(ElementCatalog_OR);
-
-class LogicNOR extends LogicOR {
- constructor(_Container, RestoreData = null, logicengine,Inputs) {
- super(_Container, RestoreData,logicengine,Inputs);
- this.Name = "NOR";
- this.Width = this.Width + 10;
- }
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- if (super.getOutput()) {
- return false;
- } else {
- return true;
- }
- }
- drawElement(x,y,ctx) {
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
-
- ctx.beginPath();
- ctx.fillStyle = "#000000";
- ctx.strokeStyle = "#000000";
- ctx.lineWidth = "1";
- ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
- ctx.stroke();
- ctx.fill();
-
- ctx.restore();
-
- this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_NOR = new ElementCatalog_Element("NOR","The NOR gate outputs a high only when all inputs are low.","!|",LogicNOR,[2]);
-ElementReferenceTable.push(ElementCatalog_NOR);
-ElementCategory_LOGIC.addElement(ElementCatalog_NOR);
-
-class LogicXOR extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
- this.Name = "XOR";
- this.removeProperty("Inputs");
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- let ORResult = false;
- if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
- return ORResult;
- }
-
- drawElement(x,y,ctx) {
- let drawWidth = this.Width;
- let drawHeight = this.Height;
-
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
-
- ctx.lineWidth = "2";
- ctx.beginPath();
- ctx.shadowColor = "transparent";
- ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
- ctx.stroke();
-
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.stroke();
- ctx.restore();
-
- this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_XOR = new ElementCatalog_Element("XOR","The XOR gate outputs a high when only 1 input is high.","^",LogicXOR,[]);
-ElementReferenceTable.push(ElementCatalog_XOR);
-ElementCategory_LOGIC.addElement(ElementCatalog_XOR);
-
-class LogicXNOR extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
- this.Name = "XNOR";
- this.removeProperty("Inputs");
- this.Width += 10;
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- let ORResult = false;
- if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
- return !ORResult;
- }
-
- drawElement(x,y,ctx) {
- let drawWidth = this.Width;
- let drawHeight = this.Height;
-
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y);
- ctx.lineTo((x+xOffset)+ this.Width/4,y);
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
- ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
- ctx.lineTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
-
- ctx.beginPath();
- ctx.fillStyle = "#000000";
- ctx.strokeStyle = "#000000";
- ctx.lineWidth = "1";
- ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
- ctx.stroke();
- ctx.fill();
-
- ctx.lineWidth = "2";
- ctx.beginPath();
- ctx.shadowColor = "transparent";
- ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
- ctx.stroke();
-
- ctx.beginPath();
- ctx.moveTo(x+(xOffset/4),y+this.Height);
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
- ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
- ctx.stroke();
- ctx.restore();
-
- this.drawTextCentered(ctx,x,y,this.Width-(xOffset/2),this.Height,this.Designator,"10px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-
-let ElementCatalog_XNOR = new ElementCatalog_Element("XNOR","The XNOR gate outputs a high only when both inputs are either low or high.","!^",LogicXNOR,[]);
-ElementReferenceTable.push(ElementCatalog_XNOR);
-ElementCategory_LOGIC.addElement(ElementCatalog_XNOR);
-
-class LogicNOT extends Element {
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,1); // Only 1 inputs on NOT
- this.Name = "NOT";
- this.removeProperty("Inputs");
- this.Width += 10;
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- if (this.Inputs[0]) return false;
- return true;
- }
-
- drawElement(x,y,ctx) {
- let drawWidth = this.Width;
- let drawHeight = this.Height;
-
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+xOffset,y);
- ctx.lineTo(x+(this.Width-(xOffset+10)),y+(this.Height/2));
- ctx.lineTo(x+xOffset,y+this.Height);
- ctx.lineTo(x+xOffset,y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
-
- ctx.beginPath();
- ctx.fillStyle = "#000000";
- ctx.strokeStyle = "#000000";
- ctx.lineWidth = "1";
- ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
- ctx.stroke();
- ctx.fill();
-
- ctx.restore();
- this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_NOT = new ElementCatalog_Element("NOT","The NOT gate outputs the opposite of the input.","!",LogicNOT,[]);
-ElementReferenceTable.push(ElementCatalog_NOT);
-ElementCategory_LOGIC.addElement(ElementCatalog_NOT);
-
-class LogicBuffer extends Element {
- ClockTick() {
- this.Output = this.Inputs[0];
- this.Task.Enabled = false;
- this.Task.LastCall = 0;
-
- for (let a = 0; a < this.OutputConnections.length; a++) {
- this.LogicEngine.RecursionCount = 0;
- this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
- }
- }
-
- Delete() {
- super.Delete();
- this.LogicEngine.Scheduler.deleteTask(this.Task);
- }
-
- getOutput(Output=0) {
- if (super.getOutput() === 0) return false;
- return this.Output;
- }
-
- setInput(Input, Value) {
- if (Input > 0) return;
- //super.setInput(Input, Value);
- Value = this._Container.isHigh(this,Input);
- if (Value !== false) Value = true;
- this.Inputs[Input] = Value;
- this.redraw = true;
-
- if (!this.Task.Enabled) {
- this.Task.LastCall = 0;
- this.Task.Enabled = true;
- }
- }
-
- constructor(_Container, RestoreData = null, logicengine) {
- super(_Container, RestoreData,logicengine,1);
- this.removeProperty("Inputs");
- this.Output = false;
- this.Name = "Buffer";
- this.Width = 100;
- this.Task = new Task("BufferTask","Buffer",0,9999999,this.ClockTick.bind(this));
- this.Task.Time = 4;
- this.Task.Enabled = false;
- this.Task.LastCall = 0;
- this.removeProperty("Inputs");
- if (RestoreData) this.Output = RestoreData.Output;
- this.LogicEngine.Scheduler.addTask(this.Task);
- }
-
- toJSON(key) {
- let superjson = super.toJSON(key);
- superjson.Task = {
- Enabled: this.Task.Enabled,
- Time: this.Task.Time,
- LastCall: Date.now() - this.Task.LastCall,
- CallCount: this.Task.CallCount
- };
- return superjson;
- }
-
-
- drawElement(x, y, ctx) {
- let drawWidth = this.Width;
- let drawHeight = this.Height;
-
- let xOffset = 20;
- let shadowColor = this.LogicEngine.Settings.ShadowColor;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x+xOffset,y);
- ctx.lineTo(x+(this.Width-(xOffset)),y+(this.Height/2));
- ctx.lineTo(x+xOffset,y+this.Height);
- ctx.lineTo(x+xOffset,y);
- ctx.lineWidth = "3";
- ctx.fillStyle = "#f7e979";
- ctx.shadowColor = shadowColor;
- ctx.shadowBlur = "6";
- ctx.shadowOffsetX = 2;
- ctx.shadowOffsetY = 2;
- ctx.stroke();
- ctx.fill();
- ctx.restore();
- this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
- this.drawInputs(ctx,x,y);
- this.drawOutputs(ctx,x,y);
- }
-}
-let ElementCatalog_BUFFER = new ElementCatalog_Element("Buffer","The buffer is a special gate to allow the prevention of recursion loops.","|>",LogicBuffer,[]);
-ElementReferenceTable.push(ElementCatalog_BUFFER);
-ElementCategory_LOGIC.addElement(ElementCatalog_BUFFER);
-
diff --git a/js/elements/BaseElementClasses.js b/js/elements/BaseElementClasses.js
new file mode 100644
index 0000000..d3e704a
--- /dev/null
+++ b/js/elements/BaseElementClasses.js
@@ -0,0 +1,499 @@
+let ElementReferenceTable = new Array();
+
+let ElementCategory_Inputs = new ElementCatalog_Category("Inputs","");
+let ElementCategory_Outputs = new ElementCatalog_Category("Outputs","");
+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 ElementCategory_Other = new ElementCatalog_Category("Other" ,"");
+let elementCatalog = new ElementCatalog([ElementCategory_Inputs,
+ ElementCategory_Outputs,
+ ElementCategory_LOGIC,
+ ElementCategory_FlipFlop,
+ ElementCategory_Timing,
+ ElementCategory_ICs,
+ ElementCategory_Other]);
+
+class ElementProperty {
+ constructor(name,type,callback,defaultValue,currentValue = false,values=false,min=0,max=64) {
+ /*
+ Types
+ ---------------------------------------
+ bool Boolean Values
+ int Integer Value
+ string String Value
+ list Dropdown box of values
+
+ Callback is an object of:
+ ---------------------------------------
+ CBObject Object to call function on
+ CBFunction The function
+
+ */
+ this.Name = name;
+ this.Type = type;
+ this.Callback = callback;
+ this.DefaultValue = defaultValue;
+ if (!currentValue) currentValue = defaultValue;
+ this.CurrentValue = currentValue;
+ this.Values = values;
+ if (!values) this.Values = new Array();
+ this.Minimium = min;
+ this.Maximium = max;
+ }
+
+ toJSON(key) {
+ return {
+ Name: this.Name,
+ DefaultValue: this.DefaultValue,
+ CurrentValue: this.CurrentValue,
+ Values: this.Values,
+ Minimium: this.Minimium,
+ Maximium: this.Maximium
+ };
+ }
+
+ Call(value) {
+ this.Callback.CBObject[this.Callback.CBFunction](value);
+ }
+}
+
+class ElementConnection {
+ constructor(elementContainer,element,input,output = 0) {
+ this.Container = elementContainer;
+ this.Element = element;
+ this.Input = input;
+ this.Output = output;
+ }
+ toJSON(key) {
+ return {
+ Element: this.Element.Designator,
+ Input: parseInt(this.Input),
+ Output: this.Output
+ };
+ }
+}
+
+class Element extends CanvasTools {
+
+ constructor(_Container,RestoreData = null,logicengine,Inputs) {
+ super();
+ this.Name = "Element";
+ this.Designator = "";
+ this.Inputs = new Array(Inputs);
+ this.InputLabels = new Array(1);
+ this.Width = 100;
+ this.Height = 60;
+ this.inputCircleRadius = 10;
+ this.outputCircleRadius = 10;
+ this.X = 0;
+ this.Y = 0;
+ this.OutputConnections = new Array();
+ this.MouseOver = false;
+ this.MousePosition = {x: 0, y: 0};
+ this.Properties = new Array();
+ this.LogicEngine = logicengine;
+ this.Outputs = new Array(1);
+ this.OutputLabels = new Array(1);
+ this.NoOutput = false;
+ this.OutputLink = 0;
+ this._Container = _Container;
+ this.Disconnecting = false;
+ this.redraw = true;
+ this.StaticCanvas = document.createElement("canvas");
+ this.StaticCtx = this.StaticCanvas.getContext('2d');
+
+ let inputProperty = new ElementProperty("Inputs","int",{CBObject: this,CBFunction: "ChangeInputs"},2,Inputs,false,2);
+ this.Properties.push(inputProperty);
+ if (RestoreData) {
+ this.Designator = RestoreData.Designator;
+ this.Width = RestoreData.Width;
+ this.Height = RestoreData.Height;
+ this.X = RestoreData.X;
+ this.Y = RestoreData.Y;
+ if (RestoreData.Properties.length > 0) {
+ if (RestoreData.Properties[0].Name == "Inputs") {
+ this.ChangeInputs(RestoreData.Properties[0].CurrentValue);
+ this.Properties[0].DefaultValue = RestoreData.Properties[0].DefaultValue;
+ this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
+ this.Properties[0].Values = RestoreData.Properties[0].Values;
+ this.Properties[0].Minimium = RestoreData.Properties[0].Minimium;
+ this.Properties[0].Maximium = RestoreData.Properties[0].Maximium;
+ }
+ }
+ this.Inputs = RestoreData.Inputs;
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ clearStatic() {
+ this.StaticCtx.clearRect(0, 0, this.StaticCanvas.width,this.StaticCanvas.height);
+ }
+
+ ConnectsTo(element,input = false) {
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ if (this.OutputConnections[a].Element == element || this.OutputConnections[a].Element.Designator == element) {
+ if (input !== false) {
+ if (this.OutputConnections[a].Input == input) return this.OutputConnections[a].Output;
+ } else {
+ return 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ isVisible() {
+ let isvisible = false;
+ if (this.LogicEngine) {
+ let LeftX = this.LogicEngine.Panning.OffsetX;
+ if (LeftX < 0) {
+ LeftX = Math.abs(LeftX);
+ } else {
+ LeftX = -Math.abs(LeftX);
+ }
+ let RightX = LeftX + this.LogicEngine.Canvas.width;
+ let TopY = this.LogicEngine.Panning.OffsetY;
+ if (TopY < 0) {
+ TopY = Math.abs(TopY);
+ } else {
+ TopY = -Math.abs(TopY);
+ }
+ let BottomY = TopY + this.LogicEngine.Canvas.height;
+ if (((this.X + this.Width) >= LeftX) && ((this.X) <= RightX) && ((this.Y + this.Height) >= TopY) && ((this.Y) <= BottomY)) isvisible = true;
+ }
+ return isvisible;
+
+ }
+
+ toJSON(key) {
+ return {
+ Name: this.Name,
+ Designator: this.Designator,
+ Inputs: this.Inputs,
+ Outputs: this.OutputConnections,
+ Output: this.getOutput(),
+ Width: this.Width,
+ Height: this.Height,
+ X: this.X,
+ Y: this.Y,
+ Properties: this.Properties
+ };
+ }
+
+ getProperty(property) {
+ for (let a = 0; a < this.Properties.length;a++) {
+ if (this.Properties[a].Name == property) return this.Properties[a];
+ }
+ return false;
+ }
+
+ removeProperty(property) {
+ for (let a = 0; a < this.Properties.length;a++) {
+ if (this.Properties[a].Name == property) {
+ this.Properties.splice(a,1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ totalInputs() {
+ return this.Inputs.length;
+ }
+
+ ChangeInputs(inputs) {
+ inputs = parseInt(inputs,10);
+ this.Inputs = new Array(inputs);
+ this.getProperty("Inputs").CurrentValue = inputs;
+ this.Height = inputs*25;
+ if (this.Height < 60) this.Height = 60;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ Delete() {
+ // if we are active linking stop doing so
+ if (this.LogicEngine.ActiveLink == this) this.LogicEngine.ActiveLink = false;
+ // Just to clean up connections
+ this.Disconnect();
+ }
+
+ Disconnect(element = false) {
+ if (element) {
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ if (this.OutputConnections[a].Element == element) {
+ this.LogicEngine.RecursionCount = 0;
+ let element = this.OutputConnections[a].Element;
+ let input = this.OutputConnections[a].Input;
+ this.OutputConnections.splice(a,1);
+ element.setInput(input, false);
+ a--;
+ }
+ }
+ } else {
+ this.Disconnecting = true;
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, false);
+ this.OutputConnections.splice(a,1);
+ a--;
+ }
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ MouseDown(mousePos) {
+ return;
+ }
+
+ MouseUp(mousePos) {
+ return;
+ }
+
+ LinkOutLocation() {
+ return this.OutputLink;
+ }
+
+ MouseClick(mousePos) {
+
+ let ctxMousePos = this.MousePosition;
+ let mouseDistOutput = length2D(this.X+(this.Width-10),
+ this.Y+(this.Height/2),
+ ctxMousePos.x,
+ ctxMousePos.y);
+ if (this.LogicEngine.ActiveLink) {
+ // We need to see if an input is being clicked on to be linked to
+ let foundInput = false;
+ for (let a = 0; a < this.Inputs.length;a++) {
+ let centerY = this.Y + Math.round(this.Height / 2);
+ let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
+ let firstY = (centerY - (totalHeight/2)) + 12;
+
+ let mouseDist = length2D(this.X+10,
+ firstY+ (a*24),
+ ctxMousePos.x,
+ ctxMousePos.y);
+ if (mouseDist <= (this.inputCircleRadius)) {
+ this.LogicEngine.Link(a);
+ foundInput = true;
+ break;
+ }
+ }
+
+ } else {
+ let foundOutput = false;
+ for (let a = 0; a < this.Outputs.length;a++) {
+ let centerY = this.Y + Math.round(this.Height / 2);
+ let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4);
+ let firstY = (centerY - (totalHeight/2)) + 12;
+
+ let mouseDist = length2D(this.X+(this.Width-10),
+ firstY+ (a*24),
+ ctxMousePos.x,
+ ctxMousePos.y);
+ if (mouseDist <= (this.outputCircleRadius)) {
+ this.OutputLink = {Output: a,x: this.X+(this.Width-10), y: firstY+ (a*24)};
+ this.LogicEngine.Link();
+ foundOutput = true;
+ break;
+ }
+ }
+ }
+ }
+
+ mouseInside(mousePos) {
+ mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
+ let oldMouseOver = this.MouseOver;
+ this.MouseOver = false;
+ if (((mousePos.x >= this.X ) && (mousePos.x <= (this.X + this.Width))) && ((mousePos.y >= this.Y ) && (mousePos.y <= (this.Y + this.Height)))) this.MouseOver = true;
+ this.MousePosition = mousePos;
+ if (this.MouseOver) this.drawInputs(this.StaticCtx, 0,0);
+ if (this.MouseOver) this.drawOutputs(this.StaticCtx, 0,0);
+ if (oldMouseOver && !this.MouseOver) this.drawInputs(this.StaticCtx, 0,0); // Make sure we clear hover
+ if (oldMouseOver && !this.MouseOver) this.drawOutputs(this.StaticCtx, 0,0);
+ return this.MouseOver;
+ }
+
+ addConnection(container, element, input,output=0) {
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ if (this.OutputConnections[a].Element == element && this.OutputConnections[a].Input == input) {
+ // Already existing link, we will remove it instead
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections.splice(a,1);
+ element.setInput(input,false);
+ this.drawElement(0,0,this.StaticCtx);
+ return;
+ }
+ }
+ let newConnection = new ElementConnection(container,element,input,output);
+ this.OutputConnections.push(newConnection);
+ this.LogicEngine.RecursionCount = 0;
+ element.setInput(input,this.getOutput(output));
+ this.drawElement(0,0,this.StaticCtx);
+ return newConnection;
+ }
+
+ drawInputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff",drawFresh = false) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ ctx.save();
+ //this.inputCircleRadius = 10;
+ let centerY = y + Math.round(this.Height / 2);
+ let totalHeight = this.totalInputs() * ((this.inputCircleRadius*2)+4);
+ let firstY = (centerY - (totalHeight/2)) + 12;
+ let centerYReal = this.Y + Math.round(this.Height / 2);
+ let firstYReal = (centerYReal - (totalHeight/2)) + 12;
+
+ for (let a = 0; a < this.totalInputs();a++) {
+ let mouseDist = length2D(this.X+10, firstYReal + (a*24),this.MousePosition.x,this.MousePosition.y);
+ ctx.beginPath();
+ ctx.arc(x+10,firstY + (a*24),this.inputCircleRadius,0,2*Math.PI);
+ ctx.strokeStyle = borderColor;
+ ctx.fillStyle = circleColorFalse;
+ if (this.Inputs[a]) ctx.fillStyle = circleColorTrue;
+ if ((mouseDist <= (this.inputCircleRadius)) && this.LogicEngine.ActiveLink) ctx.fillStyle = circleColorHover;
+ ctx.fill();
+ ctx.stroke();
+ if (this.InputLabels[a] && drawFresh) 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",drawFresh = false) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ 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;
+ let centerYReal = this.Y + Math.round(this.Height / 2);
+ let firstYReal = (centerYReal - (totalHeight/2)) + 12;
+
+ for (let a = 0; a < this.Outputs.length;a++) {
+ let mouseDist = length2D(this.X+(this.Width - 10), firstYReal + (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] && drawFresh) 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) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ let centerY = this.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.OutputConnections.length;a++) {
+ let mouseDist = length2D(this.X+(this.Width - 10), firstY + (this.OutputConnections[a].Output*24),this.MousePosition.x,this.MousePosition.y);
+ if (!this.OutputConnections[a].Container.HasElement(this.OutputConnections[a].Element)) {
+ // This is a ghosted connection, lets get rid of it
+ this.OutputConnections.splice(a,1);
+ a--;
+ } else {
+ let endCenterY = this.OutputConnections[a].Element.Y + Math.round(this.OutputConnections[a].Element.Height / 2);
+ let endTotalHeight = this.OutputConnections[a].Element.totalInputs() * ((this.OutputConnections[a].Element.inputCircleRadius*2)+4);
+ let endFirstY = (endCenterY - (endTotalHeight/2)) + 12;
+
+ let startX = this.X + this.Width;
+ let startY = firstY+ (this.OutputConnections[a].Output*24);
+ let endX = this.OutputConnections[a].Element.X;
+ //let endY = this.OutputConnections[a].Element.Y+(this.OutputConnections[a].Element.inputCircleRadius + 2)+(((this.OutputConnections[a].Input*(4+(this.OutputConnections[a].Element.inputCircleRadius*2))))-2)+(this.OutputConnections[a].Element.inputCircleRadius/2);
+ let endY = endFirstY + (this.OutputConnections[a].Input*24);
+ let startMidX = startX + ((endX - startX)/2);
+ let startMidY = startY;
+ let midX = startMidX;
+ let midY = startY + ((endY - startY)/2);
+ let endMidX = startMidX;
+ let endMidY = endY;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.lineWidth = settings.LinkWidth;
+ if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.lineWidth = (settings.LinkWidth * 2);
+ ctx.setLineDash(settings.LinkDash);
+ ctx.moveTo(startX, startY);
+ //ctx.lineTo(endX, endY);
+ ctx.quadraticCurveTo(startMidX,startMidY,midX,midY);
+ ctx.quadraticCurveTo(endMidX,endMidY,endX,endY);
+ ctx.strokeStyle = settings.ActiveConnectionColor;
+ if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.ActiveConnectionHoverColor;
+ if (!this.getOutput(this.OutputConnections[a].Output)) {
+ ctx.strokeStyle = settings.InactiveConnectionColor;
+ if ((mouseDist <= (this.outputCircleRadius)) && !this.LogicEngine.ActiveLink) ctx.strokeStyle = settings.InactiveConnectionHoverColor;
+ }
+
+ ctx.stroke();
+ ctx.restore();
+
+ }
+ }
+ }
+
+ setConnections() {
+ for (let a = 0; a < this.OutputConnections.length;a++) {
+ 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) {
+ if (Value) {
+ Value = true;
+ } else {
+ Value = false;
+ }
+ let oldInput = this.Inputs[Input];
+ if (Input < this.totalInputs()) {
+ this.Inputs[Input] = Value;
+ } else {
+ return;
+ }
+ let isHigh = this._Container.isHigh(this,Input);
+ if (isHigh !== false) this.Inputs[Input] = true;
+ if (isHigh === false) this.Inputs[Input] = false;
+ if (oldInput != this.Inputs[Input]) {
+ this.setConnections();
+ this.drawElement(0,0,this.StaticCtx);
+ }
+ }
+
+ getOutput(Output=0) {
+ /*
+ Should return true or false
+ */
+ if (this.Disconnecting) return 0;
+ return false;
+ }
+
+ drawElement(x,y,ctx) {
+ /*
+ Draw routine for the element
+ */
+
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx,x+10,y,this.Width-20,this.Height);
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,"LOGIC");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
diff --git a/js/elements/BasicElements.js b/js/elements/BasicElements.js
new file mode 100644
index 0000000..2fba696
--- /dev/null
+++ b/js/elements/BasicElements.js
@@ -0,0 +1,522 @@
+class LogicAND extends Element {
+ constructor(_Container, RestoreData = null, logicengine,Inputs) {
+ super(_Container, RestoreData,logicengine,Inputs);
+ this.Name = "AND";
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ let ANDResult = true;
+ for (let a = 0; a < this.totalInputs();a++) {
+ if (!this.Inputs[a]) ANDResult = false;
+ }
+ return ANDResult;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ //this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height);
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+xOffset,y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - xOffset),y,x+(this.Width-xOffset),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - xOffset),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+xOffset,y+this.Height);
+ ctx.lineTo(x+xOffset,y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+ ctx.restore();
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_AND = new ElementCatalog_Element("AND","The AND gate only outputs high when all of the inputs are high.","&",LogicAND,[2]);
+ElementReferenceTable.push(ElementCatalog_AND);
+ElementCategory_LOGIC.addElement(ElementCatalog_AND);
+
+class LogicNAND extends LogicAND {
+ constructor(_Container, RestoreData = null, logicengine,Inputs) {
+ super(_Container, RestoreData,logicengine,Inputs);
+ this.Name = "NAND";
+ this.Width = this.Width + 10;
+ }
+
+ getOutput(Output=0) {
+ if (this.Disconnecting) return false;
+ if (super.getOutput()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+xOffset,y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*1.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+xOffset,y+this.Height);
+ ctx.lineTo(x+xOffset,y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.strokeStyle = "#000000";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = "#000000";
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = "1";
+ ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.restore();
+ this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_NAND = new ElementCatalog_Element("NAND","The NAND gate always outputs a high signal unless all the inputs are high then it goes low.","!&",LogicNAND,[2]);
+ElementReferenceTable.push(ElementCatalog_NAND);
+ElementCategory_LOGIC.addElement(ElementCatalog_NAND);
+
+class LogicOR extends Element {
+ constructor(_Container, RestoreData = null, logicengine,Inputs) {
+ super(_Container, RestoreData,logicengine,Inputs);
+ this.Name = "OR";
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ let ORResult = false;
+ for (let a = 0; a < this.totalInputs();a++) {
+ if (this.Inputs[a]) ORResult = true;
+ }
+ return ORResult;
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let drawWidth = this.Width;
+ let drawHeight = this.Height;
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+ ctx.restore();
+
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_OR = new ElementCatalog_Element("OR","The OR gate outputs a high when any input is HIGH.","|",LogicOR,[2]);
+ElementReferenceTable.push(ElementCatalog_OR);
+ElementCategory_LOGIC.addElement(ElementCatalog_OR);
+
+class LogicNOR extends LogicOR {
+ constructor(_Container, RestoreData = null, logicengine,Inputs) {
+ super(_Container, RestoreData,logicengine,Inputs);
+ this.Name = "NOR";
+ this.Width = this.Width + 10;
+ }
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ if (super.getOutput()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = "#000000";
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = "1";
+ ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.restore();
+
+ this.drawTextCentered(ctx,x,y,this.Width-xOffset,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_NOR = new ElementCatalog_Element("NOR","The NOR gate outputs a high only when all inputs are low.","!|",LogicNOR,[2]);
+ElementReferenceTable.push(ElementCatalog_NOR);
+ElementCategory_LOGIC.addElement(ElementCatalog_NOR);
+
+class LogicXOR extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
+ this.Name = "XOR";
+ this.removeProperty("Inputs");
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ let ORResult = false;
+ if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
+ return ORResult;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let drawWidth = this.Width;
+ let drawHeight = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y,x+(this.Width-xOffset),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.lineWidth = "2";
+ ctx.beginPath();
+ ctx.shadowColor = "transparent";
+ ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.stroke();
+ ctx.restore();
+
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_XOR = new ElementCatalog_Element("XOR","The XOR gate outputs a high when only 1 input is high.","^",LogicXOR,[]);
+ElementReferenceTable.push(ElementCatalog_XOR);
+ElementCategory_LOGIC.addElement(ElementCatalog_XOR);
+
+class LogicXNOR extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,2); // Only 2 inputs on XOR
+ this.Name = "XNOR";
+ this.removeProperty("Inputs");
+ this.Width += 10;
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ let ORResult = false;
+ if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true;
+ return !ORResult;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let drawWidth = this.Width;
+ let drawHeight = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y);
+ ctx.lineTo((x+xOffset)+ this.Width/4,y);
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y,x+(this.Width-(xOffset*1.5)),y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(this.Width - (xOffset*2.5)),y+this.Height,(x+xOffset)+ this.Width/4,y+this.Height);
+ ctx.lineTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = "#000000";
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = "1";
+ ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.lineWidth = "2";
+ ctx.beginPath();
+ ctx.shadowColor = "transparent";
+ ctx.moveTo(x+(xOffset/4)+10,y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height - (this.Height/32)),x+xOffset+10,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+10),y+(this.Height/32),x+(xOffset/4)+10,y);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(x+(xOffset/4),y+this.Height);
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height - (this.Height/32)),x+xOffset+5,y+(this.Height/2));
+ ctx.quadraticCurveTo(x+(xOffset+5),y+(this.Height/32),x+(xOffset/4),y);
+ ctx.stroke();
+ ctx.restore();
+
+ this.drawTextCentered(ctx,x,y,this.Width-(xOffset/2),this.Height,this.Designator,"10px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+
+let ElementCatalog_XNOR = new ElementCatalog_Element("XNOR","The XNOR gate outputs a high only when both inputs are either low or high.","!^",LogicXNOR,[]);
+ElementReferenceTable.push(ElementCatalog_XNOR);
+ElementCategory_LOGIC.addElement(ElementCatalog_XNOR);
+
+class LogicNOT extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,1); // Only 1 inputs on NOT
+ this.Name = "NOT";
+ this.removeProperty("Inputs");
+ this.Width += 10;
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ if (this.Inputs[0]) return false;
+ return true;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let drawWidth = this.Width;
+ let drawHeight = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+xOffset,y);
+ ctx.lineTo(x+(this.Width-(xOffset+10)),y+(this.Height/2));
+ ctx.lineTo(x+xOffset,y+this.Height);
+ ctx.lineTo(x+xOffset,y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = "#000000";
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = "1";
+ ctx.arc(x + (this.Width - 25),y + (this.Height/2),5,0,2*Math.PI);
+ ctx.stroke();
+ ctx.fill();
+
+ ctx.restore();
+ this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_NOT = new ElementCatalog_Element("NOT","The NOT gate outputs the opposite of the input.","!",LogicNOT,[]);
+ElementReferenceTable.push(ElementCatalog_NOT);
+ElementCategory_LOGIC.addElement(ElementCatalog_NOT);
+
+class LogicBuffer extends Element {
+ ClockTick() {
+ this.Output = this.Inputs[0];
+ this.Task.Enabled = false;
+ this.Task.LastCall = 0;
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ setInput(Input, Value) {
+ if (Input > 0) return;
+ //super.setInput(Input, Value);
+ Value = this._Container.isHigh(this,Input);
+ if (Value !== false) Value = true;
+ this.Inputs[Input] = Value;
+ this.redraw = true;
+
+ if (!this.Task.Enabled) {
+ this.Task.LastCall = 0;
+ this.Task.Enabled = true;
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,1);
+ this.removeProperty("Inputs");
+ this.Output = false;
+ this.Name = "Buffer";
+ this.Width = 100;
+ this.Task = new Task("BufferTask","Buffer",0,9999999,this.ClockTick.bind(this));
+ this.Task.Time = 4;
+ this.Task.Enabled = false;
+ this.Task.LastCall = 0;
+ this.removeProperty("Inputs");
+ if (RestoreData) this.Output = RestoreData.Output;
+ this.LogicEngine.Scheduler.addTask(this.Task);
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ let drawWidth = this.Width;
+ let drawHeight = this.Height;
+
+ let xOffset = 20;
+ let shadowColor = this.LogicEngine.Settings.ShadowColor;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x+xOffset,y);
+ ctx.lineTo(x+(this.Width-(xOffset)),y+(this.Height/2));
+ ctx.lineTo(x+xOffset,y+this.Height);
+ ctx.lineTo(x+xOffset,y);
+ ctx.lineWidth = "3";
+ ctx.fillStyle = "#f7e979";
+ ctx.shadowColor = shadowColor;
+ ctx.shadowBlur = "6";
+ ctx.shadowOffsetX = 2;
+ ctx.shadowOffsetY = 2;
+ ctx.stroke();
+ ctx.fill();
+ ctx.restore();
+ this.drawTextCentered(ctx,x,y,this.Width-(xOffset),this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_BUFFER = new ElementCatalog_Element("Buffer","The buffer is a special gate to allow the prevention of recursion loops.","|>",LogicBuffer,[]);
+ElementReferenceTable.push(ElementCatalog_BUFFER);
+ElementCategory_LOGIC.addElement(ElementCatalog_BUFFER);
diff --git a/js/elements/DisplayElements.js b/js/elements/DisplayElements.js
new file mode 100644
index 0000000..36ba38e
--- /dev/null
+++ b/js/elements/DisplayElements.js
@@ -0,0 +1,172 @@
+class LabelElement extends Element {
+ constructor(_Container, RestoreData = null,logicengine) {
+ super(_Container, RestoreData,logicengine,0);
+ this.removeProperty("Inputs");
+ this.Name = "Label";
+ this.Font = "48px Console";
+ this.FontStyle = "black";
+ this.Label = this.Name;
+ this.NoOutput = true;
+ let LabelProperty = new ElementProperty("Label","string",{CBObject: this,CBFunction: "setLabel"},"Label","Label",false,1,255);
+ this.Properties.push(LabelProperty);
+ let LabelSize = new ElementProperty("Size","int",{CBObject: this,CBFunction: "setSize"},"48","48",false,6,99999);
+ this.Properties.push(LabelSize);
+ let LabelColor = new ElementProperty("Color","color",{CBObject: this,CBFunction: "setColor"},"#000000","#000000",false,0,0);
+ this.Properties.push(LabelColor);
+
+ if (RestoreData) {
+ this.Label = RestoreData.Properties[0].CurrentValue;
+ this.Properties[0].CurrentValue = this.Label;
+ this.Font = RestoreData.Properties[1].CurrentValue + "px Console";
+ this.Properties[1].CurrentValue = RestoreData.Properties[1].CurrentValue;
+ this.FontStyle = RestoreData.Properties[2].CurrentValue;
+ this.Properties[2].CurrentValue = this.FontStyle;
+ }
+
+ let textWidth = this.textSize(logicengine.Ctx,this.Label,this.Font);
+ this.Width = textWidth.width;
+ this.Height = textWidth.height;
+
+ }
+ setLabel(label) {
+ this.Label = label;
+ this.Properties[0].CurrentValue = label;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+ setSize(size) {
+ this.Font = size + "px Console";
+ this.Properties[1].CurrentValue = size;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ setColor(color) {
+ this.FontStyle = color;
+ this.Properties[2].CurrentValue = color;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
+ // No outputs to draw
+ }
+
+ drawInputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
+ // No outputs to draw
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ let textWidth = this.textSize(ctx,this.Label,this.Font);
+ this.Width = textWidth.width;
+ this.Height = textWidth.height;
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawTextCentered(ctx,x,y,textWidth.width,textWidth.height,this.Label,this.Font,this.FontStyle);
+ }
+}
+let ElementCatalog_Label = new ElementCatalog_Element("Label","Allows you to place a label","🏷",LabelElement,[]);
+ElementReferenceTable.push(ElementCatalog_Label);
+ElementCategory_Other.addElement(ElementCatalog_Label);
+
+class output4bitDisplay extends Element {
+ constructor(_Container, RestoreData = null,logicengine) {
+ super(_Container, RestoreData,logicengine,4);
+ this.Name = "4B Display";
+ this.Output = false;
+ this.OutputChar = "0";
+ this.NoOutput = true;
+ this.Width = 100;
+ this.Height = 100;
+ this.Inputs = [0,0,0,0];
+ this.InputLabels = ["I0","I1","I2","I3"];
+ this.Type = 0;
+ this.removeProperty("Inputs");
+ let typeProperty = new ElementProperty("Type","list",{CBObject: this,CBFunction: "setType"},0,0,[{Value: "0", String: "Hex"},{Value: "1", String: "Decimal"},{Value: "2", String: "BCD"}],0,0);
+ this.Properties.push(typeProperty);
+
+ if (RestoreData) {
+ if (RestoreData.Properties.length > 0) {
+ this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
+ this.Properties[0].Values = RestoreData.Properties[0].Values;
+ this.setType(this.Properties[0].CurrentValue);
+ }
+ }
+ }
+
+ setType(type) {
+ this.Properties[0].CurrentValue = type;
+ switch (type) {
+ case "0":
+ this.Type = 0;
+ break;
+ case "1":
+ this.Type = 1;
+ break;
+ case "2":
+ this.Type = 2;
+ break;
+ }
+ this.setInput(0,this.Inputs[0]); // Trigger a proper recalc of the display.
+ }
+
+ setInput(Input, Value) {
+ Value = this._Container.isHigh(this,Input);
+ this.Inputs[Input] = (Value !== false) ? 1 : 0;
+ let outchar = (this.Inputs[3] << 3) + (this.Inputs[2] << 2) + (this.Inputs[1] << 1) + (this.Inputs[0]);
+
+ this.OutputChar = (outchar);
+ switch (outchar) {
+ case 10:
+ if (this.Type == 0) this.OutputChar = 'A';
+ if (this.Type == 1) this.OutputChar = '10';
+ if (this.Type == 2) this.OutputChar = '0';
+ break;
+ case 11:
+ if (this.Type == 0) this.OutputChar = 'B';
+ if (this.Type == 1) this.OutputChar = '11';
+ if (this.Type == 2) this.OutputChar = '1';
+ break;
+ case 12:
+ if (this.Type == 0) this.OutputChar = 'C';
+ if (this.Type == 1) this.OutputChar = '12';
+ if (this.Type == 2) this.OutputChar = '2';
+ break;
+ case 13:
+ if (this.Type == 0) this.OutputChar = 'D';
+ if (this.Type == 1) this.OutputChar = '13';
+ if (this.Type == 2) this.OutputChar = '3';
+ break;
+ case 14:
+ if (this.Type == 0) this.OutputChar = 'E';
+ if (this.Type == 1) this.OutputChar = '14';
+ if (this.Type == 2) this.OutputChar = '4';
+ break;
+ case 15:
+ if (this.Type == 0) this.OutputChar = 'F';
+ if (this.Type == 1) this.OutputChar = '15';
+ if (this.Type == 2) this.OutputChar = '5';
+ break;
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
+ // Do nothing, we want to overide
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x+20,y,this.Width-40,this.Height);
+ let fontStyle = "72px Console";
+ if (this.Type == 1) fontStyle = "36px Console";
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.OutputChar,fontStyle);
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+
+let ElementCatalog_Output_4BDisplay = new ElementCatalog_Element("4B Display","A display! Takes a 4 bit input and shows hex value of the input.","[8]",output4bitDisplay,[]);
+ElementReferenceTable.push(ElementCatalog_Output_4BDisplay);
+ElementCategory_Outputs.addElement(ElementCatalog_Output_4BDisplay);
diff --git a/js/elements/FFElements.js b/js/elements/FFElements.js
new file mode 100644
index 0000000..8054b56
--- /dev/null
+++ b/js/elements/FFElements.js
@@ -0,0 +1,261 @@
+class FlipFlopJK extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, 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.OutputStates;
+ }
+ }
+
+ toJSON(key) {
+ let $superjson = super.toJSON(key);
+
+ $superjson.OutputStates = this.Outputs;
+ return $superjson;
+ }
+
+ setInput(Input, Value) {
+ if (Input >= this.Inputs.length) return false;
+ Value = this._Container.isHigh(this,Input);
+ if (Value !== false) Value = true;
+ let oldOutput = this.Outputs[0];
+ let oldOutput2 = this.Outputs[1];
+ 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) || oldOutput2 != this.getOutput(1)) {
+ this.setConnections();
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Outputs[Output];
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ 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,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+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.","",FlipFlopJK,[]);
+ElementReferenceTable.push(ElementCatalog_JKFlipFlop);
+ElementCategory_FlipFlop.addElement(ElementCatalog_JKFlipFlop);
+
+class FlipFlopSR extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, 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.OutputStates;
+ }
+ }
+
+ toJSON(key) {
+ let $superjson = super.toJSON(key);
+
+ $superjson.OutputStates = this.Outputs;
+ return $superjson;
+ }
+
+ setInput(Input, Value) {
+ if (Input >= this.Inputs.length) return false;
+ Value = this._Container.isHigh(this,Input);
+ if (Value !== false) Value = true;
+ let oldOutput = this.Outputs[0];
+ let oldOutput2 = this.Outputs[1];
+ this.Inputs[Input] = Value;
+ this.redraw = true;
+
+ 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) || oldOutput2 != this.getOutput(1)) {
+ this.setConnections();
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Outputs[Output];
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ 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,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+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.","",FlipFlopSR,[]);
+ElementReferenceTable.push(ElementCatalog_SRFlipFlop);
+ElementCategory_FlipFlop.addElement(ElementCatalog_SRFlipFlop);
+
+class FlipFlopT extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, 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.OutputStates;
+ }
+ }
+
+ toJSON(key) {
+ let $superjson = super.toJSON(key);
+
+ $superjson.OutputStates = this.Outputs;
+ return $superjson;
+ }
+
+ setInput(Input, Value) {
+ if (Input >= this.Inputs.length) return false;
+ Value = this._Container.isHigh(this,Input);
+ if (Value !== false) Value = true;
+ let oldOutput = this.Outputs[0];
+ let oldOutput2 = this.Outputs[1];
+ this.Inputs[Input] = Value;
+ this.redraw = true;
+
+ if (this.Inputs[0] && this.Inputs[1]) {
+ this.Outputs[0] = !this.Outputs[0];
+ this.Outputs[1] = !this.Outputs[0];
+ }
+ if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
+ this.setConnections();
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Outputs[Output];
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ 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,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+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.","",FlipFlopT,[]);
+ElementReferenceTable.push(ElementCatalog_TFlipFlop);
+ElementCategory_FlipFlop.addElement(ElementCatalog_TFlipFlop);
+
+class FlipFlopD extends Element {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, 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.OutputStates;
+ }
+ }
+
+ toJSON(key) {
+ let $superjson = super.toJSON(key);
+
+ $superjson.OutputStates = this.Outputs;
+ return $superjson;
+ }
+
+ setInput(Input, Value) {
+ if (Input >= this.Inputs.length) return false;
+ Value = this._Container.isHigh(this,Input);
+ if (Value !== false) Value = true;
+ let oldOutput = this.Outputs[0];
+ let oldOutput2 = this.Outputs[1];
+ let oldInput = this.Inputs[1];
+ this.Inputs[Input] = Value;
+ this.redraw = true;
+
+ if (this.Inputs[1] && !oldInput) {
+ this.Outputs[0] = this.Inputs[0];
+ this.Outputs[1] = !this.Outputs[0];
+ }
+ if (oldOutput != this.getOutput(0) || oldOutput2 != this.getOutput(1)) {
+ this.setConnections();
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Outputs[Output];
+ }
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ 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,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+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","",FlipFlopD,[]);
+ElementReferenceTable.push(ElementCatalog_DFlipFlop);
+ElementCategory_FlipFlop.addElement(ElementCatalog_DFlipFlop);
diff --git a/js/elements/ICElements.js b/js/elements/ICElements.js
new file mode 100644
index 0000000..61748fe
--- /dev/null
+++ b/js/elements/ICElements.js
@@ -0,0 +1,347 @@
+class ICInput extends Element {
+ setPinName(pinname) {
+ // Dont actually need it to do anything
+ this.Properties[0].CurrentValue = pinname;
+ this.Output = false;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ constructor(_Container, RestoreData = null,logicengine) {
+ super(_Container, RestoreData ,logicengine,0);
+ this.removeProperty("Inputs");
+ this.Task = new Task("ICInputTask","CLOCK",0,1,this.ClockTick.bind(this));
+ this.Name = "ICInput";
+ this.Input = false;
+
+ let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
+ this.Properties.push(pinNameProperty);
+ this.LogicEngine.Scheduler.addTask(this.Task);
+
+ if (RestoreData) {
+ pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
+ }
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+ setInput(notused = 0,value) {
+
+ if (notused > 0) return;
+
+ if (value === false) this.Input = false;
+ if (value !== false) this.Input = true;
+ this.Inputs[0] = this.Input;
+ this.drawElement(0,0,this.StaticCtx);
+
+ if (!this.Task.Enabled) {
+ this.Task.LastCall = 0;
+ this.Task.Enabled = true;
+ }
+ }
+
+ ClockTick() {
+ this.Output = this.Input;
+ this.Task.Enabled = false;
+ this.Task.LastCall = 0;
+ this.drawElement(0,0,this.StaticCtx);
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ /*
+ Draw routine for the element
+ */
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx,x+10,y,this.Width-30,this.Height);
+ this.drawTextCentered(ctx,x,y+2,this.Width-10,14,this.Designator,"12px Console");
+ this.drawTextCentered(ctx,x,y,this.Width-10,this.Height,this.Properties[0].CurrentValue,"12px Console");
+ this.drawOutputs(ctx,x,y);
+ }
+
+}
+let ElementCatalog_ICInput = new ElementCatalog_Element("ICInput","Allows for external signal to be applied to the design when used as an IC.","-<|",ICInput,[]);
+ElementReferenceTable.push(ElementCatalog_ICInput);
+ElementCategory_ICs.addElement(ElementCatalog_ICInput);
+
+class ICOutput extends Element {
+ setPinName(pinname) {
+ // Dont actually need it to do anything
+ this.Properties[0].CurrentValue = pinname;
+ this.Output = false;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ constructor(_Container, RestoreData = null,logicengine) {
+ super(_Container, RestoreData ,logicengine,1);
+ this.removeProperty("Inputs");
+ this.Task = new Task("ICOutputTask","CLOCK",0,1,this.ClockTick.bind(this));
+ this.Name = "ICOutput";
+ this.Input = false;
+ this.NoOutput = true;
+ this.ParentIC = false;
+
+ let pinNameProperty = new ElementProperty("Pin Name","string",{CBObject: this,CBFunction: "setPinName"},"Pin","Pin",false,1,32);
+ this.Properties.push(pinNameProperty);
+ this.LogicEngine.Scheduler.addTask(this.Task);
+ if (RestoreData) {
+ pinNameProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
+ }
+
+ }
+
+ setParent(element) {
+ this.ParentIC = element;
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+ setInput(notused = 0,value) {
+ if (notused > 0) return;
+ value = this._Container.isHigh(this,0);
+ if (value === false) this.Input = false;
+ if (value !== false) this.Input = true;
+ this.Inputs[0] = this.Input;
+ this.drawElement(0,0,this.StaticCtx);
+
+ if (!this.Task.Enabled) {
+ this.Task.LastCall = 0;
+ this.Task.Enabled = true;
+ }
+ }
+
+ ClockTick() {
+ this.Output = this.Input;
+ this.Task.Enabled = false;
+ this.Task.LastCall = 0;
+ this.drawElement(0,0,this.StaticCtx);
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+
+ if (this.ParentIC) {
+ this.ParentIC.drawOutputs(this.ParentIC.StaticCtx,0,0);
+ }
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ drawConnections(ctx, settings) {
+ // Don't draw connections from IC Outputs
+ }
+
+ drawOutputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
+ // Don't draw outputs
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ /*
+ Draw routine for the element
+ */
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx,x+20,y,this.Width-10,this.Height);
+ this.drawTextCentered(ctx,x+20,y+2,this.Width-10,14,this.Designator,"12px Console");
+ this.drawTextCentered(ctx,x+20,y,this.Width-10,this.Height,this.Properties[0].CurrentValue,"12px Console");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_ICOutput = new ElementCatalog_Element("ICOutput","Allows for routing a signal externally when the design is used as an IC.","|>-",ICOutput,[]);
+ElementReferenceTable.push(ElementCatalog_ICOutput);
+ElementCategory_ICs.addElement(ElementCatalog_ICOutput);
+
+class ICElement extends Element {
+ constructor(_Container, RestoreData = null, logicengine,ICSettings = null) {
+
+
+ let newContainer = false;
+ let icContainer = false;
+ try {
+ newContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
+ icContainer = loadContainer(JSON.parse(JSON.parse(ICSettings.Container)));
+ } catch (ex) {
+ newContainer = loadContainer(JSON.parse(ICSettings.Container));
+ icContainer = loadContainer(JSON.parse(ICSettings.Container));
+ }
+ ICSettings.Inputs = new Array();
+ ICSettings.Outputs = new Array();
+
+ let inputLabels = new Array();
+ let outputLabels = new Array();
+
+ for (let a = 0; a < newContainer.Elements.length; a++) {
+ if (newContainer.Elements[a] instanceof ICInput) {
+ let cConn = new ContainerConnection(null,newContainer,null,newContainer.Elements[a],0);
+ inputLabels.push(newContainer.Elements[a]?.Properties[0]?.CurrentValue);
+ ICSettings.Inputs.push(cConn);
+ }
+ if (newContainer.Elements[a] instanceof ICOutput) {
+ let cConn = new ContainerConnection(newContainer,null,newContainer.Elements[a],null,0);
+ outputLabels.push(newContainer.Elements[a]?.Properties[0]?.CurrentValue);
+ ICSettings.Outputs.push(cConn);
+ }
+ }
+
+ let totalInputs = ICSettings.Inputs.length;
+ let totalOutputs = ICSettings.Outputs.length;
+
+ super(_Container, RestoreData, logicengine, totalInputs);
+ this.removeProperty("Inputs");
+ this.ICBlueprint = icContainer;
+ this.Outputs = ICSettings.Outputs;
+ this.InputConnections = ICSettings.Inputs;
+ this.InputLabels = inputLabels;
+ this.OutputLabels = outputLabels;
+
+ this.Container = newContainer;
+ this.Name = ICSettings.Name;
+ this.Icon = "≡[°]≡";
+ this.Width = 140;
+ this.Height = (totalInputs >= totalOutputs) ? totalInputs*25 : totalOutputs*25;
+ if (this.Height < 60) this.Height = 60;
+
+ for (let a = 0; a < this.Outputs.length; a++) {
+ this.Outputs[a].fromElement.setParent(this);
+ }
+
+ let editProperty = new ElementProperty("Edit","button",{CBObject: this,CBFunction: "editIC"},"Edit IC","Edit IC",false,0,0);
+ this.Properties.push(editProperty);
+
+ if (RestoreData) {
+ //console.log(RestoreData);
+ }
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.IsIC = true;
+ let eCat = getElementInfo(this.Name);
+ superjson.Description = eCat.Description;
+ superjson.ICOutputs = this.Outputs;
+ superjson.InputConnections = this.InputConnections;
+ superjson.ICBlueprint = this.ICBlueprint;
+ superjson.ICContainer = this.Container;
+ return superjson;
+ }
+
+ editIC(value) {
+ if (this.LogicEngine.StashedContainer) {
+ this.LogicEngine.StashedContainer.push(this.LogicEngine.ActiveContainer);
+ } else {
+ this.LogicEngine.StashedContainer = new Array();
+ this.LogicEngine.StashedContainer.push(this.LogicEngine.ActiveContainer);
+ }
+
+ this.LogicEngine.ActiveContainer = this.ICBlueprint;
+ this.LogicEngine.RedrawStatics();
+ }
+
+ Disconnect(element = false) {
+ super.Disconnect(element);
+ if (element) {
+ for (let a = 0; a < this.Outputs.length; a++) {
+ if (this.Outputs[a].toElement == element || this.Outputs[a].fromElement == element) {
+ if (this.Outputs[a].toElement == element) {
+ // It's a too element, we need to tell it we are not high anymore
+ } else {
+ // Its a from element, we need to tell it's too element
+ }
+ }
+ }
+ } else {
+ // We are disconnecting all of our own connections
+ for (let a = 0; a < this.Outputs.length; a++) {
+
+ }
+ }
+ }
+
+ addConnection(container, element, input, output = 0) {
+ super.addConnection(container, element, input, output);
+ this.Outputs[output].toElementContainer = container;
+ this.Outputs[output].toElement = element;
+ this.Outputs[output].Input = input;
+ this.Outputs[output].fromElement.addConnection(container,element,input,0);
+ }
+
+ setInput(Input, Value) {
+ if (this.InputConnections.length >= Input) {
+ // No need to worry about recursion as this goes to an input element which is buffered
+ Value = this._Container.isHigh(this,Input);
+ this.Inputs[Input] = (Value === false) ? false : true;
+ this.drawElement(0,0,this.StaticCtx);
+ this.InputConnections[Input].toElement.setInput(0,Value);
+ }
+ return false;
+ }
+
+ getOutput(Output = 0) {
+ if (super.getOutput() === 0) return false;
+ if (this.Outputs.length >= Output) {
+ return this.Outputs[Output].fromElement.getOutput(this.Outputs[Output].Input);
+ }
+ return false;
+ }
+
+ drawElement(x,y,ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ /*
+ Draw routine for the element
+ */
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.clearStatic();
+ this.drawBorderBox(ctx,x+20,y,this.Width-40,this.Height);
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Icon,"12px Console");
+ this.drawTextCentered(ctx,x,(y+this.Height)-14,this.Width,14,this.Designator,"10px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
diff --git a/js/elements/InputElements.js b/js/elements/InputElements.js
new file mode 100644
index 0000000..9727410
--- /dev/null
+++ b/js/elements/InputElements.js
@@ -0,0 +1,321 @@
+class inputElement extends Element {
+ constructor(_Container, RestoreData = null,logicengine) {
+ super(_Container, RestoreData,logicengine,0);
+ this.Name = "InputElement";
+ this.Output = false;
+ this.Width = 100;
+ this.removeProperty("Inputs");
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ drawInputs(ctx, x, y, borderColor = "#000", circleColorFalse = "#ff0000", circleColorTrue = "#00ff00", circleColorHover = "#00ffff") {
+ // There are no inputs
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x,y,this.Width,this.Height);
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,"IN");
+ this.drawOutputs(ctx,x,y);
+ }
+}
+
+
+
+class InputSwitch extends inputElement {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine);
+ this.Name = "Switch";
+ this.Height = 70;
+
+ if (RestoreData) this.Output = RestoreData.Output;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ MouseClick(mousePos) {
+ mousePos = this.LogicEngine.getCanvasMousePos(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.drawElement(0,0,this.StaticCtx);
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+ //this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
+ this.drawBorderBox(ctx,x+5,y+25,50,10,1,"#ccc","#777");
+ this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
+ if (this.getOutput()) {
+ this.drawBorderBox(ctx,x+15,y+5,30,25,1,"#ccc","#777");
+ this.drawTextCentered(ctx,x,y+this.Height - 30,this.Width-(this.outputCircleRadius*2)-20,12,"OFF","12px Console","#000");
+ } else {
+ this.drawBorderBox(ctx,x+15,y+30,30,25,1,"#ccc","#777");
+ this.drawTextCentered(ctx,x,y+7,this.Width-(this.outputCircleRadius*2)-20,12,"ON","12px Console","#000");
+ }
+ this.drawOutputs(ctx,x,y);
+ }
+}
+let ElementCatalog_SWITCH = new ElementCatalog_Element("Switch","The switch allows for toggling an output low or high.","|-",InputSwitch,[]);
+ElementReferenceTable.push(ElementCatalog_SWITCH);
+ElementCategory_Inputs.addElement(ElementCatalog_SWITCH);
+
+class InputButton extends inputElement {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine);
+ this.Name = "Button";
+ this.Height = 70;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ MouseDown(mousePos) {
+ mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
+ if ((mousePos.x >= (this.X + 5)) && (mousePos.x <= (this.X + 55)) && (mousePos.y >= (this.Y + 5)) && (mousePos.y <= (this.Y + 55))) {
+ let old_output = this.Output;
+ this.Output = true;
+ this.LogicEngine.MouseDown = false; // Prevent movement on the button when its being pushed
+ this.drawElement(0,0,this.StaticCtx);
+ if (old_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());
+ }
+ }
+ }
+ }
+
+ MouseUp(mousePos) {
+ let old_output = this.Output;
+ this.Output = false;
+ if (old_output != this.Output) {
+ this.drawElement(0,0,this.StaticCtx);
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+ }
+
+ mouseInside(mousePos) {
+ let mousestat = super.mouseInside(mousePos);
+ if (this.Output && !this.MouseOver) {
+ this.Output = false;
+ this.drawElement(0,0,this.StaticCtx);
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+ return mousestat;
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+ this.drawBorderBox(ctx,x+5,y+5,50,50,1,"#ccc","#777");
+ this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
+ this.drawOutputs(ctx,x,y);
+ }
+}
+
+let ElementCatalog_BUTTON = new ElementCatalog_Element("Button","The button only outputs high when the button is pressed.","[o]",InputButton,[]);
+ElementReferenceTable.push(ElementCatalog_BUTTON);
+ElementCategory_Inputs.addElement(ElementCatalog_BUTTON);
+
+class InputKeypad extends inputElement {
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData, logicengine);
+ this.Name = "Keypad";
+ this.Height = 210;
+ this.Width = 200;
+
+ this.Outputs = new Array(false,false,false,false,false,false,false,false);
+ this.OutputLabels = new Array("O0","O1","O2","O3","F1","F2","F3","Clr");
+ this.ButtonRows = 5;
+ this.ButtonColumns = 4;
+ this.Buttons = new Array(
+ {x: 0, y: 0,Value: 7, Text: "7"}, {x: 1, y: 0,Value: 8, Text: "8"}, {x: 2, y: 0,Value: 9, Text: "9"}, {x: 3, y: 0,Value: 16, Text: "F1"},
+ {x: 0, y: 1,Value: 4, Text: "4"}, {x: 1, y: 1,Value: 5, Text: "5"}, {x: 2, y: 1,Value: 6, Text: "6"}, {x: 3, y: 1,Value: 32, Text: "F2"},
+ {x: 0, y: 2,Value: 1, Text: "1"}, {x: 1, y: 2,Value: 2, Text: "2"}, {x: 2, y: 2,Value: 3, Text: "3"}, {x: 3, y: 2,Value: 64, Text: "F3"},
+ {x: 0, y: 3,Value: 10, Text: "A"}, {x: 1, y: 3,Value: 0, Text: "0"}, {x: 2, y: 3,Value: 11, Text: "B"}, {x: 3, y: 3,Value: 128, Text: "Clr"},
+ {x: 0, y: 1,Value: 12, Text: "C"}, {x: 1, y: 1,Value: 13, Text: "D"}, {x: 2, y: 1,Value: 14, Text: "E"}, {x: 3, y: 1,Value: 15, Text: "F"}
+ );
+ let typeProperty = new ElementProperty("Function Type","list",{CBObject: this,CBFunction: "setType"},true,true,[{Value: true, String: "Switch"},{Value: false, String: "Button"}],0,0);
+ this.Properties.push(typeProperty);
+
+ if (RestoreData) {
+ if (RestoreData.OutputValues) {
+ this.Outputs = RestoreData.OutputValues;
+ }
+ if (RestoreData.Properties?.length > 0) {
+ this.Properties[0].CurrentValue = RestoreData.Properties[0].CurrentValue;
+ }
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.OutputValues = this.Outputs;
+ return superjson;
+ }
+
+ setType(type) {
+ if (type == "false") type = false;
+ if (type == "true") type = true;
+ this.Properties[0].CurrentValue = type;
+ if (!type) {
+ this.Outputs[4] = false;
+ this.Outputs[5] = false;
+ this.Outputs[6] = false;
+ this.drawElement(0,0,this.StaticCtx);
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
+ }
+ }
+ }
+
+ MouseDown(mousePos) {
+ if (!this.Properties[0].CurrentValue) {
+ mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
+ let buttonWidth = 34;
+ let buttonHeight = 34;
+ let x = this.X;
+ let y = this.Y;
+
+ for (let pY = 0; pY < this.ButtonRows; pY++) {
+ for (let pX = 0; pX < this.ButtonColumns; pX++) {
+
+ if ((mousePos.x >= (x + (5 * (pX + 1)) + (buttonWidth * pX))) && (mousePos.x <= ((x + (5 * (pX + 1)) + (buttonWidth * pX)) + buttonWidth)) && (mousePos.y >= (y + (5 * (pY + 1)) + (buttonHeight * pY))) && (mousePos.y <= ((y + (5 * (pY + 1)) + (buttonHeight * pY)) + buttonHeight))) {
+ let button = this.Buttons[(pY * this.ButtonColumns) + pX];
+ if (button.Value & 0b0010000) this.Outputs[4] = true;
+ if (button.Value & 0b0100000) this.Outputs[5] = true;
+ if (button.Value & 0b1000000) this.Outputs[6] = true;
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
+ }
+ }
+ }
+ }
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ MouseUp(mousePos) {
+ if (!this.Properties[0].CurrentValue) {
+ this.Outputs[4] = false;
+ this.Outputs[5] = false;
+ this.Outputs[6] = false;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+ }
+
+ MouseClick(mousePos) {
+ mousePos = this.LogicEngine.getCanvasMousePos(mousePos);
+ super.MouseClick(mousePos);
+ let buttonWidth = 34;
+ let buttonHeight = 34;
+ let x = this.X;
+ let y = this.Y;
+
+ for (let pY = 0; pY < this.ButtonRows; pY++) {
+ for (let pX = 0; pX < this.ButtonColumns; pX++) {
+
+ if ((mousePos.x >= (x + (5*(pX+1))+(buttonWidth*pX))) && (mousePos.x <= ((x + (5*(pX+1))+(buttonWidth*pX)) + buttonWidth)) && (mousePos.y >= (y + (5*(pY+1))+(buttonHeight*pY))) && (mousePos.y <= ((y + (5*(pY+1))+(buttonHeight*pY))+buttonHeight))) {
+ let button = this.Buttons[(pY*this.ButtonColumns) + pX];
+
+ if (button.Value <= 15) {
+ if (button.Value & 0b0000001) this.Outputs[0] = true; else this.Outputs[0] = false;
+ if (button.Value & 0b0000010) this.Outputs[1] = true; else this.Outputs[1] = false;
+ if (button.Value & 0b0000100) this.Outputs[2] = true; else this.Outputs[2] = false;
+ if (button.Value & 0b0001000) this.Outputs[3] = true; else this.Outputs[3] = false;
+ }
+
+ if (this.Properties[0].CurrentValue) {
+ if (button.Value & 0b0010000) this.Outputs[4] = !this.Outputs[4];
+ if (button.Value & 0b0100000) this.Outputs[5] = !this.Outputs[5];
+ if (button.Value & 0b1000000) this.Outputs[6] = !this.Outputs[6];
+ }
+ if (button.Value & 0b10000000) {
+ for (let a = 0; a < this.Outputs.length; a++) {
+ this.Outputs[a] = false;
+ }
+ this.Outputs[7] = true;
+ let tempTimer = new Task("keypadClearTask","Keypad Clear",0,100,this.ClockTick.bind(this),true);
+ tempTimer.LastCall = Date.now();
+ tempTimer.Enabled = true;
+ this.LogicEngine.Scheduler.addTask(tempTimer);
+ }
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
+ }
+ }
+ }
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ ClockTick() {
+ console.log("Keypad clear signal");
+ this.Outputs[7] = false;
+ this.drawElement(0,0,this.StaticCtx);
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput(this.OutputConnections[a].Output));
+ }
+ }
+
+ getOutput(Output = 0) {
+ if (super.getOutput() === 0) return false;
+ return (this.Outputs[Output]) ? true : false;
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ let buttonWidth = 34;
+ let buttonHeight = 34;
+
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+
+ for (let pY = 0; pY < this.ButtonRows; pY++) {
+ for (let pX = 0; pX < this.ButtonColumns; pX++) {
+ this.drawBorderBox(ctx, x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, 1, "#ccc", "#777");
+ this.drawTextCentered(ctx,x + (5*(pX+1))+(buttonWidth*pX), y + (5*(pY+1))+(buttonHeight*pY), buttonWidth, buttonHeight, this.Buttons[(pY*this.ButtonColumns) + pX].Text,"16px Console", "#fff");
+
+ }
+ }
+
+ this.drawTextCentered(ctx,x,y+(this.Height-14),this.Width-(this.outputCircleRadius*2),12,this.Designator,"12px Console","#000");
+ this.drawOutputs(ctx,x,y);
+ }
+}
+
+let ElementCatalog_Keypad = new ElementCatalog_Element("Keypad","A hex entry keypad with 4 bit output","[][]",InputKeypad,[]);
+ElementReferenceTable.push(ElementCatalog_Keypad);
+ElementCategory_Inputs.addElement(ElementCatalog_Keypad);
diff --git a/js/elements/TimingElements.js b/js/elements/TimingElements.js
new file mode 100644
index 0000000..c103062
--- /dev/null
+++ b/js/elements/TimingElements.js
@@ -0,0 +1,398 @@
+class ClockElement extends Element {
+ ClockTick() {
+ if (this.Inputs[0]) {
+ this.Output = ~this.Output;
+ if (this.Output) {
+ this.Task.Time = Math.round(this.Period * this.Duty);
+ } else {
+ this.Task.Time = this.Period - Math.round(this.Period * this.Duty);
+ }
+ this.drawElement(0,0,this.StaticCtx);
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ setInput(Input, Value) {
+
+ super.setInput(Input, Value);
+
+ if (!this.Inputs[0]) {
+ this.Output = false;
+ this.Task.LastCall = 0;
+ this.Task.Enabled = false;
+ this.drawElement(0,0,this.StaticCtx);
+ for (let a = 0; a < this.OutputConnections.length;a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
+ }
+ } else {
+ this.Task.Enabled = true;
+ }
+ }
+
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,1);
+ this.removeProperty("Inputs");
+ this.Name = "Clock";
+ this.Period = 1000;
+ this.Duty = 0.5;
+ this.Output = false;
+ this.Width = 100;
+ this.Task = new Task("ClockTask","CLOCK",0,Math.round(this.Period * this.Duty),this.ClockTick.bind(this));
+ this.setInput(0,true);
+ this.removeProperty("Inputs");
+ let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},1000,false,false,4,999999);
+ let dutyProperty = new ElementProperty("Duty","int",{CBObject: this,CBFunction: "setDuty"},50,false,false,0,100);
+ if (RestoreData) {
+ if (this.Height != RestoreData.Height) {
+ // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
+ this.Height = RestoreData.Height;
+ }
+ periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
+ periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
+ periodProperty.Values = RestoreData.Properties[0].Values;
+ periodProperty.Minimium = RestoreData.Properties[0].Minimium;
+ periodProperty.Maximium = RestoreData.Properties[0].Maximium;
+ dutyProperty.DefaultValue = RestoreData.Properties[1].DefaultValue;
+ dutyProperty.CurrentValue = RestoreData.Properties[1].CurrentValue;
+ dutyProperty.Values = RestoreData.Properties[1].Values;
+ dutyProperty.Minimium = RestoreData.Properties[1].Minimium;
+ dutyProperty.Maximium = RestoreData.Properties[1].Maximium;
+
+ this.Period = periodProperty.CurrentValue;
+ this.Duty = dutyProperty.CurrentValue/100;
+ this.Task.Enabled = RestoreData.Task.Enabled;
+ this.Task.Time = RestoreData.Task.Time;
+ this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
+ this.Task.CallCount = RestoreData.Task.CallCount;
+ }
+ this.Properties.push(periodProperty);
+ this.Properties.push(dutyProperty);
+ this.LogicEngine.Scheduler.addTask(this.Task);
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+ setPeriod(period) {
+ this.Period = period;
+ this.Task.LastCall = 0;
+ this.getProperty("Period").CurrentValue = period;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ setDuty(duty) {
+ this.Duty = duty/100;
+ this.Task.LastCall = 0;
+ this.getProperty("Duty").CurrentValue = duty;
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+ this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms " + (this.Duty * 100) + "%","10px Console");
+ if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_CLOCK = new ElementCatalog_Element("Clock","The clock will output a pulse for the length of the duty cycle for a given period. I.E. if a 1000ms period with 50% duty cycle is given, the output will be high for 500ms and low for 500ms. The clock can be disabled with a low input, this also resets the clocks start time to allow for syncing.","🕑",ClockElement,[]);
+ElementReferenceTable.push(ElementCatalog_CLOCK);
+ElementCategory_Timing.addElement(ElementCatalog_CLOCK);
+
+class PulseElement extends Element {
+ ClockTick() {
+ this.Output = false;
+ this.Task.Enabled = false;
+ this.Task.LastCall = Date.now();
+ this.drawElement(0,0,this.StaticCtx);
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ setInput(Input, Value) {
+ if (Input > 0) return;
+
+ Value = this._Container.isHigh(this,Input);
+ //super.setInput(Input, Value);
+ this.Inputs[Input] = (Value === false) ? false : true;
+
+ if (this.Inputs[0] && !this.Task.Enabled) {
+ this.Output = true;
+ for (let a = 0; a < this.OutputConnections.length;a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
+ }
+ this.Task.LastCall = Date.now();
+ this.Task.Enabled = true;
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,1);
+ this.removeProperty("Inputs");
+ this.Name = "Pulse";
+ this.Period = 100;
+ this.Output = false;
+ this.Width = 100;
+ this.Task = new Task("PulseTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
+ this.removeProperty("Inputs");
+ let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
+ if (RestoreData) {
+ if (this.Height != RestoreData.Height) {
+ // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
+ this.Height = RestoreData.Height;
+ }
+ periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
+ periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
+ periodProperty.Values = RestoreData.Properties[0].Values;
+ periodProperty.Minimium = RestoreData.Properties[0].Minimium;
+ periodProperty.Maximium = RestoreData.Properties[0].Maximium;
+
+ this.Period = periodProperty.CurrentValue;
+
+ this.Task.Enabled = RestoreData.Task.Enabled;
+ this.Task.Time = RestoreData.Task.Time;
+ this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
+ this.Task.CallCount = RestoreData.Task.CallCount;
+ }
+
+
+ this.Properties.push(periodProperty);
+ this.LogicEngine.Scheduler.addTask(this.Task);
+ }
+
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+
+ setPeriod(period) {
+ this.Period = parseInt(period);
+ this.Task.Time = parseInt(period);
+ this.getProperty("Period").CurrentValue = parseInt(period);
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+ this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
+ if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_PULSE = new ElementCatalog_Element("Pulse","The pulse upon input will output a high only for as long as the period set, I.E. if a period of 1000ms is set then any input no matter how long, will trigger a pulse 1000ms long.","|\\__",PulseElement,[]);
+ElementReferenceTable.push(ElementCatalog_PULSE);
+ElementCategory_Timing.addElement(ElementCatalog_PULSE);
+
+class DelayElement extends Element {
+ ClockTick() {
+ if (this.Output) {
+ this.Output = false;
+ this.Task.Enabled = false;
+ this.Task.LastCall = Date.now();
+ this.Task.Time = this.Period;
+ if (this.InputPulses.length > 0) {
+ this.Task.Enabled = true;
+ }
+ } else {
+ this.Output = true;
+ this.Task.Enabled = false;
+ if (this.InputPulses.length > 0) {
+ this.Task.Time = this.InputPulses[0];
+ this.Task.Enabled = true;
+ this.InputPulses.splice(0,1);
+ } else {
+ if (this.InputEnd > 0) {
+ this.Task.Time = this.InputEnd - this.InputStart;
+ this.Task.Enabled = true;
+ }
+ }
+ this.Task.LastCall = Date.now();
+ }
+
+ for (let a = 0; a < this.OutputConnections.length; a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ Delete() {
+ super.Delete();
+ this.LogicEngine.Scheduler.deleteTask(this.Task);
+ }
+
+ getOutput(Output=0) {
+ if (super.getOutput() === 0) return false;
+ return this.Output;
+ }
+
+ setInput(Input, Value) {
+ if (Input > 0) return;
+ Value = this._Container.isHigh(this,Input);
+ if (this.Inputs[Input] == (Value === false) ? false : true) return;
+ //super.setInput(Input, Value);
+ this.Inputs[Input] = Value;
+
+ if (this.Inputs[0] && !this.Task.Enabled) {
+ this.InputStart = Date.now();
+ this.InputEnd = 0;
+ this.Output = false;
+ for (let a = 0; a < this.OutputConnections.length;a++) {
+ this.LogicEngine.RecursionCount = 0;
+ this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
+ }
+ this.Task.LastCall = Date.now();
+ this.Task.Enabled = true;
+ } else {
+ if (!this.Inputs[Input]) {
+ this.InputEnd = Date.now();
+ if (this.InputPulses.length > 0) {
+ this.InputPulses.push(this.InputEnd - this.InputStart);
+ this.InputStart = 0;
+ this.InputEnd = 0;
+ } else {
+ if (!this.Task.Enabled) {
+ this.Task.LastCall = Date.now();
+ this.Task.Time = (this.InputEnd - this.InputStart) - (Date.now() - this.InputEnd);
+ this.Task.Enabled = true;
+ } else {
+ this.InputPulses.push(this.InputEnd - this.InputStart);
+ }
+ }
+ } else {
+ // We are in a condition where input went true, but the task is already enabled
+ this.InputStart = Date.now();
+ }
+ }
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ constructor(_Container, RestoreData = null, logicengine) {
+ super(_Container, RestoreData,logicengine,1);
+ this.removeProperty("Inputs");
+ this.Name = "Delay";
+ this.Period = 100;
+ this.Output = false;
+ this.Width = 100;
+ this.Task = new Task("DelayTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
+ this.Task.Enabled = false;
+ this.removeProperty("Inputs");
+ let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
+ if (RestoreData) {
+ if (this.Height != RestoreData.Height) {
+ // Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
+ this.Height = RestoreData.Height;
+ }
+ periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
+ periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
+ periodProperty.Values = RestoreData.Properties[0].Values;
+ periodProperty.Minimium = RestoreData.Properties[0].Minimium;
+ periodProperty.Maximium = RestoreData.Properties[0].Maximium;
+
+ this.Period = periodProperty.CurrentValue;
+
+ this.Task.Enabled = RestoreData.Task.Enabled;
+ this.Task.Time = RestoreData.Task.Time;
+ this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
+ this.Task.CallCount = RestoreData.Task.CallCount;
+ }
+ this.Properties.push(periodProperty);
+ this.InputStart = 0;
+ this.InputEnd = 0;
+ this.InputPulses = new Array();
+ this.Inputs[0] = false;
+ this.LogicEngine.Scheduler.addTask(this.Task);
+ }
+ toJSON(key) {
+ let superjson = super.toJSON(key);
+ superjson.InputStart = this.InputStart;
+ superjson.InputEnd = this.InputEnd;
+ superjson.InputPulses = this.InputPulses;
+ superjson.Task = {
+ Enabled: this.Task.Enabled,
+ Time: this.Task.Time,
+ LastCall: Date.now() - this.Task.LastCall,
+ CallCount: this.Task.CallCount
+ };
+ return superjson;
+ }
+
+ setPeriod(period) {
+ this.Period = parseInt(period);
+ this.Task.Time = parseInt(period);
+ this.getProperty("Period").CurrentValue = parseInt(period);
+ this.drawElement(0,0,this.StaticCtx);
+ }
+
+ drawElement(x, y, ctx) {
+ if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
+ this.StaticCanvas.width = this.Width;
+ this.StaticCanvas.height = this.Height;
+
+ this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
+ this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
+ if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms " + "(" + this.InputPulses.length + ")","10px Console");
+ this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
+ this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
+ }
+}
+let ElementCatalog_DELAY = new ElementCatalog_Element("Delay","The delay element will delay any input signal for the given period then output it for the same length of time it came in. Inputs will be buffered and played back out at the period rate.","__|\\",DelayElement,[]);
+ElementReferenceTable.push(ElementCatalog_DELAY);
+ElementCategory_Timing.addElement(ElementCatalog_DELAY);
diff --git a/js/globalfunctions.js b/js/globalfunctions.js
index c9b3e95..67f3984 100644
--- a/js/globalfunctions.js
+++ b/js/globalfunctions.js
@@ -26,7 +26,6 @@ function addElement(RestoreData = null,refClass,props) {
if (y + height > logicEngine.Canvas.height) y = logicEngine.Canvas.height - height;
x = logicEngine.Settings.GridSize * Math.round(x/logicEngine.Settings.GridSize);
y = logicEngine.Settings.GridSize * Math.round(y/logicEngine.Settings.GridSize);
-
}
}
newElement.X = x;
@@ -437,5 +436,6 @@ function loadsave(savedata) {
logicEngine.Panning.OffsetY = savedata.PanY;
logicEngine.Ctx.translate(logicEngine.Panning.OffsetX,logicEngine.Panning.OffsetY);
}
+ logicEngine.RedrawStatics();
return true;
}
diff --git a/js/logicengine.js b/js/logicengine.js
index 28db691..7a5e206 100644
--- a/js/logicengine.js
+++ b/js/logicengine.js
@@ -546,6 +546,12 @@ class LogicEngine {
return {x: mousePos.x - this.Panning.OffsetX, y: mousePos.y - this.Panning.OffsetY};
}
+ RedrawStatics() {
+ for (let a = 0; a < this.ActiveContainer.Elements.length; a++) {
+ this.ActiveContainer.Elements[a].drawElement(0,0,this.ActiveContainer.Elements[a].StaticCtx);
+ }
+ }
+
DrawLoop() {
if (this.RecursionError) {
this.RecursionError = false;
diff --git a/js/main.js b/js/main.js
index db8e155..9575b2d 100644
--- a/js/main.js
+++ b/js/main.js
@@ -2,7 +2,8 @@
MatCat BrowserLogic Simulator
*/
-let Version = "0.4.9";
+let Version = "0.4.10";
+
let spanVersion = document.getElementById("version");
spanVersion.innerText = Version;
// get the canvas and get the engine object going