Merge branch 'main' into feature

This commit is contained in:
Blaž Kristan 2023-05-30 16:20:20 +02:00 committed by GitHub
commit 69ab2ce402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 117 additions and 72 deletions

View File

@ -353,7 +353,8 @@ typedef struct Segment {
bool mirror_y : 1; // 8 : mirrored Y (2D)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
uint8_t soundSim : 3; // 13-15 : 0-7 sound simulation types
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
};
};
uint8_t grouping, spacing;

View File

@ -474,7 +474,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1);
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
@ -750,7 +750,7 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: (msb first) sound:3, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
//bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;

View File

@ -174,37 +174,62 @@ button {
}
.slider-icon {
/*transform: translate(3px,3px);*/
position: absolute;
left: 8px;
bottom: 5px;
}
.e-icon {
transform: translateY(3px);
}
.sel-icon {
transform: translateX(3px);
}
.e-icon, .sel-icon, .slider-icon {
.e-icon, .g-icon, .sel-icon, .slider-icon {
cursor: pointer;
color: var(--c-d);
}
.g-icon {
font-style: normal;
position: absolute;
top: 8px;
right: 8px;
}
/* pop-up container */
.pop {
position: absolute;
display: inline-block;
top: 0;
right: 0;
}
/* pop-up content (segment sets) */
.pop-c {
position: absolute;
background-color: var(--c-2);
border: 1px solid var(--c-8);
border-radius: 20px;
z-index: 1;
top: 3px;
right: 35px;
padding: 3px 8px 1px;
font-size: 24px;
line-height: 24px;
}
.pop-c span {
padding: 2px 6px;
}
.search-icon {
position: absolute;
top: 8px;
left: 12px;
/*pointer-events: none;*/
width: 24px;
height: 24px;
}
.clear-icon {
position: absolute;
display: none;
top: 8px;
right: 9px;
cursor: pointer;
@ -232,14 +257,12 @@ button {
#liveview {
height: 4px;
display: none;
width: 100%;
border: 0px;
}
#liveview2D {
height: 90%;
display: none;
width: 90%;
border: 0px;
position: absolute;
@ -482,13 +505,6 @@ button {
opacity: 1;
}
#pql, .edit-icon {
display: none;
}
.hide {
display: none !important;
}
.fade {
visibility: hidden; /* hide it */
opacity: 0; /* make it transparent */
@ -567,7 +583,6 @@ button {
position: fixed;
top: calc(var(--th) + 5px);
left: 1px;
display: none;
cursor: pointer;
}
@ -662,7 +677,9 @@ img {
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
/* wrapper divs hidden by default */
#rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw {
#liveview, #liveview2D, #roverstar, #pql
#rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw,
.clear-icon, .edit-icon, .ptxt {
display: none;
}
@ -771,8 +788,8 @@ input[type=range]::-moz-range-thumb {
-webkit-transform:translate3d(0,0,0);
backface-visibility: hidden;
transform:translate3d(0,0,0);
overflow: clip;
text-overflow: clip;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid var(--c-3);
background-color: var(--c-3);
}
@ -803,6 +820,7 @@ input[type=range]::-moz-range-thumb {
.btn-xs, .btn-pl-del, .btn-pl-add {
width: 42px !important;
height: 42px !important;
text-overflow: clip;
}
.btn-xs {
margin: 2px 0 0 0;
@ -980,7 +998,6 @@ textarea {
.ptxt {
margin: -1px 4px 8px !important;
display: none;
}
.stxt {
@ -990,7 +1007,7 @@ textarea {
.segname, .pname {
white-space: nowrap;
text-align: center;
overflow: clip;
overflow: hidden;
text-overflow: ellipsis;
line-height: 24px;
padding: 8px 24px;
@ -1048,11 +1065,9 @@ textarea {
#csl .xxs {
border: 2px solid var(--c-d) !important;
/*box-shadow: 0 0 0 2px var(--c-d);*/
}
#csl .xxs-w {
border-width: 5px !important;
/*box-shadow: 0 0 0 5px var(--c-d);*/
}
.qcs, #namelabel { /* text shadow for name to be legible on grey backround */
@ -1066,7 +1081,6 @@ textarea {
.pwr {
color: var(--c-6);
transform: translate(1px, 1px);
cursor: pointer;
}
@ -1081,18 +1095,13 @@ textarea {
}
.frz {
left: 32px;
left: 10px;
position: absolute;
top: -3px;
top: 8px;
cursor: pointer;
padding: 8px;
z-index: 1;
}
.expanded .frz {
display: none;
}
/* radiobuttons and checkmarks */
.check, .radio {
display: block;
@ -1234,7 +1243,6 @@ TD .checkmark, TD .radiomark {
.lbl-s {
display: inline-block;
/* margin: 10px 4px 0 0; */
margin-top: 6px;
font-size: 13px;
width: 48%;
@ -1284,10 +1292,6 @@ TD .checkmark, TD .radiomark {
background-color: var(--c-3);
}
/*.selected .radiomark {
border: 1px solid var(--c-3);
}*/
/* selected list item */
.lstI.selected {
top: 0;
@ -1405,7 +1409,7 @@ TD .checkmark, TD .radiomark {
.expanded {
display: inline-block !important;
}
.expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide {
.hide, .expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide, .expanded .frz, .expanded .g-icon {
display: none !important;
}
@ -1541,11 +1545,7 @@ TD .checkmark, TD .radiomark {
left: 12px;
}
.segname {
padding: 8px 16px;
max-width: 140px;
}
.segname, .pname {
max-width: 134px;
max-width: calc(100% - 110px);
}
.segt TD {
padding: 0 !important;

View File

@ -702,6 +702,14 @@ function populateSegments(s)
let sg = gId(`seg${i}`);
let exp = sg ? (sg.classList.contains('expanded') || (i===0 && cfg.comp.segexp)) : false;
// segment set icon color
let cG = "var(--c-b)";
switch (inst.set) {
case 1: cG = "var(--c-r)"; break;
case 2: cG = "var(--c-g)"; break;
case 3: cG = "var(--c-l)"; break;
}
let segp = `<div id="segp${i}" class="sbs">`+
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>`+
`<div class="sliderwrap il">`+
@ -713,10 +721,11 @@ function populateSegments(s)
let stoX = inst.stop;
let staY = inst.startY;
let stoY = inst.stopY;
let isMSeg = isM && staX<mw*mh; // 2D matrix segment
let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark"></span></label>`;
let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark"></span></label>`;
let rvYck = "", miYck ="";
if (isM && staX<mw*mh) {
if (isMSeg) {
rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark"></span></label>`;
miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark"></span></label>`;
}
@ -732,18 +741,20 @@ function populateSegments(s)
`<div class="sel-p"><select class="sel-p" id="seg${i}si" onchange="setSi(${i})">`+
`<option value="0" ${inst.si==0?' selected':''}>BeatSin</option>`+
`<option value="1" ${inst.si==1?' selected':''}>WeWillRockYou</option>`+
`<option value="2" ${inst.si==2?' selected':''}>U10_3</option>`+
`<option value="3" ${inst.si==3?' selected':''}>U14_3</option>`+
`</select></div>`+
`</div>`;
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}">`+
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}" data-set="${inst.set}">`+
`<label class="check schkl">`+
`<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>`+
`<span class="checkmark"></span>`+
`</label>`+
`<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
`<div class="segname" onclick="selSegEx(${i})">`+
`<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
(inst.n ? inst.n : "Segment "+i) +
`<div class="pop hide" onclick="event.preventDefault();event.stopPropagation();">`+
`<i class="icons g-icon" style="color:${cG};" onclick="this.nextElementSibling.classList.toggle('hide');">&#x278${String.fromCharCode(inst.set+"A".charCodeAt(0))};</i>`+
`<div class="pop-c hide"><span style="color:var(--c-f);" onclick="setGrp(${i},0);">&#x278A;</span><span style="color:var(--c-r);" onclick="setGrp(${i},1);">&#x278B;</span><span style="color:var(--c-g);" onclick="setGrp(${i},2);">&#x278C;</span><span style="color:var(--c-l);" onclick="setGrp(${i},3);">&#x278D;</span></div>`+
`</div> `+
`<i class="icons edit-icon flr" id="seg${i}nedit" onclick="tglSegn(${i})">&#xe2c6;</i>`+
`</div>`+
`<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>`+
@ -752,16 +763,16 @@ function populateSegments(s)
`<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>`+
`<table class="infot segt">`+
`<tr>`+
`<td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td>`+
`<td>${isM&&staX<mw*mh?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>`+
`<td>${isM&&staX<mw*mh?'':'Offset'}</td>`+
`<td>${isMSeg?'Start X':'Start LED'}</td>`+
`<td>${isMSeg?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>`+
`<td>${isMSeg?'':'Offset'}</td>`+
`</tr>`+
`<tr>`+
`<td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>`+
`<td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isMSeg?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isMSeg?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td ${isMSeg?'style="text-align:revert;"':''}>${isMSeg?miXck+'<br>'+rvXck:''}<input class="segn ${isMSeg?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>`+
`</tr>`+
(isM && staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
(isMSeg ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+
'<td><input class="segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
@ -775,15 +786,15 @@ function populateSegments(s)
`<tr>`+
`<td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>`+
`<td><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>`+
`</tr>`+
`</table>`+
`<div class="h bp" id="seg${i}len"></div>`+
(!(isM&&staX<mw*mh) ? rvXck : '') +
(isM&&staX<mw*mh&&stoY-staY>1&&stoX-staX>1 ? map2D : '') +
(!isMSeg ? rvXck : '') +
(isMSeg&&stoY-staY>1&&stoX-staX>1 ? map2D : '') +
(s.AudioReactive && s.AudioReactive.on ? "" : sndSim) +
`<label class="check revchkl" id="seg${i}lbtm">`+
(isM&&staX<mw*mh?'Transpose':'Mirror effect') + (isM&&staX<mw*mh ?
(isMSeg?'Transpose':'Mirror effect') + (isMSeg ?
'<input type="checkbox" id="seg'+i+'tp" onchange="setTp('+i+')" '+(inst.tp?"checked":"")+'>':
'<input type="checkbox" id="seg'+i+'mi" onchange="setMi('+i+')" '+(inst.mi?"checked":"")+'>') +
`<span class="checkmark"></span>`+
@ -1368,6 +1379,8 @@ function readState(s,command=false)
return true;
}
if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");});
var cd = gId('csl').children;
for (let e = cd.length-1; e >= 0; e--) {
cd[e].dataset.r = i.col[e][0];
@ -1749,9 +1762,13 @@ function makeSeg()
function resetUtil(off=false)
{
gId('segutil').innerHTML = `<div class="seg btn btn-s ${off?'off':''}" style="border-radius:24px;padding:0;">`
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;">`
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon">&#xe18a;</i>Add segment</div></div>`;
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon">&#xe18a;</i>Add segment</div>`
+ '<div class="pop hide" onclick="event.stopPropagation();">'
+ `<i class="icons g-icon" onclick="this.nextElementSibling.classList.toggle('hide');">&#xE34B;</i>`
+ '<div class="pop-c hide"><span style="color:var(--c-f);" onclick="selGrp(0);">&#x278A;</span><span style="color:var(--c-r);" onclick="selGrp(1);">&#x278B;</span><span style="color:var(--c-g);" onclick="selGrp(2);">&#x278C;</span><span style="color:var(--c-l);" onclick="selGrp(3);">&#x278D;</span></div>'
+ '</div></div>';
}
function makePlSel(el, incPl=false)
@ -2037,6 +2054,20 @@ function selSeg(s)
requestJson(obj);
}
function selGrp(g)
{
event.preventDefault();
event.stopPropagation();
var sel = gId(`segcont`).querySelectorAll(`div[data-set="${g}"]`);
var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":false});
if (sel) for (let s of sel||[]) {
let i = parseInt(s.id.substring(3));
obj.seg[i] = {"id":i,"sel":true};
}
if (obj.seg.length) requestJson(obj);
}
function rptSeg(s)
{
//TODO: 2D support
@ -2157,6 +2188,14 @@ function setTp(s)
requestJson(obj);
}
function setGrp(s, g)
{
event.preventDefault();
event.stopPropagation();
var obj = {"seg": {"id": s, "set": g}};
requestJson(obj);
}
function setSegPwr(s)
{
var pwr = gId(`seg${s}pwr`).classList.contains('act');

View File

@ -93,7 +93,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = constrain(map1D2D, 0, 7);
seg.soundSim = constrain(soundSim, 0, 7);
seg.soundSim = constrain(soundSim, 0, 1);
uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3);
uint16_t len = 1;
if (stop > start) len = stop - start;
@ -477,6 +480,7 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct;
root[F("set")] = seg.set;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer

View File

@ -390,11 +390,12 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
// Begin simulateSound (to enable audio enhanced effects to display something)
///////////////////////////////////////////////////////////////////////////////
// Currently 4 types defined, to be fine tuned and new types added
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0,
UMS_WeWillRockYou,
UMS_10_3,
UMS_14_3
UMS_WeWillRockYou
//UMS_10_13,
//UMS_14_3
} um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId)
@ -479,7 +480,7 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0;
}
break;
case UMS_10_3:
/*case UMS_10_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
@ -488,7 +489,7 @@ um_data_t* simulateSound(uint8_t simulationId)
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;
break;*/
}
samplePeak = random8() > 250;