How can copy nested components

I can copy SUComponentInstanceRef, but I can not copy nested SUComponentInstanceRef.

SDK_WIN_x64_2021-0-339

image

nested component_instance.skp (525.1 KB)
no nested component_instance.skp (523.2 KB)

// find definition by guid
void FindComponentDefinition(SUModelRef model, SUComponentDefinitionRef def_from_other_model, SUComponentDefinitionRef * find_def)
{
	SUStringRef guid_ref = SU_INVALID;
	SU(SUStringCreate(&guid_ref));
	SU(SUComponentDefinitionGetGuid(def_from_other_model, &guid_ref));
	size_t num = 0;
	SU(SUModelGetNumComponentDefinitions(model, &num));
	if (num > 0) {
		std::vector<SUComponentDefinitionRef> definitions(num);
		SU(SUModelGetComponentDefinitions(model, num,&definitions[0],&num));
		for (int i = 0; i < num; i++) {
			SUStringRef guid_ref_temp = SU_INVALID;
			SU(SUStringCreate(&guid_ref_temp));
			SU(SUComponentDefinitionGetGuid(definitions[i], &guid_ref_temp));
			int res = 0;
			SU(SUStringCompare(guid_ref_temp, guid_ref, &res));
			SU(SUStringRelease(&guid_ref_temp));
			if (res == 0) { *find_def = definitions[i]; break; }
		}
	}
	SU(SUStringRelease(&guid_ref));
}

// add definition, return it if definition exist, add it if definition not exist.
void AddComponentDefinition(SUModelRef src_model, SUModelRef * dest_model, SUComponentDefinitionRef src_def, SUComponentDefinitionRef * res_def)
{
	SUStringRef guid_ref = SU_INVALID;
	SU(SUStringCreate(&guid_ref));
	SU(SUComponentDefinitionGetGuid(src_def, &guid_ref));
	FindComponentDefinition(*dest_model, src_def, res_def);
	if (SUIsInvalid(*res_def)) {
		SUComponentDefinitionCreate(res_def);

		// 设置group name
		SUStringRef name = SU_INVALID;
		SU(SUStringCreate(&name));
		SU(SUComponentDefinitionGetName(src_def, &name));
		SU(SUComponentDefinitionSetName(*res_def, sustr2str(name).c_str()));
		SU(SUStringRelease(&name));

		SUStringRef description = SU_INVALID;
		SU(SUStringCreate(&description));
		SU(SUComponentDefinitionGetDescription(src_def, &description));
		SU(SUComponentDefinitionSetDescription(*res_def, sustr2str(description).c_str()));
		SU(SUStringRelease(&description));

		SUComponentBehavior behavior;
		SU(SUComponentDefinitionGetBehavior(src_def, &behavior));
		SU(SUComponentDefinitionSetBehavior(*res_def, &behavior));

		SUEntitiesRef src_entities = SU_INVALID, dest_entities = SU_INVALID;
		SU(SUComponentDefinitionGetEntities(src_def, &src_entities));
		SU(SUComponentDefinitionGetEntities(*res_def, &dest_entities));
		MergeEntities(src_model, dest_model, src_entities, &dest_entities);

		std::vector<SUComponentDefinitionRef> defs(1);
		defs.push_back(*res_def);
		SU(SUModelAddComponentDefinitions(*dest_model,defs.size(),&defs[0]));

		SUStringRef guid = SU_INVALID;
		SU(SUStringCreate(&guid));
		SU(SUComponentDefinitionGetGuid(*res_def, &guid));
		int res = 0;
		SU(SUStringCompare(guid_ref, guid, &res));
		SU(SUStringRelease(&guid));
	}
	SU(SUStringRelease(&guid_ref));
}

