Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Individual Start Nodes to Circle and Star #4554

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions xLights/controllers/ControllerUploadData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,22 @@ UDControllerPortModel::UDControllerPortModel(Model* m, Controller* controller, O
_protocol = _model->GetControllerProtocol();
_smartRemote = _model->GetSmartRemoteForString(string+1);
_smartRemoteType = _model->GetSmartRemoteType();

if (string == -1) {
_startChannel = _model->GetNumberFromChannelString(_model->ModelStartChannel);
_endChannel = _startChannel + _model->GetChanCount() - 1;
}
else {
_startChannel = _model->GetStringStartChan(string) + 1;
_endChannel = _startChannel + _model->NodesPerString(string) * _model->GetChanCountPerNode() - 1;
if (m->GetDisplayAs() == "Custom") {
_endChannel = _startChannel + _model->NodesPerString(string) * _model->GetChanCountPerNode() - 1;
} else {
if ((string + 1) == _model->GetParm1()) { // last custom string; zero indexed vs parm1
_endChannel = _model->GetLastChannel() + 1;
} else {
_endChannel = _model->GetStringStartChan(string + 1);
}
}
}

Output* o = nullptr;
if (controller != nullptr) {
o = controller->GetOutput(_startChannel, _universeStartChannel);
Expand Down
171 changes: 151 additions & 20 deletions xLights/models/CircleModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ void CircleModel::InitModel()
if (GetLayerSizeCount() == 1) {
SetLayerSize(0, parm1 * parm2);
}
totalNodes = (int)GetLayerSizesTotalNodes();

if (ModelXml->HasAttribute("InsideOut")) {
insideOut = wxAtoi(ModelXml->GetAttribute("InsideOut"));
Expand All @@ -102,34 +103,79 @@ static wxPGChoices CIRCLE_START_LOCATION(wxArrayString(8, CIRCLE_START_LOCATION_

void CircleModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* outputManager)
{

wxPGProperty *p = grid->Append(new wxUIntProperty("# Strings", "CircleStringCount", parm1));
p->SetAttribute("Min", 1);
p->SetAttribute("Max", 100);
p->SetEditor("SpinCtrl");
p->SetHelpString("This is typically the number of connections from the prop to your controller.");
wxPGProperty* p;
AddLayerSizeProperty(grid);
p = grid->Append(new wxStringProperty("Total Nodes", "TotalNodes", wxString::Format("%d", totalNodes)));
p->SetHelpString("Total Nodes for this model");
p->SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
p->ChangeFlag(wxPGPropertyFlags::ReadOnly, true);

if (SingleNode) {
p = grid->Append(new wxUIntProperty("Lights/String", "CircleLightCount", parm2));
p->SetAttribute("Min", 1);
p->SetAttribute("Max", 2000);
p->SetEditor("SpinCtrl");
}
else {
p->SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
p->ChangeFlag(wxPGPropertyFlags::Hidden, true);
} else {
p = grid->Append(new wxUIntProperty("Nodes/String", "CircleLightCount", parm2));
p->SetAttribute("Min", 1);
p->SetAttribute("Max", 2000);
p->SetEditor("SpinCtrl");
p->SetHelpString("This is typically the total number of pixels per #String.");
p->SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
p->ChangeFlag(wxPGPropertyFlags::Hidden, true);
}

p = grid->Append(new wxUIntProperty("Center %", "CircleCenterPercent", parm3));
p->SetAttribute("Min", 0);
p->SetAttribute("Max", 100);
p->SetEditor("SpinCtrl");

AddLayerSizeProperty(grid);
p = grid->Append(new wxUIntProperty("# Strings", "CircleStringCount", parm1));
p->SetAttribute("Min", 1);
p->SetAttribute("Max", 100);
p->SetEditor("SpinCtrl");
p->SetHelpString("This is typically the number of connections from the prop to your controller.");

if (parm1 == 1) {
// cant set start node
} else {
wxString nm = StartNodeAttrName(0);
bool hasIndiv = ModelXml->HasAttribute(nm);

p = grid->Append(new wxBoolProperty("Indiv Start Nodes", "ModelIndividualStartNodes", hasIndiv));
p->SetAttribute("UseCheckbox", true);

wxPGProperty* psn = grid->AppendIn(p, new wxUIntProperty(nm, nm, wxAtoi(ModelXml->GetAttribute(nm, "1"))));
psn->SetAttribute("Min", 1);
psn->SetAttribute("Max", (int)GetNodeCount());
psn->SetEditor("SpinCtrl");

if (hasIndiv) {
int c = parm1;
for (int x = 0; x < c; ++x) {
nm = StartNodeAttrName(x);
std::string val = ModelXml->GetAttribute(nm, "").ToStdString();
if (val.empty()) {
val = ComputeStringStartNode(x);
ModelXml->DeleteAttribute(nm);
ModelXml->AddAttribute(nm, val);
}
int v = wxAtoi(val);
if (v < 1) v = 1;
if (v > NodesPerString()) v = NodesPerString();
if (x == 0) {
psn->SetValue(v);
} else {
grid->AppendIn(p, new wxUIntProperty(nm, nm, v));
}
}
} else {
psn->Enable(false);
}
}

int start = IsLtoR ? 1 : 0;
if (insideOut) {
start += 2;
Expand All @@ -143,9 +189,24 @@ void CircleModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager
int CircleModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGridEvent& event)
{
if ("CircleStringCount" == event.GetPropertyName()) {
int prvVal = wxAtoi(ModelXml->GetAttribute("parm1")); //we never removed old custom strings.. now we do if/when you reduce the ports
int newVal = event.GetPropertyValue().GetLong();
if (newVal < prvVal) {
for (int i = newVal; i < prvVal; i++) {
wxString nm = StartNodeAttrName(i);
ModelXml->DeleteAttribute(nm);
}
}
ModelXml->DeleteAttribute("parm1");
ModelXml->AddAttribute("parm1", wxString::Format("%d", (int)event.GetPropertyValue().GetLong()));
//AdjustStringProperties(grid, parm1);
ModelXml->AddAttribute("parm1", wxString::Format("%d", newVal));
ModelXml->DeleteAttribute("parm2");
ModelXml->AddAttribute("parm2", wxString::Format("%d", (int)GetLayerSizesTotalNodes() / newVal));
if (ModelXml->HasAttribute("CustomStrings")) {
for (int x = 0; x < parm1; x++) {
ModelXml->DeleteAttribute(StartNodeAttrName(x));
}
ModelXml->DeleteAttribute("CustomStrings");
}
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "CircleModel::OnPropertyGridChange::CircleStringCount");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "CircleModel::OnPropertyGridChange::CircleStringCount");
Expand Down Expand Up @@ -183,18 +244,55 @@ int CircleModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyG
ModelXml->DeleteAttribute("Dir");
ModelXml->DeleteAttribute("StartSide");
ModelXml->DeleteAttribute("InsideOut");

int v = event.GetValue().GetLong();
ModelXml->AddAttribute("Dir", v & 0x1 ? "L" : "R");
ModelXml->AddAttribute("StartSide", v < 4 ? "T" : "B");
ModelXml->AddAttribute("InsideOut", v & 0x2 ? "1" : "0");

IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "CircleModel::OnPropertyGridChange::CircleStart");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "CircleModel::OnPropertyGridChange::CircleStart");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "CircleModel::OnPropertyGridChange::CircleStart");
AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "CircleModel::OnPropertyGridChange::CircleLayerSizes");
return 0;
} else if (event.GetPropertyName() == "ModelIndividualStartNodes") {
for (int x = 0; x < parm1; x++) {
wxString nm = StartNodeAttrName(x);
ModelXml->DeleteAttribute(nm);
}
for (int x = 0; x < parm1; x++) {
wxString nm = StartNodeAttrName(x);
ModelXml->AddAttribute(nm, ComputeStringStartNode(x));
}
if (ModelXml->HasAttribute("CustomStrings")) {
ModelXml->DeleteAttribute("CustomStrings");
} else {
ModelXml->AddAttribute("CustomStrings", wxString::Format("%d", parm1));
}
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes");
return 0;
} else if (event.GetPropertyName().StartsWith("ModelIndividualStartNodes.Strings")) {
wxString s = event.GetPropertyName().substr(strlen("ModelIndividualStartNodes.Strings"));
int string = wxAtoi(s);
wxString nm = StartNodeAttrName(string - 1);
int value = event.GetValue().GetInteger();
if (value < 1) value = 1;
if (value > NodesPerString()) value = NodesPerString();
ModelXml->DeleteAttribute(nm);
ModelXml->AddAttribute(nm, wxString::Format("%d", value));
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "CircleModel::OnPropertyGridChange::ModelIndividualStartNodes2");
return 0;
}

