function addElement(RestoreData = null,refClass,props) { let newElement = new refClass(logicEngine.ActiveContainer,RestoreData,logicEngine,...props); if (!RestoreData) { let x = Math.round(logicEngine.Canvas.width / 2) - (newElement.Width/2); let y = Math.round(logicEngine.Canvas.height / 2) - (newElement.Height/2); x -= logicEngine.Panning.OffsetX; y -= logicEngine.Panning.OffsetY; x = logicEngine.Settings.GridSize * Math.round(x/logicEngine.Settings.GridSize); y = logicEngine.Settings.GridSize * Math.round(y/logicEngine.Settings.GridSize); let width = newElement.Width; let height = newElement.Height; let noclearspot = true; let findattempt = 0; while (noclearspot && findattempt < 100) { findattempt++; if (!logicEngine.ActiveContainer.checkOverlayBounds(x, y, width, height)) { noclearspot = false; } else { x += Math.floor(Math.random() * (width - (width - (width * 2)))) + (width - (width * 2)); y += Math.floor(Math.random() * (height - (height - (height * 2)))) + (height - (height * 2)); if (x < 0) x = 0; if (y < 0) y = 0; if (x + width > logicEngine.Canvas.width) x = logicEngine.Canvas.width - width; 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; newElement.Y = y; } logicEngine.ActiveContainer.AddElement(newElement); logicEngine.ActiveContainer.Select(newElement); logicEngine.MovingElement = new Array(logicEngine.ActiveContainer.Selected.length); for (let b = 0; b < logicEngine.ActiveContainer.Selected.length; b++) { logicEngine.ActiveContainer.Selected[b].X = (logicEngine.Mouse.x - logicEngine.Panning.OffsetX) - (logicEngine.ActiveContainer.Selected[b].Width/2); logicEngine.ActiveContainer.Selected[b].Y = (logicEngine.Mouse.y - logicEngine.Panning.OffsetY) - (logicEngine.ActiveContainer.Selected[b].Height/2); logicEngine.MovingElement[b] = { StartX: logicEngine.ActiveContainer.Selected[b].X, StartY: logicEngine.ActiveContainer.Selected[b].Y }; logicEngine.MovingElementMouseStartX = logicEngine.Mouse.x; logicEngine.MovingElementMouseStartY = logicEngine.Mouse.y; } let rect = logicEngine.Canvas.getBoundingClientRect(); let realMouseX = logicEngine.Mouse.x + rect.left; let realMouseY = logicEngine.Mouse.y + rect.top; logicEngine.MouseDown = true; logicEngine.DrawOffCanvasElement({clientX: realMouseX, clientY: realMouseY}); disableSelectedMenus(false); 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)); let expires = "expires="+ d.toUTCString(); document.cookie = cname + '="' + cvalue + '";' + expires; localStorage.setItem(cname,cvalue); } function getCookie(cname) { let name = cname + "="; let decodedCookie = decodeURIComponent(document.cookie); let ca = decodedCookie.split(';'); for(var i = 0; i (a + b)) / arr.length; } function length2D(x1,y1,x2,y2) { let xDist = x1 - x2; let yDist = y1 - y2; return Math.sqrt(xDist*xDist + yDist * yDist); } function MatchesPress(binding,evt) { let retval = true; if (binding?.Key == evt.key.toLowerCase()) { if (binding?.Alt) { if (!evt.altKey) retval = false; } else { if (evt.altKey) retval = false; } if (binding?.Ctrl) { if (!evt.ctrlKey) retval = false; } else { if (evt.ctrlKey) retval = false; } if (binding?.Shift) { if (!evt.shiftKey) retval = false; } else { if (evt.shiftKey) retval = false; } if (binding?.Meta) { if (!evt.metaKey) retval = false; } else { if (evt.metaKey) retval = false; } } else { retval = false; } return retval; } function KeybindToString(binding) { let kbs = ""; if (binding?.Meta) kbs += "Meta+"; if (binding?.Ctrl) kbs += "Ctrl+"; if (binding?.Alt) kbs += "Alt+"; if (binding?.Shift) kbs += "Shift+"; kbs += binding?.Key.toUpperCase(); return kbs; } function GetCopyObject() { if (logicEngine?.ActiveContainer?.Selected?.length > 0) { let copyObject = {Paste: true,LogicParts: Version}; copyObject.Elements = logicEngine.ActiveContainer.Selected; let copyObjectString = JSON.stringify(copyObject); copyObject = JSON.parse(copyObjectString); for (let a = 0; a < copyObject.Elements.length; a++) { console.log("Adaptering X: " + copyObject.Elements[a].X + " to " + (copyObject.Elements[a].X + logicEngine.Panning.OffsetX)); console.log("Adaptering Y: " + copyObject.Elements[a].Y + " to " + (copyObject.Elements[a].Y + logicEngine.Panning.OffsetY)); copyObject.Elements[a].X = copyObject.Elements[a].X + logicEngine.Panning.OffsetX; copyObject.Elements[a].Y = copyObject.Elements[a].Y + logicEngine.Panning.OffsetY; } return copyObject; } else { return false; } } function BuildToolbox() { let toolbox = document.getElementById("inner-left-menu"); toolbox.innerHTML = ""; for (let a = 0; a < elementCatalog.Categories.length; a++) { let div_CategoryHeader = document.createElement("div"); div_CategoryHeader.id = "TB_CATH_" + elementCatalog.Categories[a].Name; div_CategoryHeader.className ="TOOLBOX_CATEGORY_HEADER"; div_CategoryHeader.innerHTML = '

' + elementCatalog.Categories[a].Name + '

'; let div_Category = document.createElement("div"); div_Category.id = "TB_CATB_" + elementCatalog.Categories[a].Name; div_Category.className = "TOOLBOX_CATEGORY_BODY"; div_Category.innerHTML = ""; for (let b = 0; b < elementCatalog.Categories[a].Elements.length; b++) { let btn_Element = document.createElement("input"); btn_Element.type = "button"; btn_Element.id = "btn_" + elementCatalog.Categories[a].Elements[b].Name; btn_Element.title = elementCatalog.Categories[a].Elements[b].Description; btn_Element.value = elementCatalog.Categories[a].Elements[b].Icon + " " + elementCatalog.Categories[a].Elements[b].Name; btn_Element.style.width = "100px"; btn_Element.style.textAlign = "left"; btn_Element.addEventListener('click', function(evt) { addElement(null,elementCatalog.Categories[a].Elements[b].Class,elementCatalog.Categories[a].Elements[b].Args); }, false); div_Category.append(btn_Element); // div_Category.append(document.createElement("br")); } toolbox.append(div_CategoryHeader); toolbox.append(div_Category); } } function getElementInfo(element) { for (let a = 0; a < ElementReferenceTable.length; a++) { if (ElementReferenceTable[a].Name == element) return ElementReferenceTable[a]; } return false; } function isVersionNewer(version1,version2,orEqual = true) { let v1 = version1.split("."); let v2 = version1.split("."); if (v1.length != 3) return false; if (v2.length != 3) return false; for (let a = 0; a < 3; a++) { v1[a] = parseInt(v1[a]); v2[a] = parseInt(v2[a]); } if (v1[0] > v2[0]) return true; if (v1[0] == v2[0] && v1[1] > v2[1]) return true; if (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] > v2[2]) return true; if ((v1[0] == v2[0] && v1[1] == v2[1]) && (orEqual && v1[2] == v2[2])) return true; return false; } function disableSelectedMenus(bool) { disableSelectedRCMs(bool); disableSelectedTFMs(bool); } function ShowHelp() { let helpWindow = document.getElementById("HelpWindow"); helpWindow.style.display = "block"; helpWindow.style.left = Math.round((window.innerWidth / 2) - (helpWindow.clientWidth/2)) + "px"; helpWindow.style.top = Math.round((window.innerHeight / 2) - (helpWindow.clientHeight/2)) + "px"; } function HideHelp() { let helpWindow = document.getElementById("HelpWindow"); helpWindow.style.display = "none"; } function HidePropertiesWindow() { let propwin = document.getElementById("PropertiesBox"); propwin.style.display = "none"; } function SaveSettings() { localStorage.setItem("LogicEngineSettings",JSON.stringify(logicEngine.Settings)); console.log("Settings Saved"); } function createSaveState(container = false) { let saveState = { Name: "LogicDesign", Version: Version, Timestamp: Date.now(), PanX: logicEngine.Panning.OffsetX, PanY: logicEngine.Panning.OffsetY, Elements: new Array() }; if (container.Elements.length > 0) saveState.Elements = container.Elements; let saveCompressed = document.getElementById("saveCompressed"); if (saveCompressed.checked) { saveState.Elements = LZString.compressToUTF16(JSON.stringify(saveState.Elements)); saveState.Compressed = true; } else { saveState.Compressed = false; } return saveState; } function download(filename, savestate) { let text = JSON.stringify(savestate); let element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); } function getPastedElementDesignator(map,element) { for (let a = 0; a < map?.length; a++) { if (map[a].Old == element) return map[a].New; } return false; } function loadActiveContainer(Elements) { let elementConnections = new Array(); let icelementConnections = new Array(); let designatorMap = new Array(); let selectedObjects = new Array(); let lowestX = 9999999999; let lowestY = 9999999999; let highestX = 0; let highestY = 0; 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(logicEngine.ActiveContainer,Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]); if (newElement) selectedObjects.push(newElement); logicEngine.ActiveContainer.AddElement(newElement); newElement.X -= logicEngine.Panning.OffsetX; newElement.Y -= logicEngine.Panning.OffsetY; if (newElement.X < lowestX) lowestX = newElement.X; if (newElement.X > highestX) highestX = newElement.X; if (newElement.Y < lowestY) lowestY = newElement.Y; if (newElement.Y > highestY) highestY = newElement.Y; designatorMap.push({Old: Elements[a].Designator, New: newElement.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: 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 }); } } } } logicEngine.ActiveContainer.Selected = selectedObjects; logicEngine.MovingElement = new Array(logicEngine.ActiveContainer.Selected.length); for (let b = 0; b < logicEngine.ActiveContainer.Selected.length; b++) { logicEngine.ActiveContainer.Selected[b].X = (logicEngine.ActiveContainer.Selected[b].X - lowestX) + (logicEngine.Mouse.x - logicEngine.Panning.OffsetX) - ((highestX-lowestX)/2); logicEngine.ActiveContainer.Selected[b].Y = (logicEngine.ActiveContainer.Selected[b].Y - lowestY) + (logicEngine.Mouse.y - logicEngine.Panning.OffsetY) - ((highestY-lowestY)/2); logicEngine.MovingElement[b] = { StartX: logicEngine.ActiveContainer.Selected[b].X, StartY: logicEngine.ActiveContainer.Selected[b].Y }; logicEngine.MovingElementMouseStartX = logicEngine.Mouse.x; logicEngine.MovingElementMouseStartY = logicEngine.Mouse.y; } logicEngine.MouseDown = true; for (let a = 0; a < elementConnections.length; a++) { let toElement = logicEngine.ActiveContainer.HasElement(getPastedElementDesignator(designatorMap,elementConnections[a].ToElement)); if (toElement) { if (elementConnections[a].FromContainer) { let fromElement = elementConnections[a].FromContainer.HasElement(elementConnections[a].fromElement); let newConnection = new ContainerConnection(elementConnections[a].FromContainer,logicEngine.ActiveContainer,fromElement,toElement,elementConnections[a].Input); elementConnections[a].Element.Outputs.push(newConnection); } else { let newConnection = new ElementConnection(logicEngine.ActiveContainer, toElement, elementConnections[a].Input, elementConnections[a].Output); if (newConnection) { elementConnections[a].FromElement.addConnection(logicEngine.ActiveContainer,toElement,elementConnections[a].Input, elementConnections[a].Output) } else { console.log("We dont have a new connection!!!"); console.log(toElement); console.log(elementConnections[a]); } } } } } function loadContainer(Elements) { let newContainer = new elementContainer(); let elementConnections = new Array(); 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(newContainer,Elements[a],logicEngine,getElementInfo(Elements[a].Name).Args[0]); newContainer.AddElement(newElement); 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: 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 }); } } } } // Now we need to make all of the connections for (let a = 0; a < elementConnections.length; a++) { let toElement = newContainer.HasElement(elementConnections[a].ToElement); if (toElement) { 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) } 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 if (savedata?.Compressed) savedata.Elements = JSON.parse(LZString.decompressFromUTF16(savedata.Elements)); let newContainer = loadContainer(savedata.Elements); if (!newContainer) return -3; let saveName = document.getElementById("saveName"); saveName.value = savedata.Name; logicEngine.ActiveContainer = newContainer; logicEngine.Panning.OffsetX = 0; logicEngine.Panning.OffsetY = 0; logicEngine.Ctx.setTransform(1,0,0,1,0,0); if (savedata.PanX) { logicEngine.Panning.OffsetX = savedata.PanX; logicEngine.Panning.OffsetY = savedata.PanY; logicEngine.Ctx.translate(logicEngine.Panning.OffsetX,logicEngine.Panning.OffsetY); } logicEngine.RedrawStatics(); return true; }