bool AddInstances(SUModelRef src_model, SUModelRef *dest_model, SUEntitiesRef src_entities, SUEntitiesRef * dest_entities)
{
	size_t num = 0;
	SU(SUEntitiesGetNumInstances(src_entities, &num));
	if (num <= 0) return false;
	std::vector<SUComponentInstanceRef> src_instances(num);
	SU(SUEntitiesGetInstances(src_entities, num, &src_instances[0], &num));
	for (int i = 0; i < num; i++) {
		SUComponentInstanceRef src_instance = src_instances[i];
		SUComponentInstanceRef instance = SU_INVALID;
		SUComponentDefinitionRef src_def = SU_INVALID;
		SUComponentDefinitionRef dest_def = SU_INVALID;
		SU(SUComponentInstanceGetDefinition(src_instance, &src_def));
		AddComponentDefinition(src_model, dest_model, src_def, &dest_def);
		if (SUIsInvalid(dest_def))
			continue;
		SUComponentDefinitionCreateInstance(dest_def, &instance);

		SUTransformation trans;
		SU(SUComponentInstanceGetTransform(src_instance, &trans));
		SU(SUComponentInstanceSetTransform(instance, &trans));

		SUStringRef name = SU_INVALID;
		SU(SUStringCreate(&name));
		SU(SUComponentInstanceGetName(src_instance, &name));
		SU(SUComponentInstanceSetName(instance, sustr2str(name).c_str()));
		SU(SUStringRelease(&name));

		SUResult res = SUEntitiesAddInstance(*dest_entities, instance,NULL);
		//SU(SUEntitiesAddInstance(*dest_entities,instance, &name));
	}
	return true;
}

Have you added the component definition to the model before you try to add instances of it?

Yes, I’m sure.

Before add an instance, will execute the following code.

This method (AddComponentDefinition) is used to ensure the existence of the definition. If it not exists, the definition will be added. If there are components in the definition, it will call to the method (AddInstances).

Can you add a complete example? One with a main function where we can plug in the sample models and run it?

nested_component_instance.skp (130.4 KB)
no_nested_component_instance.skp (124.9 KB)

CODE

#include <SketchUpAPI\sketchup.h>
#include <cstdio>
#include <assert.h>
#include <vector>


//\tSU[^(][^=]*\([^=]*\);  用于检查的正则
#define SU(api_function_call) {\
  SUResult su_api_result = api_function_call;\
  assert(SU_ERROR_NONE == su_api_result);\
}\

#define refute(condition) assert(!(condition))

SUResult MergeModel(SUModelRef src, SUModelRef* dest);
SUResult MergeEntities(SUModelRef src_model, SUModelRef *dest_model, SUEntitiesRef src_entities, SUEntitiesRef *dest_entities);

void FindComponentDefinition(SUModelRef model, SUComponentDefinitionRef def_from_other_model, SUComponentDefinitionRef * find_def);
void AddComponentDefinition(SUModelRef src_model, SUModelRef *dest_model, SUComponentDefinitionRef src_def, SUComponentDefinitionRef *res_def);

bool AddInstances(SUModelRef src_model, SUModelRef *dest_model, SUEntitiesRef src_entities, SUEntitiesRef * dest_entities);

bool AddFaces(SUEntitiesRef src_entities, SUGeometryInputRef geom_input_face, size_t *vertex_index);
bool AddEdges(SUEntitiesRef src_entities, SUGeometryInputRef geom_input_face, size_t *vertex_index);
void HandleLoop(SUGeometryInputRef geom_input, SULoopRef loop, SULoopInputRef input_loop, size_t *vertex_index);
size_t AddEdgeToGeomInput(SUEdgeRef src_edge, SUGeometryInputRef geom_input, size_t *vertex_index);

std::string sustr2str(SUStringRef str) {
	size_t str_len = 0;
	SUResult res = SUStringGetUTF8Length(str, &str_len);
	if (res != SU_ERROR_NONE) return NULL;
	char* str_char = new char[str_len + 1];
	SUStringGetUTF8(str, str_len, str_char, &str_len);
	if (res != SU_ERROR_NONE) {
		delete[] str_char;
		return NULL;
	}
	str_char[str_len] = '\0';
	std::string string(str_char);
	if (str_char) delete[] str_char;
	return string;
}