return Model::OnPropertyGridChange(grid, event);
Expand All @@ -213,7 +311,7 @@ int CircleModel::maxSize() {
void CircleModel::InitCircle()
{
int maxLights = 0;
int numLights = parm1 * parm2;
int numLights = totalNodes;
int cnt = 0;

if (GetLayerSizeCount() == 0) {
Expand All @@ -236,7 +334,8 @@ void CircleModel::InitCircle()
}
}

SetNodeCount(parm1, parm2, rgbOrder);
SetNodeCount(1, totalNodes, rgbOrder);

SetBufferSize(GetLayerSizeCount(), maxLights);
int LastStringNum = -1;
int chan = 0;
Expand Down Expand Up @@ -277,6 +376,18 @@ void CircleModel::InitCircle()
}
nodesToMap -= loop_count;
}

if (GetParm1() > 1) { //this is only needed to get a visialisation of the strings in NodeView. Can easily be ignored.
Nodes[0]->StringNum = 0;
std::vector<int> result(stringStartChan.size(), -1);
result = stringStartChan;
std::transform(result.begin(), result.end(), result.begin(), [](int x) { return std::max(x - 1,0); });
result.push_back(Nodes[Nodes.size() - 1]->ActChan + 1);
for (int i = 1; i < Nodes.size(); ++i) {
auto it = std::lower_bound(result.begin(), result.end(), Nodes[i]->ActChan);
if (it != result.end()) Nodes[i]->StringNum = std::distance(result.begin(), it)-1;
}
}
}

