-
-
Notifications
You must be signed in to change notification settings - Fork 371
fix(copy): Disconnect bones when copying a layer without its skeleton #3629
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
base: master
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @Abdelhadi-Wael, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a bug where copying a layer that has bone connections, but without also selecting and copying its associated skeleton, could lead to issues in the copied layer. The changes introduce a mechanism to automatically identify and disconnect these bone references in the newly copied layer if the skeleton is not present in the selection, ensuring the copied layer functions correctly and independently. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Thank you for contributing! 🐞 Bug Fixes
|
|
Thank you for attempting to improve your contribution description! 🐞 Bug Fixes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request fixes an issue where bones were not disconnected when copying a layer without its corresponding skeleton. The overall logic is sound, but I've identified some areas for improvement regarding performance, robustness, and code clarity. Specifically, the method for identifying skeleton layers is fragile, and the loop to find a bone's parent skeleton is inefficient. I've provided a comprehensive suggestion to refactor the new function to address these points.
| std::set<synfig::Layer::Handle> selected_skeletons; | ||
| for (auto& selected_layer : selected_layers) { | ||
| if (selected_layer->get_name() == "skeleton") { | ||
| selected_skeletons.insert(selected_layer); | ||
| } | ||
| } | ||
|
|
||
| // get canvas and bone map once for efficiency | ||
| synfig::Canvas::Handle canvas = canvas_interface_->get_canvas(); | ||
| auto bone_map = synfig::ValueNode_Bone::get_bone_map(canvas); | ||
|
|
||
| // check each dynamic parameter for bone references | ||
| auto param_list = layer->dynamic_param_list(); | ||
| for (auto& param_pair : param_list) { | ||
| synfig::ValueNode::Handle value_node = param_pair.second; | ||
|
|
||
| // get all bones referenced by this parameter | ||
| auto referenced_bones = synfig::ValueNode_Bone::get_bones_referenced_by(value_node); | ||
|
|
||
| bool should_disconnect = false; | ||
| for (const auto& bone : referenced_bones) { | ||
|
|
||
| // find which skeleton layer actually contains this bone | ||
| synfig::Layer::Handle bone_skeleton = nullptr; | ||
| for (auto layer_iter : *canvas) { | ||
| if (layer_iter->get_name() == "skeleton") { | ||
| synfig::ValueBase bones_param = layer_iter->get_param("bones"); | ||
| std::vector<synfig::Bone> bone_list = bones_param.get_list_of(synfig::Bone()); | ||
| for (const auto& skeleton_bone : bone_list) { | ||
| if (skeleton_bone.get_name() == bone->get_bone_name(synfig::Time())) { | ||
| bone_skeleton = layer_iter; | ||
| break; | ||
| } | ||
| } | ||
| if (bone_skeleton) | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // if the skeleton containing this bone is not selected disconnect | ||
| if (selected_skeletons.find(bone_skeleton) == selected_skeletons.end()) { | ||
| should_disconnect = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (should_disconnect) { | ||
| layer->disconnect_dynamic_param(param_pair.first); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function can be made more robust and performant.
- Robustness: Identifying skeleton layers by name (
get_name() == "skeleton") is fragile because users can rename layers. It's better to check the layer's type usingsynfig::Layer_Skeleton::Handle::cast_dynamic(). - Performance: The current implementation for finding a bone's skeleton layer involves nested loops that iterate through all canvas layers for each referenced bone. This can be slow on complex projects. A more performant approach is to first build a map of all bone names to their corresponding skeleton layers, and then use this map for quick lookups.
- Code Clarity: The
bone_mapvariable is initialized but never used and can be removed.
Here is a suggested refactoring that addresses these points:
std::set<synfig::Layer::Handle> selected_skeletons;
for (const auto& selected_layer : selected_layers) {
if (synfig::Layer_Skeleton::Handle::cast_dynamic(selected_layer)) {
selected_skeletons.insert(selected_layer);
}
}
// get canvas once for efficiency
synfig::Canvas::Handle canvas = canvas_interface_->get_canvas();
// build a map of bone names to their skeleton layers for efficient lookup.
std::map<synfig::String, synfig::Layer::Handle> bone_to_skeleton_map;
for (auto& canvas_layer : *canvas) {
if (auto skeleton_layer = synfig::Layer_Skeleton::Handle::cast_dynamic(canvas_layer)) {
synfig::ValueBase bones_param = skeleton_layer->get_param("bones");
std::vector<synfig::Bone> bone_list = bones_param.get_list_of(synfig::Bone());
for (const auto& skeleton_bone : bone_list) {
bone_to_skeleton_map[skeleton_bone.get_name()] = skeleton_layer;
}
}
}
// check each dynamic parameter for bone references
auto param_list = layer->dynamic_param_list();
for (auto& param_pair : param_list) {
synfig::ValueNode::Handle value_node = param_pair.second;
// get all bones referenced by this parameter
auto referenced_bones = synfig::ValueNode_Bone::get_bones_referenced_by(value_node);
bool should_disconnect = false;
for (const auto& bone : referenced_bones) {
synfig::Layer::Handle bone_skeleton = nullptr;
auto it = bone_to_skeleton_map.find(bone->get_bone_name(synfig::Time()));
if (it != bone_to_skeleton_map.end()) {
bone_skeleton = it->second;
}
// if the skeleton containing this bone is not selected, disconnect.
// This also handles the case where bone_skeleton is nullptr (bone not found).
if (selected_skeletons.find(bone_skeleton) == selected_skeletons.end()) {
should_disconnect = true;
break;
}
}
if (should_disconnect) {
layer->disconnect_dynamic_param(param_pair.first);
}
}|
Thank you for attempting to improve your contribution description! 🐞 Bug Fixes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saya juga pernah mengalami hal yang sama. Semoga bisa diperbaiki.
fixes #3626 & #3614