SUStringRef str2sustr(std::string str) {
	SUStringRef resstr = SU_INVALID;
	SUResult res = SUStringCreateFromUTF8(&resstr, str.c_str());
	if (res == SU_ERROR_NONE) return resstr;
	return SU_INVALID;
}


int main() {
	// Always initialize the API before using it
	SUInitialize();
	// Load the model from a file
	SUResult res;
	SUModelRef model1 = SU_INVALID, model2 = SU_INVALID;
	res = SUModelCreateFromFile(&model1, "F:/no_nested_component_instance.skp");
	if (res != SU_ERROR_NONE) {
		printf("model1 open fail");
		return res;
	}
	res = SUModelCreateFromFile(&model2,"F:/nested_component_instance.skp");
	if (res != SU_ERROR_NONE) {
		printf("model2 open fail");
		return res;
	}
	MergeModel(model2, &model1);
	SU(SUModelSaveToFile(model1, "F:/new.skp"));
	SU(SUModelRelease(&model1));
	// 因为复制后 会导致model2出错暂时取消该断言
	SU(SUModelRelease(&model2));
	SUTerminate();
	return 0;
}

SUResult MergeModel(SUModelRef src, SUModelRef* dest) {
	SUResult res;
	SUEntitiesRef src_entities = SU_INVALID, dest_entities = SU_INVALID;
	SU(SUModelGetEntities(src, &src_entities));
	SU(SUModelGetEntities(*dest, &dest_entities));
	MergeEntities(src, dest, src_entities, &dest_entities);
	return SU_ERROR_NONE;
}

SUResult MergeEntities(SUModelRef src_model, SUModelRef *dest_model, SUEntitiesRef src_entities, SUEntitiesRef *dest_entities) {
	SUEntityListRef src_list = SU_INVALID;
	SUEntityListIteratorRef list_iterator = SU_INVALID;
	SUGeometryInputRef geom_input = SU_INVALID;
	SU(SUGeometryInputCreate(&geom_input));
	size_t vertex_index = 0;

	// 通过 geom_input 处理的内容 可以防止重复点 线
	bool flag = AddFaces(src_entities, geom_input, &vertex_index);
	flag = AddEdges(src_entities, geom_input, &vertex_index) | flag;
	if (flag) SU(SUEntitiesFill(*dest_entities, geom_input, true));
	SU(SUGeometryInputRelease(&geom_input));

	AddInstances(src_model, dest_model, src_entities, dest_entities);

	return SU_ERROR_NONE;
}

// model 中是否存在 src_def(来自于另一个model),如果不存在则添加
void FindComponentDefinition(SUModelRef model, SUComponentDefinitionRef def_from_other_model, SUComponentDefinitionRef * find_def)
{
	SUStringRef guid_ref = SU_INVALID;
	SU(SUStringCreate(&guid_ref));
	SU(SUComponentDefinitionGetGuid(def_from_other_model, &guid_ref));
	size_t num = 0;
	SU(SUModelGetNumComponentDefinitions(model, &num));
	if (num > 0) {
		std::vector<SUComponentDefinitionRef> definitions(num);
		SU(SUModelGetComponentDefinitions(model, num, &definitions[0], &num));
		for (int i = 0; i < num; i++) {
			SUStringRef guid_ref_temp = SU_INVALID;
			SU(SUStringCreate(&guid_ref_temp));
			SU(SUComponentDefinitionGetGuid(definitions[i], &guid_ref_temp));
			int res = 0;
			SU(SUStringCompare(guid_ref_temp, guid_ref, &res));
			SU(SUStringRelease(&guid_ref_temp));
			if (res == 0) { *find_def = definitions[i]; break; }
		}
	}
	SU(SUStringRelease(&guid_ref));
}