// Set screen coordinates for circles
Expand Down Expand Up @@ -436,12 +547,11 @@ void CircleModel::ImportXlightsModel(wxXmlNode* root, xLightsFrame* xlights, flo
}
}

void CircleModel::OnLayerSizesChange(bool countChanged)
{
// if string count is 1 then adjust nodes per string to match sum of nodes
if (parm1 == 1 && GetLayerSizeCount() > 0) {
void CircleModel::OnLayerSizesChange(bool countChanged) {
if (GetLayerSizeCount() > 0) {
ModelXml->DeleteAttribute("parm2");
ModelXml->AddAttribute("parm2", wxString::Format("%d", (int)GetLayerSizesTotalNodes()));
ModelXml->AddAttribute("parm2", wxString::Format("%d", (int)GetLayerSizesTotalNodes()/parm1));
totalNodes = (int)GetLayerSizesTotalNodes();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "CircleModel::OnLayerSizesChange");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "CircleModel::OnLayerSizesChange");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "CircleModel::OnLayerSizesChange");
Expand All @@ -452,3 +562,24 @@ void CircleModel::OnLayerSizesChange(bool countChanged)
AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "CircleModel::OnLayerSizesChange");
}
}

std::string CircleModel::ComputeStringStartNode(int x) const {
if (x == 0) return "1";

int strings = GetNumPhysicalStrings();
int nodes = GetNodeCount();
float nodesPerString = (float)nodes / (float)strings;

return std::to_string((int)(x * nodesPerString + 1));
}

int CircleModel::NodesPerString() const {
int nodes = GetChanCount() / std::max(GetChanCountPerNode(), 1);
int ts = GetSmartTs();

if (ts <= 1) {
return nodes;
} else {
return nodes * ts;
}
}
9 changes: 8 additions & 1 deletion xLights/models/CircleModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class CircleModel : public ModelWithScreenLocation<BoxedScreenLocation>
virtual bool ModelSupportsLayerSizes() const override { return true; }
virtual void OnLayerSizesChange(bool countChanged) override;

virtual int NodesPerString() const override;
bool SupportsChangingStringCount() const override { return true; };

protected:
virtual void InitModel() override;

Expand All @@ -45,4 +48,8 @@ class CircleModel : public ModelWithScreenLocation<BoxedScreenLocation>
int maxSize();

bool insideOut = false;
};

