0.3.2: IC Chips

This commit is contained in:
MatCat 2021-03-02 00:56:19 -08:00
parent 6dc7eb5e03
commit fce90becfc
8 changed files with 593 additions and 52 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -1,5 +1,5 @@
<!doctype html>
<html class="no-js" lang="">
<html class="no-js" lang="" style="height: 100%;">
<head>
<meta charset="utf-8">
@ -21,7 +21,7 @@
<meta name="theme-color" content="#fafafa">
</head>
<body>
<body style="height: 100%;">
<div id="WelcomeWindow">
<h2>MatCat BrowserLogic Engine</h2>
@ -32,9 +32,10 @@
<script src="js/vendor/modernizr-3.11.2.min.js"></script>
<div id="top-bar">
<div id="LeftOf-SiteTitle">
<input type="button" id="btn_New" value="New"/>&nbsp;&nbsp;&nbsp;
<input type="button" id="btn_Save" value="Save"/>&nbsp;&nbsp;&nbsp;
<input type="button" id="btn_Load" value="Load"/>
<input type="file" id="file_Load" style="display: none;" />
<input type="file" id="file_Load" accept=".LogicParts" style="display: none;" />
</div>
<div id ="SiteTitle">
MatCat BrowserLogic <span id="version"> </span>
@ -44,6 +45,7 @@
<div id="inner-left-menu">
LOADING
</div>
<input type="button" id="btn_CreateIC" value="Create IC" style="position: absolute; bottom: 30px; left: 50px;">
</div>
<canvas id="GridPlane" width="400" height="300" style="position: absolute; top: 50px; left 202px;"></canvas>
<canvas id="LogicPlane" width="400" height="300" style="margin: 0px; padding: 0px; position: absolute; top: 50px; left: 202px;"></canvas>
@ -55,6 +57,18 @@
Content
</div>
</div>
<div id="CreateICBox">
<div id="CreateICBoxTitle">
Create new IC
</div>
<div id="CreateICBoxContent">
<p>To create an IC you must give it a few parameters below:</p>
<div><span style="display: inline-block; text-align: right; vertical-align: middle; width: 100px; height: 100%; padding-right: 10px;">Name</span><input type="text" id="ICName" style="width: 250px" pattern="[a-zA-Z0-9_]+" title="Only letters, numbers, and underscore, no special characters or spaces! Spaces will be converted to _"></div>
<div><span style="display: inline-block; text-align: right; vertical-align: middle; width: 100px; height: 100%; padding-right: 10px;">Description</span><textarea id="ICDescription" style="width: 250px" rows="5"></textarea></div>
<center><input type="button" id="btn_CreateIC_Create" value="Create IC" disabled>&nbsp;&nbsp;&nbsp;<input type="button" id="btn_CreateIC_Cancel" value="Cancel"></center>
</div>
</div>
<div id="darkout-overlay"></div>
<script src="js/globalfunctions.js"></script>
<script src="js/baseclasses.js"></script>

View File

@ -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 += "<input type='number' id='prop_" + this.Selected.Properties[a].Name + "' min='" + this.Selected.Properties[a].Minimium + "' max='" + this.Selected.Properties[a].Maximium + "' value='" + this.Selected.Properties[a].CurrentValue + "' onchange='logicEngine.PropertyChange(" + String.fromCharCode(34) + this.Selected.Properties[a].Name + String.fromCharCode(34) +");'>";
break;
case "string":
contentString += '<input type="text" id="prop_' + this.Selected.Properties[a].Name + '" minlength="' + this.Selected.Properties[a].Minimium + '" maxlength="' + this.Selected.Properties[a].Maximium + '" value="' + this.Selected.Properties[a].CurrentValue + '" onchange="logicEngine.PropertyChange(' + String.fromCharCode(39) + this.Selected.Properties[a].Name + String.fromCharCode(39) + ');">';
break;
}
contentString += "</td></tr>";
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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]);