// 添加定义体 如果定义体存在直接返回 不存在则添加后返回
void AddComponentDefinition(SUModelRef src_model, SUModelRef * dest_model, SUComponentDefinitionRef src_def, SUComponentDefinitionRef * res_def)
{
	SUStringRef guid_ref = SU_INVALID;
	SU(SUStringCreate(&guid_ref));
	SU(SUComponentDefinitionGetGuid(src_def, &guid_ref));
	FindComponentDefinition(*dest_model, src_def, res_def);
	if (SUIsInvalid(*res_def)) {
		SUComponentDefinitionCreate(res_def);

		// 设置group name
		SUStringRef name = SU_INVALID;
		SU(SUStringCreate(&name));
		SU(SUComponentDefinitionGetName(src_def, &name));
		SU(SUComponentDefinitionSetName(*res_def, sustr2str(name).c_str()));
		SU(SUStringRelease(&name));

		SUStringRef description = SU_INVALID;
		SU(SUStringCreate(&description));
		SU(SUComponentDefinitionGetDescription(src_def, &description));
		SU(SUComponentDefinitionSetDescription(*res_def, sustr2str(description).c_str()));
		SU(SUStringRelease(&description));

		SUComponentBehavior behavior;
		SU(SUComponentDefinitionGetBehavior(src_def, &behavior));
		SU(SUComponentDefinitionSetBehavior(*res_def, &behavior));

		SUEntitiesRef src_entities = SU_INVALID, dest_entities = SU_INVALID;
		SU(SUComponentDefinitionGetEntities(src_def, &src_entities));
		SU(SUComponentDefinitionGetEntities(*res_def, &dest_entities));
		MergeEntities(src_model, dest_model, src_entities, &dest_entities);

		std::vector<SUComponentDefinitionRef> defs(1);
		defs.push_back(*res_def);
		SU(SUModelAddComponentDefinitions(*dest_model, defs.size(), &defs[0]));

		SUStringRef guid = SU_INVALID;
		SU(SUStringCreate(&guid));
		SU(SUComponentDefinitionGetGuid(*res_def, &guid));
		int res = 0;
		SU(SUStringCompare(guid_ref, guid, &res));
		SU(SUStringRelease(&guid));
	}
	SU(SUStringRelease(&guid_ref));
}

int fun_call_count = 0;
bool AddInstances(SUModelRef src_model, SUModelRef *dest_model, SUEntitiesRef src_entities, SUEntitiesRef * dest_entities)
{
	size_t num = 0;
	SU(SUEntitiesGetNumInstances(src_entities, &num));
	if (num <= 0) return false;
	std::vector<SUComponentInstanceRef> src_instances(num);
	SU(SUEntitiesGetInstances(src_entities, num, &src_instances[0], &num));
	fun_call_count++;
	for (int i = 0; i < num; i++) {
		SUComponentInstanceRef src_instance = src_instances[i];
		SUComponentInstanceRef instance = SU_INVALID;
		SUComponentDefinitionRef src_def = SU_INVALID;
		SUComponentDefinitionRef dest_def = SU_INVALID;
		SU(SUComponentInstanceGetDefinition(src_instance, &src_def));
		AddComponentDefinition(src_model, dest_model, src_def, &dest_def);
		if (SUIsInvalid(dest_def))
			continue;
		SUComponentDefinitionCreateInstance(dest_def, &instance);

		// 设置transform
		SUTransformation trans;
		SU(SUComponentInstanceGetTransform(src_instance, &trans));
		SU(SUComponentInstanceSetTransform(instance, &trans));

		// 设置group name
		SUStringRef name = SU_INVALID;
		SU(SUStringCreate(&name));
		SU(SUComponentInstanceGetName(src_instance, &name));
		SU(SUComponentInstanceSetName(instance, sustr2str(name).c_str()));
		SU(SUStringRelease(&name));

		// 暂不处理嵌套组件
		// if (fun_call_count == 1) 
		SU(SUEntitiesAddInstance(*dest_entities, instance, NULL));
	}
	fun_call_count--;
	return true;
}

