Update.
- allow ledmap selection in UI - upload gap file - expand matrix generator
This commit is contained in:
parent
e51f7bfbff
commit
8dd1f89225
@ -71,9 +71,6 @@ void WS2812FX::setUpMatrix() {
|
||||
customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight];
|
||||
|
||||
if (customMappingTable != nullptr) {
|
||||
int gapSize = 0;
|
||||
int8_t *gapTable = nullptr;
|
||||
|
||||
customMappingSize = Segment::maxWidth * Segment::maxHeight;
|
||||
|
||||
// fill with empty in case we don't fill the entire matrix
|
||||
@ -81,24 +78,32 @@ void WS2812FX::setUpMatrix() {
|
||||
customMappingTable[i] = (uint16_t)-1;
|
||||
}
|
||||
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledgap.json"));
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
// we will try to load a "gap" array (a JSON file)
|
||||
// the array has to have the same amount of values as mapping array (or larger)
|
||||
// "gap" array is used while building ledmap (mapping array)
|
||||
// and discarded afterwards as it has no meaning after the process
|
||||
// content of the file is just raw JSON array in the form of [val1,val2,val3,...]
|
||||
// there are no other "key":"value" pairs in it
|
||||
// allowed values are: -1 (missing pixel/no LED attached), 0 (inactive/unused pixel), 1 (active/used pixel)
|
||||
char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
int gapSize = 0;
|
||||
int8_t *gapTable = nullptr;
|
||||
|
||||
if (isFile && requestJSONBufferLock(20)) {
|
||||
DEBUG_PRINT(F("Reading LED gap from "));
|
||||
DEBUG_PRINTLN(fileName);
|
||||
|
||||
// read the array into global JSON buffer
|
||||
if (readObjectFromFile(fileName, nullptr, &doc)) {
|
||||
// the array is similar to ledmap, except it has only 3 values:
|
||||
// -1 ... missing pixel (do not increase pixel count)
|
||||
// 0 ... inactive pixel (it does count, but should be mapped out)
|
||||
// 1 ... active pixel
|
||||
// 0 ... inactive pixel (it does count, but should be mapped out (-1))
|
||||
// 1 ... active pixel (it will count and will be mapped)
|
||||
JsonArray map = doc.as<JsonArray>();
|
||||
gapSize = map.size();
|
||||
if (!map.isNull() && gapSize >= customMappingSize) { // not an empty map
|
||||
if (!map.isNull() && gapSize >= customMappingSize) { // not an empty map
|
||||
gapTable = new int8_t[gapSize];
|
||||
for (size_t i = 0; i < gapSize; i++) {
|
||||
if (gapTable) for (size_t i = 0; i < gapSize; i++) {
|
||||
gapTable[i] = constrain(map[i], -1, 1);
|
||||
}
|
||||
}
|
||||
@ -117,12 +122,13 @@ void WS2812FX::setUpMatrix() {
|
||||
x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i;
|
||||
x = p.serpentine && j%2 ? h-x-1 : x;
|
||||
size_t index = (p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x);
|
||||
if (!gapTable || (gapTable && gapTable[index] > 0)) customMappingTable[index] = pix;
|
||||
if (!gapTable || (gapTable && gapTable[index] >= 0)) pix++;
|
||||
if (!gapTable || (gapTable && gapTable[index] > 0)) customMappingTable[index] = pix; // a useful pixel (otherwise -1 is retained)
|
||||
if (!gapTable || (gapTable && gapTable[index] >= 0)) pix++; // not a missing pixel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete gap array as we no longer need it
|
||||
if (gapTable) delete[] gapTable;
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
|
@ -1584,7 +1584,7 @@ void WS2812FX::loadCustomPalettes() {
|
||||
|
||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
||||
void WS2812FX::deserializeMap(uint8_t n) {
|
||||
//if (isMatrix) return; // 2D support creates its own ledmap
|
||||
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
|
||||
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
|
@ -808,7 +808,7 @@ function populateSegments(s)
|
||||
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
|
||||
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
|
||||
|
||||
if (!isM && Array.isArray(li.maps) && li.maps.length>1) {
|
||||
if (Array.isArray(li.maps) && li.maps.length>1) {
|
||||
let cont = `Ledmap: <select class="sel-sg" onchange="requestJson({'ledmap':parseInt(this.value)})"><option value="" selected>Unchanged</option>`;
|
||||
for (const k of (li.maps||[])) cont += `<option value="${k}">${k==0?'Default':'ledmap'+k+'.json'}</option>`;
|
||||
cont += "</select></div>";
|
||||
@ -1875,7 +1875,7 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
|
||||
<input type="checkbox" id="p${i}sbchk">
|
||||
<span class="checkmark"></span>
|
||||
</label>`;
|
||||
if (!isM && Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
|
||||
if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
|
||||
content += `<div class="lbl-l">Ledmap: <div class="sel-p"><select class="sel-p" id="p${i}lmp"><option value="">Unchanged</option>`;
|
||||
for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k==0?'Default':'ledmap'+k+'.json'}</option>`;
|
||||
content += "</select></div></div>";
|
||||
|
@ -54,6 +54,29 @@
|
||||
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;
|
||||
@ -79,9 +102,9 @@ Orientation: <select name="P${i}V">
|
||||
<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>
|
||||
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);
|
||||
}
|
||||
@ -107,17 +130,17 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
||||
function gen() {
|
||||
resetPanels();
|
||||
|
||||
var pansH = parseInt(d.Sf.MPH.value);
|
||||
var pansV = parseInt(d.Sf.MPV.value);
|
||||
var pansH = parseInt(Sf.MPH.value);
|
||||
var pansV = parseInt(Sf.MPV.value);
|
||||
var c = pansH*pansV;
|
||||
d.Sf.MPC.value = c; // number of panels
|
||||
Sf.MPC.value = c; // number of panels
|
||||
|
||||
var ps = d.Sf.PS.checked;
|
||||
var pv = d.Sf.PV.value==="1";
|
||||
var pb = d.Sf.PB.value==="1";
|
||||
var pr = d.Sf.PR.value==="1";
|
||||
var pw = parseInt(d.Sf.PW.value);
|
||||
var ph = parseInt(d.Sf.PH.value);
|
||||
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;
|
||||
@ -127,13 +150,19 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
||||
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;
|
||||
gId("P"+p+"X").value = (pv?y:x) * pw;
|
||||
gId("P"+p+"Y").value = (pv?x:y) * ph
|
||||
gId("P"+p+"W").value = pw;
|
||||
gId("P"+p+"H").value = ph;
|
||||
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>
|
||||
@ -151,36 +180,43 @@ 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">
|
||||
<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>These values do not affect final layout.</i><br>
|
||||
<button type="button" onclick="gen()">Populate</button>
|
||||
<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()"><br>
|
||||
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>
|
||||
<!--Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<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>
|
||||
|
File diff suppressed because it is too large
Load Diff
1490
wled00/html_ui.h
1490
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user