int totalNodes = 0;
static std::string StartNodeAttrName(int idx) { return wxString::Format(wxT("Strings%i"), idx + 1).ToStdString(); }
std::string ComputeStringStartNode(int x) const;
};
50 changes: 34 additions & 16 deletions xLights/models/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3099,7 +3099,21 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
size_t NumberOfStrings = HasOneString(DisplayAs) ? 1 : parm1;
int ChannelsPerString = CalcCannelsPerString();

SetStringStartChannels(zeroBased, NumberOfStrings, StartChannel, ChannelsPerString);
if (ModelNode->HasAttribute("CustomStrings") && ModelNode->HasAttribute("Strings1")) {
std::vector<int> customStartNodes{};
for (auto i = 1; i <= NumberOfStrings; i++) {
if (i == 1) {
customStartNodes.push_back(StartChannel - 1);
} else if (wxAtoi(ModelNode->GetAttribute(wxString::Format(wxT("Strings%i"), i).ToStdString(), "-1")) == -1 ) {
customStartNodes.push_back(wxAtoi(ModelNode->GetAttribute("parm2", "1")) * (i-1) * 3 + StartChannel - 1);
} else {
customStartNodes.push_back((wxAtoi(ModelNode->GetAttribute(wxString::Format(wxT("Strings%i"), i).ToStdString(), "1"))-1) * 3 + StartChannel-1);
}
}
SetStringStartChannels(zeroBased, customStartNodes);
} else {
SetStringStartChannels(zeroBased, NumberOfStrings, StartChannel, ChannelsPerString);
}
GetModelScreenLocation().Read(ModelNode);

InitModel();
Expand Down Expand Up @@ -3353,6 +3367,12 @@ void Model::SetStringStartChannels(bool zeroBased, int NumberOfStrings, int Star
}
}

void Model::SetStringStartChannels(int StartChannel, std::vector<int> customStartNodes) {
stringStartChan.clear();
stringStartChan.resize(customStartNodes.size());
stringStartChan = customStartNodes;
}

int Model::FindNodeAtXY(int bufx, int bufy)
{
for (int i = 0; i < Nodes.size(); ++i) {
Expand Down Expand Up @@ -3581,7 +3601,7 @@ std::string Model::GetFirstChannelInStartChannelFormat(OutputManager* outputMana
}

uint32_t Model::GetLastChannel() const
{
{
uint32_t LastChan = 0;
size_t NodeCount = GetNodeCount();
for (size_t idx = 0; idx < NodeCount; ++idx) {
Expand Down Expand Up @@ -4377,20 +4397,18 @@ void Model::AddLayerSizeProperty(wxPropertyGridInterface* grid)
psn->SetAttribute("Max", 100);
psn->SetEditor("SpinCtrl");

if (GetLayerSizeCount() > 1) {
for (int i = 0; i < GetLayerSizeCount(); ++i) {
wxString id = wxString::Format("Layer%d", i);
wxString nm = wxString::Format("Layer %d", i + 1);
if (i == 0)
nm = "Inside";
else if (i == GetLayerSizeCount() - 1)
nm = "Outside";

wxPGProperty* pls = grid->AppendIn(psn, new wxUIntProperty(nm, id, GetLayerSize(i)));
pls->SetAttribute("Min", 1);
pls->SetAttribute("Max", 1000);
pls->SetEditor("SpinCtrl");
}
for (int i = 0; i < GetLayerSizeCount(); ++i) { //Always show Layers since we're using this to get the node counts
wxString id = wxString::Format("Layer%d", i);
wxString nm = wxString::Format("Layer %d", i + 1);
if (i == 0)
nm = "Inside";
else if (i == GetLayerSizeCount() - 1)
nm = "Outside";

wxPGProperty* pls = grid->AppendIn(psn, new wxUIntProperty(nm, id, GetLayerSize(i)));
pls->SetAttribute("Min", 1);
pls->SetAttribute("Max", 1000);
pls->SetEditor("SpinCtrl");
}
}

Expand Down
1 change: 1 addition & 0 deletions xLights/models/Model.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ class Model : public BaseObject
virtual void InitModel();
virtual int CalcCannelsPerString();
virtual void SetStringStartChannels(bool zeroBased, int NumberOfStrings, int StartChannel, int ChannelsPerString);
virtual void SetStringStartChannels(int StartChannel, std::vector<int> customStartNodes);
void RecalcStartChannels();

void SetBufferSize(int NewHt, int NewWi);
Expand Down
Loading
Loading