void HandleLoop(SUGeometryInputRef geom_input, SULoopRef loop, SULoopInputRef input_loop, size_t *vertex_index) {
	size_t vertex_num = 0;
	SU(SULoopGetNumVertices(loop, &vertex_num));
	if (vertex_num < 2) return;
	std::vector<SUVertexRef> loop_verts(vertex_num);
	std::vector<SUPoint3D> loop_pts;
	SU(SULoopGetVertices(loop, vertex_num, &loop_verts[0], &vertex_num));
	for (size_t i = 0; i < vertex_num; i++) {
		SUPoint3D pt;
		SU(SUVertexGetPosition(loop_verts[i], &pt));
		SU(SUGeometryInputAddVertex(geom_input, &pt));
		SU(SULoopInputAddVertexIndex(input_loop, (*vertex_index) + i));
		loop_pts.push_back(pt);
	}

	size_t edge_num = vertex_num, curve_s = -1;
	std::vector<SUEdgeRef> edges(edge_num);
	SUCurveRef curve = SU_INVALID;
	SU(SULoopGetEdges(loop, edge_num, &edges[0], &edge_num));
	for (size_t i = 0; i < edge_num; i++) {
		// 处理面上弧线
		if (SUEdgeGetCurve(edges[i], &curve) == SU_ERROR_NONE) {
			if (curve_s == -1) curve_s = i;
		}
		else {
			if (curve_s != -1 && (i - 1)>curve_s)
				SU(SULoopInputAddCurve(input_loop, curve_s, (i - 1)));
			curve_s = -1;
			curve = SU_INVALID;
		}

		bool smoooth = false, soft = false, hidden = false;
		SU(SUEdgeGetSmooth(edges[i], &smoooth));
		SU(SULoopInputEdgeSetSmooth(input_loop, i, smoooth));
		SU(SUEdgeGetSoft(edges[i], &soft));
		SU(SULoopInputEdgeSetSoft(input_loop, i, soft));
		SUDrawingElementRef element = SUEdgeToDrawingElement(edges[i]);
		SU(SUDrawingElementGetHidden(element, &hidden));
		SU(SULoopInputEdgeSetHidden(input_loop, i, hidden));
		// 图层
		/*SULayerRef layer = SU_INVALID;
		SU(SUDrawingElementGetLayer(element, &layer));
		SU(SULoopInputEdgeSetLayer(input_loop, i, layer));*/
	}
	// 整个loop都是弧线
	if (curve_s != -1)
		SU(SULoopInputAddCurve(input_loop, curve_s, (edge_num - 1)));
	// 增加顶点数量
	(*vertex_index) += vertex_num;
}

bool AddFaces(SUEntitiesRef src_entities, SUGeometryInputRef geom_input, size_t *vertex_index) {
	size_t num = 0;
	SU(SUEntitiesGetNumFaces(src_entities, &num));
	if (num <= 0) return false;
	std::vector<SUFaceRef> src_faces(num);
	SU(SUEntitiesGetFaces(src_entities, num, &src_faces[0], &num));
	for (int i = 0; i < num; i++) {
		SUFaceRef src_face = src_faces[i];
		SULoopRef out_loop = SU_INVALID;
		SU(SUFaceGetOuterLoop(src_face, &out_loop));

		size_t out_loop_length = 0;
		SULoopInputRef outer_input_loop = SU_INVALID;
		SU(SULoopInputCreate(&outer_input_loop));
		HandleLoop(geom_input, out_loop, outer_input_loop, vertex_index);
		// 创建外圈面
		size_t added_face_index = -1;
		SU(SUGeometryInputAddFace(geom_input, &outer_input_loop, &added_face_index));
		//SU(SULoopInputRelease(&outer_input_loop));

		// 创建内圈面
		size_t inner_loops_length = 0;
		SU(SUFaceGetNumInnerLoops(src_face, &inner_loops_length));
		if (inner_loops_length != 0) {
			std::vector<SULoopRef> inner_loops(inner_loops_length);
			SU(SUFaceGetInnerLoops(src_face, inner_loops_length, &inner_loops[0], &inner_loops_length));
			for (int i = 0; i < inner_loops_length; i++) {
				SULoopInputRef inner_input_loop = SU_INVALID;
				SU(SULoopInputCreate(&inner_input_loop));
				HandleLoop(geom_input, out_loop, inner_input_loop, vertex_index);
				// 添加内圈洞
				SU(SUGeometryInputFaceAddInnerLoop(geom_input, added_face_index, &inner_input_loop));
			}
		}
	}
	return true;
}

