diff --git a/README.md b/README.md index cb9293d..3fa7ced 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ To be decided, but at this moment this code is open source and free to use for n ## Changelog +### 0.3.2 + +* Added IC Element, allows for converting a logic design to an IC for use in designs. +* Added IC Input and IC Output which allow for external connections when a design is used as an IC +* Added a New button for creating a new design + ### 0.3.1 * Toolbox is now dynamically created and categorized * The delete button is gone for now (use delete key on keyboard), it will come back when toolbar is added. diff --git a/css/main.css b/css/main.css index 2b8bba9..a752759 100644 --- a/css/main.css +++ b/css/main.css @@ -366,7 +366,7 @@ textarea { #WelcomeWindow label { font-size: 0.5em; } -#PropertiesBox { +#PropertiesBox, #CreateICBox { display: none; position: absolute; right: 20px; @@ -375,15 +375,25 @@ textarea { height: 200px; background-color: #444455; border: 1px solid black; + z-index: 999; +} +#CreateICBox { + width: 400px; + height: 300px; + z-index: 9999999; } -#PropertiesBoxTitle { +#CreateICBox input[pattern]:invalid { + border: 3px solid red; +} + +#PropertiesBoxTitle, #CreateICBoxTitle { text-align: center; background-color: #222222; font-size: 1.5em; } -#PropertiesBoxContent { +#PropertiesBoxContent, #CreateICBoxContent { padding: 5px; } diff --git a/index.html b/index.html index 5729f37..d522f61 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -21,7 +21,7 @@ - +

MatCat BrowserLogic Engine

@@ -32,9 +32,10 @@
+         - +
MatCat BrowserLogic @@ -44,6 +45,7 @@
LOADING
+
@@ -55,6 +57,18 @@ Content
+
+
+ Create new IC +
+
+

To create an IC you must give it a few parameters below:

+
Name
+
Description
+
   
