Skip to content

Commit b0ef605

Browse files
authored
Merge pull request #99 from adriengivry/feature/new_duplicated_actor_name_generation
Improving the actor duplication naming system
2 parents 9298f94 + a85dcca commit b0ef605

1 file changed

Lines changed: 68 additions & 6 deletions

File tree

Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,62 @@ bool OvEditor::Core::EditorActions::DestroyActor(OvCore::ECS::Actor & p_actor)
566566
return true;
567567
}
568568

569+
std::string FindDuplicatedActorUniqueName(OvCore::ECS::Actor& p_duplicated, OvCore::ECS::Actor& p_newActor, OvCore::SceneSystem::Scene& p_scene)
570+
{
571+
const auto sourceName = p_duplicated.GetName();
572+
auto sourceNameWithoutSuffix = sourceName;
573+
574+
auto suffixOpeningParenthesisPos = std::string::npos;
575+
auto suffixClosingParenthesisPos = std::string::npos;
576+
577+
// Keep track of the current character position when iterating onto `sourceName`
578+
auto currentPos = decltype(std::string::npos){sourceName.length() - 1};
579+
580+
// Here we search for `(` and `)` positions. (Needed to extract the number between those parenthesis)
581+
for (auto it = sourceName.rbegin(); it < sourceName.rend(); ++it, --currentPos)
582+
{
583+
auto c = *it;
584+
585+
if (suffixClosingParenthesisPos == std::string::npos && c == ')') suffixClosingParenthesisPos = currentPos;
586+
if (suffixClosingParenthesisPos != std::string::npos && c == '(') suffixOpeningParenthesisPos = currentPos;
587+
}
588+
589+
// We need to declare our `duplicationCounter` here to store the number between found parenthesis OR 1 (In the case no parenthesis, AKA, suffix, has been found)
590+
auto duplicationCounter = uint32_t{ 1 };
591+
592+
// If the two parenthis have been found AND the closing parenthesis is the last character AND there is a space before the opening parenthesis
593+
if (suffixOpeningParenthesisPos != std::string::npos && suffixClosingParenthesisPos == sourceName.length() - 1 && suffixOpeningParenthesisPos > 0 && sourceName[suffixOpeningParenthesisPos - 1] == ' ')
594+
{
595+
// Extract the string between those parenthesis
596+
const auto between = sourceName.substr(suffixOpeningParenthesisPos + 1, suffixClosingParenthesisPos - suffixOpeningParenthesisPos - 1);
597+
598+
// If the `between` string is composed of digits (AKA, `between` is a number)
599+
if (!between.empty() && std::find_if(between.begin(), between.end(), [](unsigned char c) { return !std::isdigit(c); }) == between.end())
600+
{
601+
duplicationCounter = static_cast<uint32_t>(std::atoi(between.c_str()));
602+
sourceNameWithoutSuffix = sourceName.substr(0, suffixOpeningParenthesisPos - 1);
603+
}
604+
}
605+
606+
const auto parent = p_newActor.GetParent();
607+
const auto adjacentActors = parent ? parent->GetChildren() : p_scene.GetActors();
608+
609+
auto foundName = sourceNameWithoutSuffix;
610+
611+
// Lambda that checks if the current `foundName` is used by the actor given in parameter (Also ensure that the given actor is adjacent to our new actor)
612+
// We call "adjacent" two actors that shares the same hierarchical level. (Ex: If [A] and [B] are both direction children of [C], then, they are adjacents)
613+
const auto isActorNameTaken = [&foundName, parent](auto actor) { return (parent || !actor->GetParent()) && actor->GetName() == foundName; };
614+
615+
// While there is an adjacent actor with the current `foundName`, we keep generating new names
616+
while (std::find_if(adjacentActors.begin(), adjacentActors.end(), isActorNameTaken) != adjacentActors.end())
617+
{
618+
// New names are composed of the `sourceNameWithoutSuffix` (Ex: "Cube (1)" name without suffix is "Cube")
619+
foundName = sourceNameWithoutSuffix + " (" + std::to_string(duplicationCounter++) + ")";
620+
}
621+
622+
return foundName;
623+
}
624+
569625
void OvEditor::Core::EditorActions::DuplicateActor(OvCore::ECS::Actor & p_toDuplicate, OvCore::ECS::Actor* p_forcedParent, bool p_focus)
570626
{
571627
tinyxml2::XMLDocument doc;
@@ -582,12 +638,18 @@ void OvEditor::Core::EditorActions::DuplicateActor(OvCore::ECS::Actor & p_toDupl
582638
newActor.SetParent(*p_forcedParent);
583639
else
584640
{
585-
newActor.SetName(p_toDuplicate.GetName() + " (Copy)"); // Dont overload the name if it has a forced parent
586-
if (newActor.GetParentID() > 0)
587-
{
588-
if (auto found = m_context.sceneManager.GetCurrentScene()->FindActorByID(newActor.GetParentID()); found)
589-
newActor.SetParent(*found);
590-
}
641+
auto currentScene = m_context.sceneManager.GetCurrentScene();
642+
643+
if (newActor.GetParentID() > 0)
644+
{
645+
if (auto found = currentScene->FindActorByID(newActor.GetParentID()); found)
646+
{
647+
newActor.SetParent(*found);
648+
}
649+
}
650+
651+
const auto uniqueName = FindDuplicatedActorUniqueName(p_toDuplicate, newActor, *currentScene);
652+
newActor.SetName(uniqueName);
591653
}
592654

593655
if (p_focus)

0 commit comments

Comments
 (0)