I’m a novice plugin developer, so I apologize beforehand if this has already been answered elsewhere.

Is there a way of ‘using’ the SelEdgeLoop command from within a plugin command?
By this I mean if there is any Rhino C++ SDK API function doing this same thing? (much like other RhinoXYZ functions for other commands…) I’ve also checked ON_Mesh with no luck…

I’m also aware of RhinoApp().RunScript(), and while this solution is not preferred, I could work with it. However, I do not know how edges can be selected from a script, if at all possible. I tried edge indices but it didn’t work.

[On a related side topic: is there any way to figure out the arguments you can pass into commands in script mode, i.e. RhinoApp().RunScript()?]


Hi Victor,

ON_SubDEdgeChain is the C++ SDK class that is closest in function to the SelEdgeLoop command. A combination of BeginEdgeChain and AddAllNeighbors (once in each direction) should get you close to what the command is doing.

Note that if what you want to do is allow the user to click one edge, and get the edge loop in return, then it is usually easier to rely on the subobject picking of Rhino. A CRhinoGetObject with the geometry filter set to CRhinoGetObject::meshedge_filter, and subobject selection enabled, will let the user select an edge loop with Ctrl+Shift+DoubleClick. You get a list of SubD edges returned, and can rebuild the edge chain with ON_SubDEdgeChain::SortEdgesIntoEdgeChains. That makes highlighting and pre-selection easier to sort out.

Basically everything that you can type in Rhino’s command line should work here, and prefixing command names with an hyphen - will allow more options. The online help lists them.

Hi Pierre,
Thanks for your answers.
I’m sorry I don’t think I explained what I am trying to do clearly.

I have an ON_Mesh object and an edge index. I need to find an edge loop on the mesh containing this edge. There is no user interaction involved.

I have looked at ON_SubDEdgeChain, but it seems to only apply to ON_SubD objects, right? Is there anything similar for ON_Mesh?

Is this the online help you’re referring to? If so, I assume SelEdgeLoop doesn’t allow for any options?

Sorry I was deep in SubD and forgot SelEdgeLoop applies to meshes too. I do not see anything accessible in the C++ SDK for mesh edge loops unfortunately. You could convert to SubD, get the edge loop with ON_SubDEdgeChain, then convert back to a mesh, but at that point I would just script the command.

Correct on both.

I was trying to get this to work, but noticed a problem.

Edge indices seem to change from the mesh to the SubD. Can I assume they always increase by one? My ON_Mesh mesh is a manifold, with no ngons, no duplicate vertices, etc. I worry ON_SubD::Internal_CreateFromMeshWithValidNgons might still introduce new topology.

I have also managed to get this to work, by a combination of CRhinoMeshObject::SelectSubObject and RhinoApp::RunScript. However, I need to:

  • first create a copy of the CRhinoMeshObject object, as selection on the const CRhinoGetObject::Object object doesn’t work.
  • select the seed edge with CRhinoMeshObject::SelectSubObject and then replace the original CRhinoMeshObject with the copy
  • script the command with RhinoApp::RunScript
  • loop over every edge in the CRhinoMeshObject::Mesh topology and check whether it was selected with CRhinoMeshObject::IsSubObjectSelected

You can see how this is not very efficient when I need to run it over a thousand times on a large mesh. Is there a better way to do this?


I’m surprised, SelectSubObject's signature seems to be OK with const objects. Do you mean that scripting SelEdgeLoop itself does not work if your mesh object is const? Can you start with a non-const, or do a const_cast instead?

CRhinoMeshObject::GetSelectedSubObjects looks like it will be more efficient than your method.

Apart from that, sounds like standard code for scripting a command from C++.

Added to the list of requests:

Here’s what I meant, I might not be selecting the edge correctly?

	// Select a mesh
	CRhinoGetObject go;
	go.SetCommandPrompt(L"Select a mesh");
	go.GetObjects(1, 1);
	if (go.CommandResult() != CRhinoCommand::success)
		return CRhinoCommand::failure;
	const CRhinoMeshObject* object = CRhinoMeshObject::Cast(go.Object(0).Object());
	if (!object)
		return CRhinoCommand::failure;

	// Edge of interest
	int edge_index = 0;
	ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::TYPE::meshtop_edge, edge_index);

	// This doesn't work
	object->SelectSubObject(ci, true, true, true);
	//context.m_doc.AddObject(object); // cannot add const CRhinoMeshObject*
	context.m_doc.AddObject(const_cast<CRhinoMeshObject*>(object)); // doesn't help anyway
	RhinoApp().RunScript(context.m_doc.RuntimeSerialNumber(), L"_-SelEdgeLoop _Enter", 1);
	ON_SimpleArray<ON_COMPONENT_INDEX> components;
	::RhinoApp().Print("edge loop length %d\n", components.Count());
	for (int i = 0; i < components.Count(); ++i)
		::RhinoApp().Print("edge %d\n", components.At(i)->m_index);

	// This works
	CRhinoMeshObject* object_copy = new CRhinoMeshObject(*object);
	object_copy->SelectSubObject(ci, true, true, true);
	RhinoApp().RunScript(context.m_doc.RuntimeSerialNumber(), L"_-SelEdgeLoop _Enter", 1);
	ON_SimpleArray<ON_COMPONENT_INDEX> components_copy;
	::RhinoApp().Print("edge loop length %d\n", components_copy.Count());
	for (int i = 0; i < components_copy.Count(); ++i)
		::RhinoApp().Print("edge %d\n", components_copy.At(i)->m_index);

	return CRhinoCommand::success;

I think I’ll be converting my meshes to SubD anyway…
Thanks for pointing me to ON_SubDEdgeChain.