diff --git a/xLights/controllers/ControllerUploadData.cpp b/xLights/controllers/ControllerUploadData.cpp index 178b5de878..6ea6f69818 100644 --- a/xLights/controllers/ControllerUploadData.cpp +++ b/xLights/controllers/ControllerUploadData.cpp @@ -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); diff --git a/xLights/models/CircleModel.cpp b/xLights/models/CircleModel.cpp index 59f9026720..381052b5df 100644 --- a/xLights/models/CircleModel.cpp +++ b/xLights/models/CircleModel.cpp @@ -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")); @@ -102,25 +103,28 @@ 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)); @@ -128,8 +132,50 @@ void CircleModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager 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; @@ -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"); @@ -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); @@ -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) { @@ -236,7 +334,8 @@ void CircleModel::InitCircle() } } - SetNodeCount(parm1, parm2, rgbOrder); + SetNodeCount(1, totalNodes, rgbOrder); + SetBufferSize(GetLayerSizeCount(), maxLights); int LastStringNum = -1; int chan = 0; @@ -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 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 @@ -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"); @@ -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; + } +} \ No newline at end of file diff --git a/xLights/models/CircleModel.h b/xLights/models/CircleModel.h index a5417d2439..1f660fae6a 100644 --- a/xLights/models/CircleModel.h +++ b/xLights/models/CircleModel.h @@ -35,6 +35,9 @@ class CircleModel : public ModelWithScreenLocation 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; @@ -45,4 +48,8 @@ class CircleModel : public ModelWithScreenLocation 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; +}; \ No newline at end of file diff --git a/xLights/models/Model.cpp b/xLights/models/Model.cpp index 4b07a067e9..f810e8a23f 100644 --- a/xLights/models/Model.cpp +++ b/xLights/models/Model.cpp @@ -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 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(); @@ -3353,6 +3367,12 @@ void Model::SetStringStartChannels(bool zeroBased, int NumberOfStrings, int Star } } +void Model::SetStringStartChannels(int StartChannel, std::vector customStartNodes) { + stringStartChan.clear(); + stringStartChan.resize(customStartNodes.size()); + stringStartChan = customStartNodes; +} + int Model::FindNodeAtXY(int bufx, int bufy) { for (int i = 0; i < Nodes.size(); ++i) { @@ -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) { @@ -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"); } } diff --git a/xLights/models/Model.h b/xLights/models/Model.h index 0332b80afc..73d09bf1ee 100644 --- a/xLights/models/Model.h +++ b/xLights/models/Model.h @@ -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 customStartNodes); void RecalcStartChannels(); void SetBufferSize(int NewHt, int NewWi); diff --git a/xLights/models/StarModel.cpp b/xLights/models/StarModel.cpp index 9f264172dd..d19cca1dcb 100644 --- a/xLights/models/StarModel.cpp +++ b/xLights/models/StarModel.cpp @@ -254,7 +254,6 @@ void StarModel::InitModel() innerPercent = wxAtoi(ModelXml->GetAttribute("starCenterPercent", "-1")); if (parm3 < 2) parm3 = 2; // need at least 2 arms - SetNodeCount(parm1, parm2, rgbOrder); // Found a problem where a user had multiple layer sizes but just 1 string and set to RGB dumb string type. // I think the commented out code would fix this but I am not sure it would work in all situations. @@ -272,14 +271,14 @@ void StarModel::InitModel() // each layer is then applied inside the prior one by some factor // the radius of the outer circle starts are bufferWi / 2 - int numlights = parm1 * parm2; - if (numlights == 0) return; if (GetLayerSizeCount() == 0) { SetLayerSizeCount(1); } if (GetLayerSizeCount() == 1) { - SetLayerSize(0, numlights); + SetLayerSize(0, parm1 * parm2); } + totalNodes = (int)GetLayerSizesTotalNodes(); + SetNodeCount(1, totalNodes, rgbOrder); int maxLightsOnLayer = 0; for (int l = 0; l < GetLayerSizeCount(); l++) { @@ -378,8 +377,8 @@ void StarModel::InitModel() while (curPos < segEndLen && currentNode < endNodeForLayer) { - int currentString = currentNode / parm2; - int nodeInString = currentNode % parm2; + int currentString = currentNode / totalNodes; + int nodeInString = currentNode % totalNodes; if (nodeInString == 0 && currentString < GetNumStrings()) { chan = stringStartChan[currentString]; } @@ -416,8 +415,8 @@ void StarModel::InitModel() // handle any left over nodes for (int n = currentNode; n < Nodes.size(); n++) { - int currentString = n / parm2; - int nodeInString = n % parm2; + int currentString = n / totalNodes; + int nodeInString = n % totalNodes; if (nodeInString == 0) { chan = stringStartChan[currentString]; } @@ -510,6 +509,19 @@ void StarModel::InitModel() innerRadius = outerRadius / starRatio; } } + + 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 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; + } + } + GetModelScreenLocation().SetRenderSize(BufferWi, BufferHt, GetModelScreenLocation().GetRenderDp()); screenLocation.RenderDp = 10.0f; // give the bounding box a little depth } @@ -533,23 +545,29 @@ static wxPGChoices TOP_BOT_LEFT_RIGHT(wxArrayString(12, TOP_BOT_LEFT_RIGHT_VALUE void StarModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* outputManager) { - wxPGProperty* p = grid->Append(new wxUIntProperty("# Strings", "StarStringCount", parm1)); - p->SetAttribute("Min", 1); - p->SetAttribute("Max", 640); - 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", "StarLightCount", parm2)); p->SetAttribute("Min", 1); p->SetAttribute("Max", 10000); p->SetEditor("SpinCtrl"); + p->SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + p->ChangeFlag(wxPGPropertyFlags::Hidden, true); } else { p = grid->Append(new wxUIntProperty("Nodes/String", "StarLightCount", parm2)); p->SetAttribute("Min", 1); p->SetAttribute("Max", 10000); 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("# Points", "StarStrandCount", parm3)); @@ -557,6 +575,52 @@ void StarModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* p->SetAttribute("Max", 250); p->SetEditor("SpinCtrl"); + p = grid->Append(new wxUIntProperty("# Strings", "StarStringCount", parm1)); + p->SetAttribute("Min", 1); + p->SetAttribute("Max", 640); + 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 ssl = 0; for (size_t i = 0; i < TOP_BOT_LEFT_RIGHT.GetCount(); i++) { if (TOP_BOT_LEFT_RIGHT[i].GetText() == _starStartLocation) { @@ -566,7 +630,6 @@ void StarModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* } grid->Append(new wxEnumProperty("Starting Location", "StarStart", TOP_BOT_LEFT_RIGHT, ssl)); - AddLayerSizeProperty(grid); p = grid->Append(new wxFloatProperty("Outer to Inner Ratio", "StarRatio", starRatio)); p->SetAttribute("Precision", 2); @@ -584,9 +647,24 @@ void StarModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* int StarModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGridEvent& event) { if ("StarStringCount" == 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, "StarModel::OnPropertyGridChange::StarStringCount"); AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "StarModel::OnPropertyGridChange::StarStringCount"); @@ -647,6 +725,47 @@ int StarModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGri AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "StarModel::OnPropertyGridChange::StarRatio"); AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "StarModel::OnPropertyGridChange::StarRatio"); 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, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes"); + AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes"); + AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes"); + AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes"); + AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes"); + AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "StarModel::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, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "StarModel::OnPropertyGridChange::ModelIndividualStartNodes2"); + return 0; } return Model::OnPropertyGridChange(grid, event); @@ -655,9 +774,10 @@ int StarModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGri void StarModel::OnLayerSizesChange(bool countChanged) { // if string count is 1 then adjust nodes per string to match sum of nodes - if (parm1 == 1) { + 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(); IncrementChangeCount(); AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "StarModel::OnLayerSizesChange"); AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "StarModel::OnLayerSizesChange"); @@ -815,3 +935,23 @@ void StarModel::ImportXlightsModel(wxXmlNode* root, xLightsFrame* xlights, float DisplayError("Failure loading Star model file."); } } +std::string StarModel::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 StarModel::NodesPerString() const { + int nodes = GetChanCount() / std::max(GetChanCountPerNode(), 1); + int ts = GetSmartTs(); + + if (ts <= 1) { + return nodes; + } else { + return nodes * ts; + } +} \ No newline at end of file diff --git a/xLights/models/StarModel.h b/xLights/models/StarModel.h index 20de4b7be2..630efefdad 100644 --- a/xLights/models/StarModel.h +++ b/xLights/models/StarModel.h @@ -29,9 +29,7 @@ class StarModel : public ModelWithScreenLocation virtual int MapToNodeIndex(int strand, int node) const override; virtual int GetMappedStrand(int strand) const override; - int GetStarSize(int starLayer) const { - return GetLayerSize(starLayer); - } + int GetStarSize(int starLayer) const { return GetLayerSize(starLayer); } virtual int GetNumStrands() const override; virtual bool AllNodesAllocated() const override; @@ -45,6 +43,9 @@ class StarModel : public ModelWithScreenLocation 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: static std::vector STAR_BUFFER_STYLES; virtual void InitModel() override; @@ -59,4 +60,7 @@ class StarModel : public ModelWithScreenLocation // The ratio between the inner start and outer star radius (if more than 1 layer) int innerPercent = -1; std::string _starStartLocation = "Bottom Ctr-CW"; -}; + int totalNodes = 0; + static std::string StartNodeAttrName(int idx) { return wxString::Format(wxT("Strings%i"), idx + 1).ToStdString(); } + std::string ComputeStringStartNode(int x) const; +}; \ No newline at end of file