2D setup: add graphical display of panels

FX.h, cfg.cpp, set.cpp, xml.cpp:
- add basic or advanced: bOrA / ba (mpv, mph done earlier)
- add 1st led: panelO / pbl, prl, pvl, psl

settings_2D.htm
- add canvas / context
- addPanel: 1st LED: copy from Matrix setup and draw() on all fields
This commit is contained in:
Ewoud
2023-02-05 16:22:40 +01:00
parent a5607a9901
commit f71e6262f6
12 changed files with 1515 additions and 1191 deletions

View File

@@ -740,9 +740,9 @@ function populateSegments(s)
<option value="1" ${inst.m12==1?' selected':''}>Bar</option>
<option value="2" ${inst.m12==2?' selected':''}>Arc</option>
<option value="3" ${inst.m12==3?' selected':''}>Corner</option>
<option value="4" ${inst.m12==4?' selected':''}>jMap</option>
<option value="5" ${inst.m12==5?' selected':''}>Circle</option>
<option value="6" ${inst.m12==6?' selected':''}>Block</option>
<option value="4" ${inst.m12==4?' selected':''}>jMap</option>
<option value="5" ${inst.m12==5?' selected':''}>Circle</option>
<option value="6" ${inst.m12==6?' selected':''}>Block</option>
</select></div>
</div>`;
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>

View File

@@ -9,6 +9,9 @@
var d=document;
var loc = false, locip;
var maxPanels=64;
var ctx; // WLEDMM
var ledIndex = 0; // WLEDMM
var wasAdvanced = -1; //WLEDMM
function H(){window.open("https://mm.kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);}
@@ -25,6 +28,8 @@
GetV();
UI();
Sf.MPC.setAttribute("max",maxPanels);
baChange(); //WLEDMM: Set Basic or advanced blocks
draw(); //WLEDMM: draw graphics
});
// error event
scE.addEventListener("error", (ev) => {
@@ -43,6 +48,16 @@
}
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
//WLEDMM: add canvas, initialize and set UI
var c = gId("canvas");
c.width = window.innerWidth > 800?800:400; //Mobile gets 400, pc 800
c.height = c.width;
ctx = c.getContext('2d');
// window.requestAnimationFrame(animate);
}
function UI() {
@@ -67,21 +82,21 @@
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">
1<sup>st</sup> LED: <select id="P${i}B" name="P${i}B" oninput="draw()">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="P${i}R">
</select><select id="P${i}R" name="P${i}R" oninput="draw()">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="P${i}V">
Orientation: <select id="P${i}V" name="P${i}V" oninput="draw()">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="P${i}S"><br>
Dimensions (WxH): <input id="P${i}W" name="P${i}W" type="number" min="1" max="128" value="${pw}"> x <input id="P${i}H" name="P${i}H" type="number" min="1" max="128" value="${ph}"><br>
Offset X:<input id="P${i}X" name="P${i}X" type="number" min="0" max="256" value="0">
Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br><i>(offset from top-left corner in # LEDs)</i>
Serpentine: <input id="P${i}S" type="checkbox" name="P${i}S" onclick="draw()"><br>
Dimensions (WxH): <input id="P${i}W" name="P${i}W" type="number" min="1" max="128" value="${pw}" oninput="draw()"> x <input id="P${i}H" name="P${i}H" type="number" min="1" max="128" value="${ph}" oninput="draw()"><br>
Offset X:<input id="P${i}X" name="P${i}X" type="number" min="0" max="256" value="0" oninput="draw()">
Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0" oninput="draw()"><br><i>(offset from top-left corner in # LEDs)</i>
</div>`;
p.insertAdjacentHTML("beforeend", b);
}
@@ -104,6 +119,40 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
gId("pnl_rem").style.display = (i>1) ? "inline":"none";
}
*/
//WLEDMM
function baChange(radioElement=null) {
console.log("baChange", gId("form_s"), radioElement, gId("form_s").elements, d.Sf.BA.value);
if (radioElement) {
if (d.Sf.BA.value == 0 && wasAdvanced == 1) {
if (!confirm('Are you sure to go back to basic (advanced settings will be lost)?')) {
d.Sf.BA.value = 1;
return;
}
fieldChange(); //Create basic layout
}
// console.log("baChange", gId("form_s"), radioElement, gId("form_s").elements, d.Sf.BA.value, radioElement.value);
d.Sf.BA.value = parseInt(radioElement.value);
}
wasAdvanced = d.Sf.BA.value == 1;
gId("advancedBlock").style.display = wasAdvanced ? "inline":"none";
gId("title").innerHTML = !wasAdvanced ? "Matrix Setup":"Matrix Generator";
}
//WLEDMM
function fieldChange(force=false) { //Done on all fields of matrix setup area
console.log("fieldChange", force);
var pansH = parseInt(d.Sf.MPH.value);
var pansV = parseInt(d.Sf.MPV.value);
if ((pansH>0 && pansV>0 && d.Sf.BA.value == 0) || force) gen(); //Generate if basic or forced (in advanced by populate button)
draw();
gId("panelOrientationBlock").style.display = pansH*pansV>1?"inline":"none"; //WLEDMM: panel orientation only needed when more than one panel
gId("popButton").disabled = force;
gId("popButton").style.color= force?"grey":"red";
}
function gen() {
resetPanels();
@@ -128,12 +177,169 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
var x = (pv?pb:pr) ? h-i-1 : i;
x = ps && j%2 ? h-x-1 : x;
gId("P"+p+"X").value = (pv?y:x) * pw;
gId("P"+p+"Y").value = (pv?x:y) * ph
gId("P"+p+"Y").value = (pv?x:y) * ph;
gId("P"+p+"W").value = pw;
gId("P"+p+"H").value = ph;
//WLEDMM: also calculate orientation of panel !
gId("P"+p+"B").value = d.Sf.PBL.value;
gId("P"+p+"R").value = d.Sf.PRL.value;
gId("P"+p+"V").value = d.Sf.PVL.value;
gId("P"+p+"S").checked = d.Sf.PSL.checked;
}
}
}
//WLEDMM
function draw() {
//calc max height and width
var maxWidth = 0;
var maxHeight = 0;
for (let p=0; p<gId("panels").children.length; p++) {
var px = parseInt(gId("P"+p+"X").value); //first led x
var py = parseInt(gId("P"+p+"Y").value); //first led y
var pw = parseInt(gId("P"+p+"W").value); //width
var ph = parseInt(gId("P"+p+"H").value); //height
maxWidth = Math.max(maxWidth, px + pw);
maxHeight = Math.max(maxHeight, py + ph);
}
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.canvas.height = ctx.canvas.width / maxWidth * maxHeight;
var space=20; // space between panels + margin
var ppL = (ctx.canvas.width - space * 2) / maxWidth; //pixels per led
// console.log("dim", ctx.canvas.width , maxWidth, ctx.canvas.height , maxHeight, ppL);
ctx.lineWidth = 1;
ctx.strokeStyle="yellow";
ctx.strokeRect(0, 0, ctx.canvas.width, ctx.canvas.height); // add space between panels
for (let p=0; p<gId("panels").children.length; p++) {
// console.log(gId("P"+p+"X").value, gId("P"+p+"Y").value, gId("P"+p+"W").value, gId("P"+p+"H").value, gId("P"+p+"B").value, gId("P"+p+"R").value, gId("P"+p+"V").value, gId("P"+p+"S").checked);
var px = parseInt(gId("P"+p+"X").value); //first led x
var py = parseInt(gId("P"+p+"Y").value); //first led y
var pw = parseInt(gId("P"+p+"W").value); //width
var ph = parseInt(gId("P"+p+"H").value); //height
var pb = gId("P"+p+"B").value == "1"; //bottom
var pr = gId("P"+p+"R").value == "1"; //right
var pv = gId("P"+p+"V").value == "1"; //vertical
var ps = gId("P"+p+"S").checked; //serpentine
// console.log("panel", p, px,py,pw,ph);
var topLeftX = px*ppL + space; //left margin
var topLeftY = py*ppL + space; //top margin
// console.log("rect", p, topLeftX, topLeftY, pw*ppL, ph*ppL);
ctx.lineWidth = 3;
ctx.strokeStyle="white";
ctx.strokeRect(topLeftX, topLeftY, pw*ppL, ph*ppL); // add space between panels
var lnX;
var lnY;
//find start led
if (pb) //bottom
lnY = topLeftY + ph*ppL - ppL/2;
else //top
lnY = topLeftY + ppL/2;
if (pr) //right
lnX = topLeftX + pw*ppL - ppL/2;
else //left
lnX = topLeftX + ppL/2;
//first led
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
ctx.fill();
//start line
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(lnX, lnY);
var longLineLength = (pv?ph:pw)*ppL - ppL;
for (let ln=0; ln<(pv?pw:ph); ln++) { //loop over panelwidth (or height of vertical?)
var serpLine = ps && ln%2!=0; //serp: turn around if even line
if (pv) //if vertical
lnY += (pb?-1:1) * longLineLength * (serpLine?-1:1); //if vertical change the Y
else
lnX += (pr?-1:1) * longLineLength * (serpLine?-1:1); //if horizontal change the X
ctx.lineTo(lnX, lnY); //draw the long line
if (ln<(pv?pw:ph)-1) { //not the last
//find the small line end point
if (pv) //vertical
lnX += (pr?-1:1) * ppL;
else //horizontal
lnY += (pb?-1:1) * ppL;
//if serpentine go next else go down
if (ps) { //serpentine
ctx.lineTo(lnX, lnY); //draw the serpentine line
} else {
//find the other end of the long line
if (pv) //vertical
lnY += (pb?1:-1) * longLineLength * (serpLine?-1:1); //min as we go back
else //horizontal
lnX += (pr?1:-1) * longLineLength * (serpLine?-1:1);
ctx.moveTo(lnX, lnY); //move to the start point of the next long line
}
}
}
ctx.stroke();
//last led
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
ctx.fill();
ctx.font = '40px Arial';
ctx.fillStyle = "orange";
ctx.fillText(p, topLeftX-ppL/2 + (pw*ppL+ppL)/2, topLeftY-ppL/2 + (ph*ppL+ppL)/2);
}
gId("MD").innerHTML = "Matrix Dimensions (W*H=LC): " + maxWidth + " x " + maxHeight + " = " + maxWidth * maxHeight;
}
// //WLEDMM
// function getCoordOfLed(ledIndex) {
// var pw = parseInt(d.Sf.PW.value);
// var ph = parseInt(d.Sf.PH.value);
// //calculate which panel
// //panelsize
// var p = parseInt(ledIndex / (pw*ph));
// var ledPanelIndex = ledIndex % (pw*ph);
// // console.log(ledIndex, p, ledPanelIndex);
// //calculate which led in panel
// //calculate coordinates
// }
// //WLEDMM
// function animate() {
// // clear previous led
// var pw = parseInt(d.Sf.PW.value);
// var ph = parseInt(d.Sf.PH.value);
// var pansH = parseInt(d.Sf.MPH.value);
// var pansV = parseInt(d.Sf.MPV.value);
// getCoordOfLed(ledIndex);
// ledIndex = (ledIndex + 1) % (pw*pansH*ph*pansV);
// window.requestAnimationFrame(animate);
// }
</script>
<style>@import url("style.css");</style>
</head>
@@ -151,29 +357,51 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
</select><br>
<div id="mpdiv" style="display:none;">
<hr class="sml">
<h3>Matrix Generator</h3>
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">
Interface:
<input type="radio" value="0" name="BA" onclick="baChange(this)"><label>Basic</label>
<input type="radio" value="1" name="BA" onclick="baChange(this)"><label>Advanced</label>
<br>
<hr class="sml">
<h3 id="title">Matrix Generator</h3>
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8" oninput="fieldChange()"> x <input name="PH" type="number" min="1" max="128" value="8" oninput="fieldChange()"><br>
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1" oninput="fieldChange()">
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1" oninput="fieldChange()"><br>
<div id="panelOrientationBlock">
1<sup>st</sup> panel: <select name="PB" oninput="fieldChange()">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="PR">
</select><select name="PR" oninput="fieldChange()">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="PV">
Orientation: <select name="PV" oninput="fieldChange()">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="PS"><br>
<!-- <br>
Interface<br>
<input type="radio" value="0" name="Interface"><label>Classic</label>
<input type="radio" value="1" name="Interface"><label>Complex</label>
<br><br> -->
Serpentine: <input type="checkbox" name="PS" onclick="fieldChange()"><br>
<br>
</div> <!--panelOrientationBlock-->
1<sup>st</sup> LED ☾: <select name="PBL" oninput="fieldChange()">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="PRL" oninput="fieldChange()">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="PVL" oninput="fieldChange()">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="PSL" onclick="fieldChange()"><br>
<br>
<hr class="sml">
<br>
<canvas id="canvas"></canvas><br>
<div id="MD"></div> <!-- Matrix dimensions -->
<br>
<div id="advancedBlock">
<i style="color:#fa0;">Can populate LED panel layout with pre-arranged matrix.<br>These values do not affect final layout.<br>WLEDMM: Populate will overwrite earlier saved panel layouts!</i><br>
<button type="button" onclick="gen()">Populate</button>
<button id="popButton" type="button" onclick="fieldChange(true)">Populate</button>
<hr class="sml">
<h3>Panel set-up</h3>
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels()"><br>
@@ -183,6 +411,7 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
<h3>LED panel layout</h3>
<div id="panels">
</div>
</div> <!--advancedBlock-->
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>