8dd1f89225
- allow ledmap selection in UI - upload gap file - expand matrix generator
223 lines
7.6 KiB
HTML
223 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=500">
|
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
|
<title>2D Set-up</title>
|
|
<script>
|
|
var d=document;
|
|
var loc = false, locip;
|
|
var maxPanels=64;
|
|
function H(){window.open("https://kno.wled.ge/features/2D");}
|
|
function B(){window.open("/settings","_self");}
|
|
function gId(n){return d.getElementById(n);}
|
|
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
|
function loadJS(FILE_URL, async = true) {
|
|
let scE = d.createElement("script");
|
|
scE.setAttribute("src", FILE_URL);
|
|
scE.setAttribute("type", "text/javascript");
|
|
scE.setAttribute("async", async);
|
|
d.body.appendChild(scE);
|
|
// success event
|
|
scE.addEventListener("load", () => {
|
|
//console.log("File loaded");
|
|
GetV();
|
|
UI();
|
|
Sf.MPC.setAttribute("max",maxPanels);
|
|
});
|
|
// error event
|
|
scE.addEventListener("error", (ev) => {
|
|
console.log("Error on loading file", ev);
|
|
alert("Loading of configuration script failed.\nIncomplete page data!");
|
|
});
|
|
}
|
|
function S() {
|
|
if (window.location.protocol == "file:") {
|
|
loc = true;
|
|
locip = localStorage.getItem('locIp');
|
|
if (!locip) {
|
|
locip = prompt("File Mode. Please enter WLED IP!");
|
|
localStorage.setItem('locIp', locip);
|
|
}
|
|
}
|
|
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10';
|
|
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
|
}
|
|
|
|
function UI() {
|
|
if (gId("somp").value === "0") {
|
|
gId("mpdiv").style.display = "none";
|
|
resetPanels();
|
|
return;
|
|
}
|
|
gId("mpdiv").style.display = "block";
|
|
}
|
|
|
|
var timeout;
|
|
function showToast(text, error = false)
|
|
{
|
|
var x = gId("toast");
|
|
x.innerHTML = text;
|
|
x.className = error ? "error":"show";
|
|
clearTimeout(timeout);
|
|
x.style.animation = 'none';
|
|
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
|
}
|
|
|
|
function uploadFile(name) {
|
|
var req = new XMLHttpRequest();
|
|
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
|
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
|
req.open("POST", "/upload");
|
|
var formData = new FormData();
|
|
formData.append("data", d.Sf.data.files[0], name);
|
|
req.send(formData);
|
|
d.Sf.data.value = '';
|
|
return false;
|
|
}
|
|
|
|
function addPanels() {
|
|
let c = parseInt(d.Sf.MPC.value);
|
|
let i = gId("panels").children.length;
|
|
if (i<c) for (let j=i; j<c; j++) addPanel(j);
|
|
if (i>c) for (let j=i; j>c; j--) remPanel();
|
|
}
|
|
|
|
function addPanel(i=0) {
|
|
let p = gId("panels");
|
|
if (p.children.length >= maxPanels) return;
|
|
var pw = parseInt(d.Sf.PW.value);
|
|
var ph = parseInt(d.Sf.PH.value);
|
|
let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br>
|
|
1<sup>st</sup> LED: <select name="P${i}B">
|
|
<option value="0">Top</option>
|
|
<option value="1">Bottom</option>
|
|
</select><select name="P${i}R">
|
|
<option value="0">Left</option>
|
|
<option value="1">Right</option>
|
|
</select><br>
|
|
Orientation: <select name="P${i}V">
|
|
<option value="0">Horizontal</option>
|
|
<option value="1">Vertical</option>
|
|
</select><br>
|
|
Serpentine: <input type="checkbox" name="P${i}S"><br>
|
|
Dimensions (WxH): <input name="P${i}W" type="number" min="1" max="128" value="${pw}"> x <input name="P${i}H" type="number" min="1" max="128" value="${ph}"><br>
|
|
Offset X:<input name="P${i}X" type="number" min="0" max="256" value="0">
|
|
Y:<input name="P${i}Y" type="number" min="0" max="256" value="0"><br><i>(offset from top-left corner in # LEDs)</i>
|
|
</div>`;
|
|
p.insertAdjacentHTML("beforeend", b);
|
|
}
|
|
|
|
function remPanel() {
|
|
let p = gId("panels").children;
|
|
var i = p.length;
|
|
if (i <= 1) return;
|
|
p[i-1].remove();
|
|
}
|
|
|
|
function resetPanels() {
|
|
d.Sf.MPC.value = 1;
|
|
let e = gId("panels").children
|
|
for (let i = e.length; i>0; i--) e[i-1].remove();
|
|
}
|
|
/*
|
|
function btnPanel(i) {
|
|
gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
|
|
gId("pnl_rem").style.display = (i>1) ? "inline":"none";
|
|
}
|
|
*/
|
|
function gen() {
|
|
resetPanels();
|
|
|
|
var pansH = parseInt(Sf.MPH.value);
|
|
var pansV = parseInt(Sf.MPV.value);
|
|
var c = pansH*pansV;
|
|
Sf.MPC.value = c; // number of panels
|
|
|
|
var ps = Sf.PS.checked;
|
|
var pv = Sf.PV.value==="1";
|
|
var pb = Sf.PB.value==="1";
|
|
var pr = Sf.PR.value==="1";
|
|
var pw = parseInt(Sf.PW.value);
|
|
var ph = parseInt(Sf.PH.value);
|
|
|
|
var h = pv ? pansV : pansH;
|
|
var v = pv ? pansH : pansV;
|
|
for (let j = 0, p = 0; j < v; j++) {
|
|
for (let i = 0; i < h; i++, p++) {
|
|
if (j*i < maxPanels) addPanel(p);
|
|
var y = (pv?pr:pb) ? v-j-1: j;
|
|
var x = (pv?pb:pr) ? h-i-1 : i;
|
|
x = ps && j%2 ? h-x-1 : x;
|
|
Sf[`P${p}X`].value = (pv?y:x) * pw;
|
|
Sf[`P${p}Y`].value = (pv?x:y) * ph
|
|
Sf[`P${p}W`].value = pw;
|
|
Sf[`P${p}H`].value = ph;
|
|
}
|
|
}
|
|
}
|
|
|
|
function expand(o,i)
|
|
{
|
|
i.style.display = i.style.display!=="none" ? "none" : "";
|
|
o.innerHTML = i.style.display==="none" ? ">" : "V";
|
|
}
|
|
</script>
|
|
<style>@import url("style.css");</style>
|
|
</head>
|
|
<body onload="S()">
|
|
<form id="form_s" name="Sf" method="post">
|
|
<div class="toprow">
|
|
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
|
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
|
</div>
|
|
<h2>2D setup</h2>
|
|
Strip or panel:
|
|
<select id="somp" name="SOMP" onchange="resetPanels();addPanels();UI();" >
|
|
<option value="0">1D Strip</option>
|
|
<option value="1">2D Matrix</option>
|
|
</select><br>
|
|
<div id="mpdiv" style="display:none;">
|
|
<hr class="sml">
|
|
<h3>Matrix Generator <button type="button" id="expGen" onclick="expand(this,gId('mxGen'));">></button></h3>
|
|
<div id="mxGen" style="display:none;">
|
|
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
|
|
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1">
|
|
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br>
|
|
1<sup>st</sup> panel: <select name="PB">
|
|
<option value="0">Top</option>
|
|
<option value="1">Bottom</option>
|
|
</select><select name="PR">
|
|
<option value="0">Left</option>
|
|
<option value="1">Right</option>
|
|
</select><br>
|
|
Orientation: <select name="PV">
|
|
<option value="0">Horizontal</option>
|
|
<option value="1">Vertical</option>
|
|
</select><br>
|
|
Serpentine: <input type="checkbox" name="PS"><br>
|
|
<i style="color:#fa0;">Can populate LED panel layout with pre-arranged matrix.<br>Values from above do not affect final layout.<br>
|
|
WARNING: You will need to update each panel parameters after they are generated.</i><br>
|
|
<button type="button" onclick="gen();expand(gId('expGen'),gId('mxGen'));">Populate</button>
|
|
</div>
|
|
<hr class="sml">
|
|
<h3>Panel set-up</h3>
|
|
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels();UI();"><br>
|
|
<i>A matrix is made of 1 or more physical LED panels.<br>
|
|
Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br>
|
|
<h3>LED panel layout</h3>
|
|
<div id="panels">
|
|
</div>
|
|
<hr class="sml">
|
|
<div id="json" >Gap file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/2d-gaps.json')">Upload</button></div>
|
|
<i>Note: Gap file is a <b>.json</b> file containing an array with number of elements equal to the matrix size.<br>
|
|
A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i>
|
|
</div>
|
|
<hr>
|
|
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
|
</form>
|
|
<div id="toast"></div>
|
|
</body>
|
|
</html>
|