Issue When Baking Leaders

Hello!

I’m working on a plug-in using C++ which will add leaders to a document. Everything is working perfectly when the number of leaders is low, but when the number of leaders gets higher (over roughly 1000), the text starts flickering on and off. I’m using a display conduit to show the leaders while you’re working in the file, then when I go to add the leaders to the document, many are missing the text, as shown in the attached image.

class LeaderConduit : public CRhinoDisplayConduit {
public:
	std::vector<DLeader::Cluster>* t_clusters = nullptr;

	LeaderConduit() : CRhinoDisplayConduit(CSupportChannels::SC_CALCBOUNDINGBOX | CSupportChannels::SC_PREDRAWOBJECTS) {
		Enable();
	}

	bool ExecConduit(CRhinoDisplayPipeline& dp, UINT nChannel, bool& bTerminate) override;
};


bool DLeader::LeaderConduit::ExecConduit(CRhinoDisplayPipeline& dp, UINT nChannel, bool& bTerminate) {
	UNREFERENCED_PARAMETER(bTerminate);
	if (t_clusters == nullptr)
		return false;
	if (nChannel == CSupportChannels::SC_PREDRAWOBJECTS) {
		for (DLeader::Cluster& c : *t_clusters) {
			for (ON_Leader& l : c.GetLeaders(true)) {
				CRhinoLeader n_leader;
				n_leader.SetLeader(l);
				n_leader.Draw(dp);
			}
		}
	}
	return true;
}


std::vector<DLeader::Cluster> DLeader::ClusterVect = std::vector<DLeader::Cluster>();
DLeader::LeaderConduit DLeader::GlobalConduit;


(I'm just using global variables for testing. The t_clusters pointer in the conduit is being set to the address of the above global vector.)


CRhinoCommand::result CCommandBakeLeaders::RunCommand(const CRhinoCommandContext& context) {
	DLeader::BakeLeaders(context);
	return CRhinoCommand::success;
}


void DLeader::BakeLeaders(const CRhinoCommandContext& a_context) {
	for (DLeader::Cluster& c : ClusterVect) {
		for (ON_Leader& l : c.GetLeaders()) {
			CRhinoLeader* n_leader = new CRhinoLeader();
			n_leader->SetLeader(l);
			a_context.m_doc.AddObject(n_leader);
		}
	}
}


std::vector<ON_Leader> DLeader::Cluster::GetLeaders(const bool& a_update) {
	if (a_update)
		UncrossPoints();
	std::vector<ON_Leader> r_vect;
	for (Item& i : t_items)
		r_vect.push_back(i.GetLeader());
	return r_vect;
}


ON_Leader DLeader::Item::GetLeader() {
	ON_Leader r_leader;
	ON_TextContent* n_content = new ON_TextContent();
	n_content->Create(stringToWchar(GetText()), ON::AnnotationType::Leader, currentDimStyle());
	r_leader.SetText(n_content);
	r_leader.SetPoints3d(3, GetLeaderPoints());
	delete n_content;
	return r_leader;
}


std::string DLeader::Item::GetText() {
	if (t_textOverride)
		return t_text;
	return textFromId(t_id);
}


inline std::string textFromId(const ON_UUID& a_id) {
	const CRhinoObject* n_object = objFromId(a_id);
	if (!n_object)
		return "NULL";
	const CRhinoPointObject* n_pointObj = CRhinoPointObject::Cast(n_object);
	if (n_pointObj) {
		ON_3dPoint n_point = n_pointObj->Point().point;
		return std::to_string(n_point.x) + ", " + std::to_string(n_point.y);
	}
	return "NULL";
}


inline const CRhinoObject* objFromId(const ON_UUID& a_id) {
	unsigned int n_serialNumber = RhinoApp().ActiveDoc()->RuntimeSerialNumber();
	return CRhinoObject::FromId(n_serialNumber, a_id);
}


inline const ON_DimStyle* currentDimStyle() {
	return &RhinoApp().ActiveDoc()->DimStyleContext().CurrentDimStyle();
}

If I’m doing anything horrible please let me know. Is there a different way I should be using the display conduit? Or is over 1000 items just too much to display in this way? I was also having trouble getting the style of the leaders to match the selected annotation style of the document, thus the bandaid solution in currentDimStyle(). Is there a resource on a better way to do this and get it to work in the display conduit? I was hoping to make them red and green, similar to the style of grasshopper.

Thank you!
Dru

Hi @dbitts960,

Try holding on to CRhinoLeader objects, rather than creating them in every time.

cmdTestLeader.cpp (4.2 KB)

– Dale

Thank you for response and the sample code.

Holding on to them is certainly the way to go. I just want to make the leaders respond immediately to changes in a Rhino object, connected via a wrapper class. I’ve found some documentation on the CRhinoEventWatcher, but is there a resource which gives examples on how one might use that to track changes in a Rhino object?

Also, I would like to make sure that the memory is cleaned up for the wrapper class by the destructor. Whenever I try freeing the memory, Rhino gives me an error.

When I call:
CRhinoLeader* myLeader = context.m_doc.CreateLeaderObject()
do I need to worry about deleting that, or can I just let it be and Rhino will manage it?

Thanks!

Hi @dbitts960,

CRhinoDoc::CreateLeaderObject allocates memory that you, the caller, is responsible for.

The sample command ends up adding the leaders to the document. Upon doing this, the document is now responsible for the allocated memory.

The conduit, in the sample, will also cleanup the memory if needed. But by the time it’s destructor is called, the contents’ of its m_leaders array should be nullptr (see the end of the command code).

As far as tracking objects, the most common way is to keep a list of uuids for the object’s that you care about. A ON_UuidList is handy for this. But you can use any structure you want.

When an object event is triggered, check to see if the object is in your tracking list. If so, set a flag indicating that you need to update something. Then when Rhino goes idle (you’ll need an idle hander), perform whatever updates are required.

– Dale