size_t AddEdgeToGeomInput(SUEdgeRef src_edge, SUGeometryInputRef geom_input, size_t *vertex_index) {
	SUVertexRef start = SU_INVALID, end = SU_INVALID;
	SUPoint3D start_point_3d, end_point_3d;
	size_t added_edge_index = 0;

	SU(SUEdgeGetStartVertex(src_edge, &start));
	SU(SUVertexGetPosition(start, &start_point_3d));
	SU(SUGeometryInputAddVertex(geom_input, &start_point_3d));

	SU(SUEdgeGetEndVertex(src_edge, &end));
	SU(SUVertexGetPosition(end, &end_point_3d));
	SU(SUGeometryInputAddVertex(geom_input, &end_point_3d));

	SU(SUGeometryInputAddEdge(geom_input, (*vertex_index), (*vertex_index) + 1, &added_edge_index));
	*vertex_index += 2;

	// 柔化
	bool smoooth = false, soft = false, hidden = false;
	SU(SUEdgeGetSmooth(src_edge, &smoooth));
	SU(SUEdgeGetSoft(src_edge, &soft));
	SU(SUGeometryInputEdgeSetHidden(geom_input, added_edge_index, hidden));
	SU(SUGeometryInputEdgeSetSoft(geom_input, added_edge_index, soft));
	SU(SUGeometryInputEdgeSetSmooth(geom_input, added_edge_index, smoooth));
	SUDrawingElementRef element = SUEdgeToDrawingElement(src_edge);
	SU(SUDrawingElementGetHidden(element, &hidden));
	SU(SUGeometryInputEdgeSetHidden(geom_input, added_edge_index, hidden));
	return added_edge_index;
}

bool AddEdges(SUEntitiesRef src_entities, SUGeometryInputRef geom_input, size_t *vertex_index) {
	size_t num = 0, handle_edge = 0;
	SU(SUEntitiesGetNumEdges(src_entities, true, &num));
	if (num <= 0) return false;
	std::vector<SUEdgeRef> src_edges(num);
	SU(SUEntitiesGetEdges(src_entities, true, num, &src_edges[0], &num));
	for (int i = 0; i < num; i++) {
		SUEdgeRef src_edge = src_edges[i];

		// 跳过组成面的线
		size_t face_num = 0;
		SU(SUEdgeGetNumFaces(src_edge, &face_num));
		if (face_num > 0) continue;

		// 跳过组成弧线的线
		SUCurveRef curve = SU_INVALID;
		if (SUEdgeGetCurve(src_edge, &curve) == SU_ERROR_NONE) continue;
		if (SUIsValid(curve)) continue;

		handle_edge++;
		AddEdgeToGeomInput(src_edge, geom_input, vertex_index);
	}
	return handle_edge > 0;
}

I finally found some time to look at this.

You are adding geometry to the definition before the definition is added to the model in AddComponentDefinition:

I moved MergeEntities until after the definition is added and there was no longer a crash.

2 Likes

Thank you. I thought I was going to construct a complete and then add it.

The order that you do things does matter.

Generally, with the C APIs, attach the objects to the model (or it’s collections) before manipulating those objects.


For example if you create edges directly, you would need to add them to an entities collection in the model before setting flags like soften and hidden.

But if you instead create geometry “virtually” using the SUGeometryInput helper, then you can set flags before adding all the “helper” geometry to entities collection in the model.


I think the LayOut C API is even “pickier” than the SketchUp C API when it comes to order of things.

Hi Javalitterboy, could you tell us wich tool did you use to edit this flow chart?
Thank you!
David

Yes, It’s right.

ProcessOn - 免费在线作图,思维导图,流程图,实时协作

I’ve logged an issue to consider adding parent checks to the C API, making functions fail if you try to act on entities not added to the model. This issue has come up a few times before.

1 Like

Is this an internal issue ? I don’t see anything in the public tracker.

Yes, internal issue.