+
+
+
diff --git a/js/baseclasses.js b/js/baseclasses.js index ebe1df9..b9abd56 100644 --- a/js/baseclasses.js +++ b/js/baseclasses.js @@ -57,12 +57,31 @@ class CanvasTools { } +class ContainerConnection { + constructor(fromElementContainer,toElementContainer,fromElement,toElement,input) { + this.fromElementContainer = fromElementContainer; + this.toElementContainer = toElementContainer; + this.fromElement = fromElement; + this.toElement = toElement; + this.Input = input; + } + toJSON(key) { + return { + fromElement: (this.fromElement) ? this.fromElement.Designator : null, + toElement: (this.toElement) ? this.toElement.Designator : null, + Input: parseInt(this.Input) + }; + } +} class elementContainer { constructor() { this.Elements = new Array(); this.Selected = false; + this.Inputs = new Array(); + this.Outputs = new Array(); + this.ICOutputs = 0; } toJSON(key) { @@ -117,7 +136,9 @@ class elementContainer { } DrawAll(ctx,settings) { + let ICOuts = 0; for (let a = 0; a < this.Elements.length; a++) { + if (this.Elements[a] instanceof ICOutput) ICOuts++; if (this.Elements[a] == this.Selected) this.Elements[a].drawBorderBox(ctx, this.Elements[a].X - 2, this.Elements[a].Y - 2, this.Elements[a].Width + 4, this.Elements[a].Height + 4, 1, "rgba(100,200,255,0.25)", "rgba(100,200,255,0.25)"); this.Elements[a].drawElement(this.Elements[a].X, this.Elements[a].Y, ctx); let old_font = ctx.font; @@ -132,6 +153,7 @@ class elementContainer { ctx.fillStyle = old_fillStyle; } + this.ICOutputs = ICOuts; if (!this.Selected) { let PropertiesBox = document.getElementById("PropertiesBox"); if (PropertiesBox.style.display != "none") PropertiesBox.style.display = "none"; @@ -157,6 +179,9 @@ class elementContainer { case "int": contentString += ""; break; + case "string": + contentString += ''; + break; } contentString += ""; } diff --git a/js/elements.js b/js/elements.js index 6e99ca5..53b78a2 100644 --- a/js/elements.js +++ b/js/elements.js @@ -3,9 +3,11 @@ let ElementReferenceTable = new Array(); let ElementCategory_Inputs = new ElementCatalog_Category("Inputs",""); let ElementCategory_LOGIC = new ElementCatalog_Category("Logic" ,""); let ElementCategory_Timing = new ElementCatalog_Category("Timing" ,""); +let ElementCategory_ICs = new ElementCatalog_Category("ICs" ,""); let elementCatalog = new ElementCatalog([ElementCategory_Inputs, ElementCategory_LOGIC, - ElementCategory_Timing]); + ElementCategory_Timing, + ElementCategory_ICs]); class ElementProperty { constructor(name,type,callback,defaultValue,currentValue = false,values=false,min=0,max=12) { /* @@ -51,15 +53,17 @@ class ElementProperty { } class ElementConnection { - constructor(elementContainer,element,input) { + 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) + Input: parseInt(this.Input), + Output: this.Output }; } } @@ -82,6 +86,9 @@ class Element extends CanvasTools { this.MousePosition = {x: 0, y: 0}; this.Properties = new Array(); this.LogicEngine = logicengine; + this.Outputs = new Array(1); + this.NoOutput = false; + this.OutputLink = 0; let inputProperty = new ElementProperty("Inputs","int",{CBObject: this,CBFunction: "ChangeInputs"},2,Inputs,false,2); this.Properties.push(inputProperty); @@ -92,7 +99,7 @@ class Element extends CanvasTools { this.X = RestoreData.X; this.Y = RestoreData.Y; if (RestoreData.Properties.length > 0) { - if (this.Properties[0].Name == "Inputs") { + 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; @@ -167,6 +174,10 @@ class Element extends CanvasTools { return; } + LinkOutLocation() { + return this.OutputLink; + } + MouseClick(mousePos) { let mouseDistOutput = length2D(this.X+(this.Width-10), @@ -193,9 +204,23 @@ class Element extends CanvasTools { } } else { - if (mouseDistOutput <= (this.outputCircleRadius)) { - // Clicked on output, let us start a link - this.LogicEngine.Link(); + 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), + this.MousePosition.x, + this.MousePosition.y); + console.log("Distance from " + a + ": " + mouseDist); + if (mouseDist <= (this.outputCircleRadius)) { + this.OutputLink = {Output: a,x: this.X+(this.Width-10), y: firstY+ (a*24)}; + this.LogicEngine.Link(); + foundOutput = true; + break; + } } } } @@ -207,20 +232,22 @@ class Element extends CanvasTools { return this.MouseOver; } - addConnection(container, element, input) { + 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 + console.log("Removing link"); this.LogicEngine.RecursionCount = 0; element.setInput(input,false); this.OutputConnections.splice(a,1); return; } } - let newConnection = new ElementConnection(container,element,input); + let newConnection = new ElementConnection(container,element,input,output); this.OutputConnections.push(newConnection); this.LogicEngine.RecursionCount = 0; element.setInput(input,this.getOutput()); + return newConnection; } drawInputs(ctx,x,y,borderColor = "#000",circleColorFalse = "#ff0000",circleColorTrue="#00ff00",circleColorHover = "#00ffff") { @@ -262,6 +289,10 @@ class Element extends CanvasTools { drawConnections(ctx,settings) { ctx.save(); + let centerY = this.Y + Math.round(this.Height / 2); + let totalHeight = this.Outputs.length * ((this.outputCircleRadius*2)+4); + let firstY = (centerY - (totalHeight/2)) + 12; + for (let a = 0; a < this.OutputConnections.length;a++) { if (!this.OutputConnections[a].Container.HasElement(this.OutputConnections[a].Element)) { // This is a ghosted connection, lets get rid of it @@ -273,7 +304,7 @@ class Element extends CanvasTools { let endFirstY = (endCenterY - (endTotalHeight/2)) + 12; let startX = this.X + this.Width; - let startY = this.Y+(this.Height/2); + 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); @@ -292,7 +323,7 @@ class Element extends CanvasTools { ctx.quadraticCurveTo(startMidX,startMidY,midX,midY); ctx.quadraticCurveTo(endMidX,endMidY,endX,endY); ctx.strokeStyle = settings.ActiveConnectionColor; - if (!this.getOutput()) ctx.strokeStyle = settings.InactiveConnectionColor; + if (!this.getOutput(this.OutputConnections[a].Output)) ctx.strokeStyle = settings.InactiveConnectionColor; ctx.stroke(); } } @@ -330,7 +361,7 @@ class Element extends CanvasTools { } } - getOutput() { + getOutput(Output=0) { /* Should return true or false */ @@ -341,7 +372,7 @@ class Element extends CanvasTools { /* Draw routine for the element */ - this.drawBorderBox(ctx,x+10,y,drawWidth-20,drawHeight); + 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); @@ -349,6 +380,313 @@ class Element extends CanvasTools { } +class ICInput extends Element { + setPinName(pinname) { + // Dont actually need it to do anything + this.Properties[0].CurrentValue = pinname; + this.Output = false; + } + + constructor(RestoreData = null,logicengine) { + super(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) this.Input = false; + if (value) this.Input = 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; + + 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) { + 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(RestoreData = null,logicengine) { + super(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; + if (!value) this.Input = false; + if (value) this.Input = true; + this.Inputs[0] = this.Input; + + if (!this.Task.Enabled) { + console.log("Starting timer"); + this.Task.LastCall = 0; + this.Task.Enabled = true; + } + } + + ClockTick() { + this.Output = this.Input; + this.Task.Enabled = false; + this.Task.LastCall = 0; + //console.log(this); + 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) { + 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(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(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; + } + + addConnection(container, element, input, output = 0) { + console.log("IC Add Connection " + element.Designator + " I:" + input + " O:"+output); + super.addConnection(container, element, input, output); + console.log(this.OutputConnections); + 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 + this.Inputs[Input] = Value; + //console.log("Calling " + this.InputConnections[Input].toElement.Designator); + this.InputConnections[Input].toElement.setInput(this.InputConnections[Input].Input,Value); + } + return false; + } + + getOutput(Output = 0) { + 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 = this.textSize(ctx,this.InputConnections[a].toElement.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]) { @@ -370,7 +708,7 @@ class ClockElement extends Element { this.LogicEngine.Scheduler.deleteTask(this.Task); } - getOutput() { + getOutput(Output=0) { return this.Output; } @@ -482,7 +820,7 @@ class PulseElement extends Element { this.LogicEngine.Scheduler.deleteTask(this.Task); } - getOutput() { + getOutput(Output=0) { return this.Output; } @@ -603,7 +941,7 @@ class DelayElement extends Element { this.LogicEngine.Scheduler.deleteTask(this.Task); } - getOutput() { + getOutput(Output=0) { return this.Output; } @@ -723,7 +1061,7 @@ class inputElement extends Element { this.removeProperty("Inputs"); } - getOutput() { + getOutput(Output=0) { return this.Output; } @@ -821,7 +1159,7 @@ class LogicAND extends Element { this.Name = "AND"; } - getOutput() { + getOutput(Output=0) { let ANDResult = true; for (let a = 0; a < this.totalInputs();a++) { if (!this.Inputs[a]) ANDResult = false; @@ -864,7 +1202,7 @@ class LogicNAND extends LogicAND { this.Name = "NAND"; this.Width = this.Width + 10; } - getOutput() { + getOutput(Output=0) { if (super.getOutput()) { return false; } else { @@ -916,7 +1254,7 @@ class LogicOR extends Element { this.Name = "OR"; } - getOutput() { + getOutput(Output=0) { let ORResult = false; for (let a = 0; a < this.totalInputs();a++) { if (this.Inputs[a]) ORResult = true; @@ -963,7 +1301,7 @@ class LogicNOR extends LogicOR { this.Name = "NOR"; this.Width = this.Width + 10; } - getOutput() { + getOutput(Output=0) { if (super.getOutput()) { return false; } else { @@ -1018,7 +1356,7 @@ class LogicXOR extends Element { this.removeProperty("Inputs"); } - getOutput() { + getOutput(Output=0) { let ORResult = false; if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true; return ORResult; @@ -1081,7 +1419,7 @@ class LogicXNOR extends Element { this.Width += 10; } - getOutput() { + getOutput(Output=0) { let ORResult = false; if ( (this.Inputs[0] && !this.Inputs[1]) || (!this.Inputs[0] && this.Inputs[1]) ) ORResult = true; return !ORResult; @@ -1155,7 +1493,7 @@ class LogicNOT extends Element { this.Width += 10; } - getOutput() { + getOutput(Output=0) { if (this.Inputs[0]) return false; return true; } @@ -1217,7 +1555,7 @@ class LogicBuffer extends Element { this.LogicEngine.Scheduler.deleteTask(this.Task); } - getOutput() { + getOutput(Output=0) { return this.Output; } diff --git a/js/globalfunctions.js b/js/globalfunctions.js index 4436d5f..fc704ce 100644 --- a/js/globalfunctions.js +++ b/js/globalfunctions.js @@ -28,6 +28,32 @@ function addElement(RestoreData = null,refClass,props) { return newElement; } +function createIC(container,name,description) { + let newContainer = false; + if (container instanceof elementContainer) newContainer = loadContainer(JSON.parse(JSON.stringify(container))); + if (typeof container == 'string' || container instanceof String) newContainer = loadContainer(JSON.parse(container)); + if (!newContainer) return false; + + let ICSettings = { + Name: name.replace('^[a-zA-Z0-9_]+$',''), + Description: description, + Container: JSON.stringify(container), + }; + + let outputCount = 0; + for (let a = 0; a < newContainer.Elements.length; a++) { + if (newContainer.Elements[a] instanceof ICOutput) { + outputCount++; + } + } + if (outputCount == 0) return false; + let ElementCatalog_IC = new ElementCatalog_Element(ICSettings.Name,ICSettings.Description,"≡█≡",ICElement,[ICSettings]); + ElementReferenceTable.push(ElementCatalog_IC); + ElementCategory_ICs.addElement(ElementCatalog_IC); + BuildToolbox(); + return ElementCatalog_IC; +} + function setCookie(cname, cvalue, exdays) { let d = new Date(); d.setTime(d.getTime() + (exdays*24*60*60*1000)); @@ -162,24 +188,47 @@ function download(filename, savestate) { document.body.removeChild(element); } -function loadsave(savedata) { - if (!savedata) return false; - if (!savedata.Version) return false; // TODO: Let the person know invalid save file - if (!isVersionNewer(savedata.Version,"0.3.0")) return false; // TODO: Let the person know the version is too old +function loadContainer(Elements) { let newContainer = new elementContainer(); let elementConnections = new Array(); - for (let a = 0; a < savedata.Elements.length; a++) { - let classRef = getElementInfo(savedata.Elements[a].Name).Class; - let newElement = new classRef(savedata.Elements[a],logicEngine,savedata.Elements[a].Args); + let icelementConnections = new Array(); + for (let a = 0; a < Elements.length; a++) { + if (Elements[a].IsIC) { + let classRef = getElementInfo(Elements[a].Name).Class; + if (!classRef) { + // We need to add this IC to the toolbox + let newIC = createIC(JSON.stringify(Elements[a].ICBlueprint),Elements[a].Name,Elements[a].Description); + console.log("NewIC:" + newIC); + if (!newIC) return false; + } + } + let classRef = getElementInfo(Elements[a].Name).Class; + + let newElement = new classRef(Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]); + newContainer.AddElement(newElement); - newElement.Designator = savedata.Elements[a].Designator; - if (savedata.Elements[a].Outputs) { - if (savedata.Elements[a].Outputs.length > 0) { - for (let b=0; b < savedata.Elements[a].Outputs.length; b++) { + newElement.Designator = Elements[a].Designator; + if (Elements[a].Outputs) { + if (Elements[a].Outputs.length > 0) { + for (let b=0; b < Elements[a].Outputs.length; b++) { elementConnections.push({ FromElement: newElement, - Input: savedata.Elements[a].Outputs[b].Input, - ToElement: savedata.Elements[a].Outputs[b].Element + Input: Elements[a].Outputs[b].Input, + Output: Elements[a].Outputs[b].Output, + ToElement: Elements[a].Outputs[b].Element + }); + } + } + } + if (Elements[a].ICOutputs) { + if (Elements[a].ICOutputs.length > 0) { + for (let b=0; b < Elements[a].ICOutputs.length; b++) { + icelementConnections.push({ + Element: newElement, + FromContainer: newElement.Container, + FromElement: Elements[a].ICOutputs[b].fromElement, + Input: Elements[a].ICOutputs[b].Input, + ToElement: Elements[a].ICOutputs[b].toElement }); } } @@ -187,12 +236,56 @@ function loadsave(savedata) { } // Now we need to make all of the connections for (let a = 0; a < elementConnections.length; a++) { + if (elementConnections[a].FromElement.Name == "1B_ADD") { + console.log("Making connection " + a); + console.log(elementConnections[a]); + } let toElement = newContainer.HasElement(elementConnections[a].ToElement); if (toElement) { - let newConnection = new ElementConnection(newContainer,toElement,elementConnections[a].Input); - elementConnections[a].FromElement.OutputConnections.push(newConnection); + if (elementConnections[a].FromContainer) { + let fromElement = elementConnections[a].FromContainer.HasElement(elementConnections[a].fromElement); + let newConnection = new ContainerConnection(elementConnections[a].FromContainer,newContainer,fromElement,toElement,elementConnections[a].Input); + elementConnections[a].Element.Outputs.push(newConnection); + + } else { + let newConnection = new ElementConnection(newContainer, toElement, elementConnections[a].Input, elementConnections[a].Output); + //elementConnections[a].FromElement.addConnection(newContainer,toElement,elementConnections[a].Input,elementConnections[a].Output) + if (newConnection) { + //elementConnections[a].FromElement.OutputConnections.push(newConnection); + elementConnections[a].FromElement.addConnection(newContainer,toElement,elementConnections[a].Input, elementConnections[a].Output) + if (elementConnections[a].FromElement.Name == "1B_ADD") { + console.log("Making Connection"); + console.log(newConnection); + console.log(elementConnections[a].FromElement); + console.log(elementConnections[a].FromElement.OutputConnections); + } + } else { + console.log("We dont have a new connection!!!"); + console.log(toElement); + console.log(elementConnections[a]); + } + + } } } +/* + for (let a = 0; a < icelementConnections.length; a++) { + let fromElement = icelementConnections[a].FromContainer.HasElement(icelementConnections[a].FromElement); + let toElement = newContainer.HasElement(icelementConnections[a].ToElement); + if (toElement) { + icelementConnections[a].Element.addConnection(newContainer, toElement, icelementConnections[a].Input); + } + } + */ + return newContainer; +} + +function loadsave(savedata) { + if (!savedata) return 0; + if (!savedata.Version) return -1; // TODO: Let the person know invalid save file + if (!isVersionNewer(savedata.Version,"0.3.0")) return -2; // TODO: Let the person know the version is too old + let newContainer = loadContainer(savedata.Elements); + if (!newContainer) return -3; logicEngine.ActiveContainer = newContainer; return true; } diff --git a/js/logicengine.js b/js/logicengine.js index 88a0d9d..73f33a5 100644 --- a/js/logicengine.js +++ b/js/logicengine.js @@ -151,7 +151,7 @@ class LogicEngine { Link(input = 0) { if (this.ActiveLink) { if (this.ActiveContainer.Selected && (this.ActiveContainer.Selected != this.ActiveLink)) { - this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected,input); + this.ActiveLink.addConnection(this.ActiveContainer,this.ActiveContainer.Selected,input,this.ActiveLink.OutputLink.Output); this.ActiveLink = false; } else { this.ActiveLink = false; @@ -173,9 +173,12 @@ class LogicEngine { this.Ctx.clearRect(0,0,this.Canvas.width,this.Canvas.height); this.ActiveContainer.DrawAll(this.Ctx,this.Settings); + let btn_CreateIC = document.getElementById("btn_CreateIC"); + btn_CreateIC.disabled = true; + if (this.ActiveContainer.ICOutputs > 0) btn_CreateIC.disabled = false; if (this.ActiveLink) { - let startX = this.ActiveLink.X + this.ActiveLink.Width; - let startY = this.ActiveLink.Y+(this.ActiveLink.Height/2); + let startX = this.ActiveLink.LinkOutLocation().x; + let startY = this.ActiveLink.LinkOutLocation().y; let endX = this.Mouse.x; let endY = this.Mouse.y; let startMidX = startX + ((endX - startX)/2); diff --git a/js/main.js b/js/main.js index f590b14..0486347 100644 --- a/js/main.js +++ b/js/main.js @@ -2,7 +2,7 @@ MatCat BrowserLogic Simulator */ -let Version = "0.3.1"; +let Version = "0.3.2"; let spanVersion = document.getElementById("version"); spanVersion.innerText = Version; // get the canvas and get the engine object going @@ -50,6 +50,45 @@ btn_CloseWelcome.addEventListener('click', function(evt) { } }, false); +let btn_CreateIC = document.getElementById("btn_CreateIC"); +btn_CreateIC.addEventListener('click', function(evt) { + let CreateICBox = document.getElementById("CreateICBox"); + CreateICBox.style.display = "block"; +}); + +let btn_CreateIC_Cancel = document.getElementById("btn_CreateIC_Cancel"); +btn_CreateIC_Cancel.addEventListener('click', function(evt) { + let CreateICBox = document.getElementById("CreateICBox"); + CreateICBox.style.display = "none"; +}); + +let btn_CreateIC_Create = document.getElementById("btn_CreateIC_Create"); +btn_CreateIC_Create.addEventListener('click', function(evt) { + let CreateICBox = document.getElementById("CreateICBox"); + let ICName = document.getElementById("ICName"); + let ICDescription = document.getElementById("ICDescription"); + createIC(logicEngine.ActiveContainer,ICName.value,ICDescription.value); + CreateICBox.style.display = "none"; +}); + +let ICName = document.getElementById("ICName"); +let ICDescription = document.getElementById("ICDescription"); + +ICName.addEventListener('input', function(evt){ + btn_CreateIC_Create.disabled = true; + if (ICName.value.length > 0 && ICDescription.value.length > 0) btn_CreateIC_Create.disabled = false; +}); + +ICDescription.addEventListener('input', function(evt){ + btn_CreateIC_Create.disabled = true; + if (ICName.value.length > 0 && ICDescription.value.length > 0) btn_CreateIC_Create.disabled = false; +}); + +let btn_New = document.getElementById("btn_New"); +btn_New.addEventListener('click', function(evt) { + logicEngine.ActiveContainer = new elementContainer(); +}); + let btn_Save = document.getElementById("btn_Save"); btn_Save.addEventListener('click', function(evt) { @@ -67,11 +106,24 @@ file_Load.addEventListener('change', function(evt) { return function (e) { try { let restoredata = JSON.parse(e.target.result); - if (!loadsave(restoredata)) { - alert("Bad file!"); + console.log(restoredata); + loadresult = loadsave(restoredata); + if (!loadresult) { + switch(loadresult) { + case -1: + alert("Invalid LogicParts file!"); + break; + case -2: + alert("The version of MatCat BrowserLogic cannot open this save version!"); + break; + default: + alert("Bad save file!"); + break; + } } } catch (ex) { alert("Bad file!"); + console.log(ex); } } })(evt.target.files[0]);