Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Abdelhadi-Wael
Copy link
Contributor

@Abdelhadi-Wael Abdelhadi-Wael commented Sep 19, 2025

fixes #3626 & #3614

@gemini-code-assist
Copy link

Summary of Changes

Hello @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

  • New Bone Disconnection Logic: Introduced a new private helper function, disconnect_bones_if_skeleton_not_selected, within LayerActionManager to manage bone connections during layer copying.
  • Integration into Copy Operation: The LayerActionManager::copy() method now utilizes this new function to automatically disconnect bone references in a cloned layer if its associated skeleton layer was not part of the current selection being copied.
  • Improved Copy Behavior: This ensures that when a layer with bone connections is copied without its skeleton, the copied layer's bone parameters are properly severed, preventing unexpected behavior or broken references.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Abdelhadi-Wael Abdelhadi-Wael changed the title fix(copy) : Disconnect bones when copying a layer without its skeleton fix(copy): Disconnect bones when copying a layer without its skeleton Sep 19, 2025
@github-actions
Copy link

Thank you for contributing!
Below is how your contribution will appear in Synfig's release notes. Review it and improve your pull request's title and description so that your contribution is clear. You can add images, videos, and anything else you want. Have fun!

🐞 Bug Fixes

  • (Copy Related) Disconnect bones when copying a layer without its skeleton

    Fixes #3626

@github-actions
Copy link

Thank you for attempting to improve your contribution description!
Below is how your updated contribution will appear in Synfig's release notes. Review it and make further improvements until you're satisfied!

🐞 Bug Fixes

  • (Copy Related) Disconnect bones when copying a layer without its skeleton

    Fixes #3626

Copy link

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines 800 to 849
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);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This function can be made more robust and performant.

  1. 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 using synfig::Layer_Skeleton::Handle::cast_dynamic().
  2. 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.
  3. Code Clarity: The bone_map variable 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);
		}
	}

@github-actions
Copy link

Thank you for attempting to improve your contribution description!
Below is how your updated contribution will appear in Synfig's release notes. Review it and make further improvements until you're satisfied!

🐞 Bug Fixes

  • (Copy Related) Disconnect bones when copying a layer without its skeleton

    Fixes #3626 & #3614

Copy link

@zaynopenapp zaynopenapp left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

File corrupted when you copy layer linked to skeleton from one file to another

2 participants