plz comment on this class

karim

CAD community veteran
I have very limited time and resources and no margin for error. I want to devise this kind of class for many many objects. please kindly review and comment.

class IMaterial
{
private:
TDF_Label root;
Handle(TDataStd_RealArray) attrib;
public:
IMaterial(const TDF_Label& label): root(label)
{
TDataStd_Name::Set(root, "Material");
attrib = TDataStd_RealArray::Set(root, 0, 3);
}
void Set(double E, double nu, double a, double r)
{


attrib->SetValue(0, E);
attrib->SetValue(1, nu);
attrib->SetValue(2, a);
attrib->SetValue(3, r);
}
double E(){attrib->Value(0);}
double Nu(){attrib->Value(1);}
double A(){attrib->Value(2);}
double Rho(){attrib->Value(3);}
};

It would be much nice to hide the passing of the label to each object. might be possible with factory pattern ? ?
 

JSlyadne

Administrator
Staff member
Hello @karim!

Not sure that I got your concern.

Your code looks fine for me, only thing I would add is to incapsulate routine around TDF_Label, something like that:

Code:
class MaterialsManager
{
public:
  MaterialsManager(TDF_Label root) { // initialize m_root; }
  int AddMaterial (E, nu, aa, r) { // create a new sub-label under root label and assign the attributes there; }
  IMaterial GetMaterial(int id)  { // get the corresponding sub-label of root label by the id; }
 
private:
  TDF_Label m_root;
}

class IMaterial
{
public:
  IMaterial (id, E, Nu, A, Rho) { // initialize the fields;}

  double E()   { // return E value;   }
  double Nu()  { // return Nu value;  }
  double A()   { // return A value;   }
  double Rho() { // return Rho value; }

private:
  int        m_id;
  double m_E;
  double m_Nu;
  double m_A;
  double m_Rho;
}

What do you mean saying "many many objects"? Many objects of "material" type, or you expect other types of objects requiring also 4 values for initialization?
 

karim

CAD community veteran
Thanks. Very nice touch. I have other objects such as Sections, Load cases, Load combinations, point objects, line objects, area and solid objects that I want to store in a ocaf document. I want to write a finite element GUI. Most of the free FEM applications in the feild of structural engineering lack CAD/CAM capabilities. I like your recommendation a lot.
 

Jonathan

CAD community veteran
I like using enum for properties
enum ToolStructure {
Model = 1,
Name = 2,
GagePoint = 3,
Undersize = 4,
StartingAngles = 5,
Type = 6,
ProbeData = 7
};
I find it easier down the road to refer to the enum rather than remembering what each number does
ie:
TDF_Label GPlabel = m_root.FindChild(ToolStructure::GagePoint);
instead of
TDF_Label GPlabel = m_root.FindChild(3);

I think it makes code more readable, especially when having to go back in the code after a long period of time :)
 

Quaoar

Administrator
Staff member
It might also help to print the target OCAF structure on a sheet of paper and annotate which sublabels/subtrees mean what. Then stick it to the wall in front of you :) Unless you have abstracted all the labels/attributes with the corresponding interfaces, maintaining in brain their arrangement in memory is tricky. Having OCAF schema (even hand-painted) is like having database schema for your relational DB (the thing data architects normally start from).
 

karim

CAD community veteran
Ok. I used the ideas in the posts and changed the code to this:

class IMaterial
{
public:
IMaterial(){};
IMaterial (long id,std::string Name, double E, double Nu, double A, double Rho):m_id(id), m_Name(Name), m_E(E), m_Nu(Nu), m_A(A), m_Rho(Rho) {
}


long id(){return m_id;}
double E() { return m_E;}
double Nu() { return m_Nu; }
double A() { return m_A;}
double Rho() { return m_Rho;}
std::string Name() { return m_Name;}

private:
int m_id;
std::string m_Name; // name is the key.
double m_E;
double m_Nu;
double m_A;
double m_Rho;
};






class MaterialManager
{
public:
MaterialManager(TDF_Label root):m_root(root)
{ // initialize m_root;
TDataStd_Name::Set(m_root, "Materials");
}
int addRefrence(TDF_Label label, int id)// add
{
Handle(TDataStd_ReferenceList) RefListAttr;
auto material_label = m_root.FindChild(id, Standard_False);
if( !material_label.IsNull())
{
//no sections attached to this material yet.
if ( !material_label.FindAttribute(TDataStd_ReferenceList::GetID(), RefListAttr))
{
Handle(TDataStd_ReferenceList) RefListAttr = TDataStd_ReferenceList::Set(material_label);
RefListAttr->Append(label);
}
RefListAttr->Append(label);
}
return 1;
}
int AddMaterial(std::string Name, double E, double nu, double A, double Rho)
{ // create a new sub-label under root label and assign the attributes there;
auto material_label = TDF_TagSource::NewChild(m_root);
//TDataStd_Name::Set(material_label, Name.c_str());
Handle(TDataStd_RealArray) RealAttr = TDataStd_RealArray::Set(material_label, 0, 4);
RealAttr->SetValue(0, E);
RealAttr->SetValue(1, nu);
RealAttr->SetValue(2, A);
RealAttr->SetValue(3, Rho);
Handle(TDataStd_AsciiString) NameAttr = TDataStd_AsciiString::Set(material_label, Name.c_str());
return material_label.Tag(); // should return id;
}
auto GetMaterial(int id)
{ // get the corresponding sub-label of root label by the id;
auto material_label = m_root.FindChild(id, Standard_False);
if(!material_label.IsNull())
{
Handle(TDataStd_RealArray) RealAttr;
Handle(TDataStd_AsciiString) NameAttr;
material_label.FindAttribute(TDataStd_RealArray::GetID(), RealAttr);
material_label.FindAttribute(TDataStd_AsciiString::GetID(), NameAttr);
return IMaterial(id, std::string(NameAttr->Get().ToCString()), RealAttr->Value(0), RealAttr->Value(1),
RealAttr->Value(2), RealAttr->Value(3));
}
else
return IMaterial(); // better to throw and exception
}
auto GetMaterial(std::string name)
{
//iterate over the tags to find the name, return zero if nothing found.
Handle(TDataStd_AsciiString) NameAttr;
for (TDF_ChildIterator it (m_root, Standard_False); it.More(); it.Next())
{
auto material_label = it.Value();


material_label.FindAttribute(TDataStd_AsciiString::GetID(), NameAttr);
if(std::string(NameAttr->Get().ToCString()) == name)
{
Handle(TDataStd_RealArray) RealAttr;
material_label.FindAttribute(TDataStd_RealArray::GetID(), RealAttr);


return IMaterial(material_label.Tag(), name , RealAttr->Value(0), RealAttr->Value(1),
RealAttr->Value(2), RealAttr->Value(3));
}
}


return IMaterial(); // better to throw an exception


}
auto GetTagList()
{
std::vector<long> tags;
for (TDF_ChildIterator it (m_root, Standard_False); it.More(); it.Next())
{
auto label = it.Value();
tags.push_back(label.Tag());
}
return tags;
}


auto Count()
{
auto tags = GetTagList();
return tags.size();
}


private:
TDF_Label m_root;

};


The above code is supposed to be used as:

auto manager = MaterialManager(TDF_TagSource::NewChild(root));
int m1 = manager.AddMaterial(std::string("Concrete"), 1E5, .3, .0005, 7850);
int m2 = manager.AddMaterial(std::string("Steel"), 2E4, .25, .00005, 2500);

std::cout << "Material 1 is is tagged as " << m1 << std::endl <<
"Material 2 is tagged as " << m2 << std::endl;

auto material = manager.GetMaterial(1);
std::cout << material.E()<< std::endl;
std::cout << material.A()<< std::endl;

auto material2 = manager.GetMaterial(1);
std::cout << material2.E()<< std::endl;
std::cout << material2.A()<< std::endl;

auto size = manager.Count();
std::cout << "There are " << size << " Material in the document"<< std::endl;
auto tags = manager.GetTagList();
for(auto x:tags)
{
std::cout << "tag " << x << std::endl;
}

auto m = manager.GetMaterial("Steel");
std::cout << "E for Steel " << m.E()<< std::endl;
std::cout << "A for Steel " << m.A()<< std::endl;


What do you think ?
Should I base my other objects on this pattern ?
 
Last edited:

karim

CAD community veteran
I like using enum for properties
enum ToolStructure {
Model = 1,
Name = 2,
GagePoint = 3,
Undersize = 4,
StartingAngles = 5,
Type = 6,
ProbeData = 7
};
I find it easier down the road to refer to the enum rather than remembering what each number does
ie:
TDF_Label GPlabel = m_root.FindChild(ToolStructure::GagePoint);
instead of
TDF_Label GPlabel = m_root.FindChild(3);

I think it makes code more readable, especially when having to go back in the code after a long period of time :)


I know there are multiple ways of doing something in programming. Building on your suggestion, I decided to incorporate the enums in to the class:
as static consts. What do you think ?

class LineManager
{
public:
LineManager(TDF_Label root):m_root(root)
{
TDataStd_Name::Set( m_root, "Lines");
}
auto Add(long id, double x1, double y1, double z1, double x2, double y2, double z2)
{
auto label = TDF_TagSource::NewChild(m_root);
//TDataStd_Name::Set(label, id.c_str());


TNaming_Builder builder(label);
auto p1 = gp_Pnt(x1, y1, z1);
auto p2 = gp_Pnt(x2, y2, z2);
auto edge = BRepBuilderAPI_MakeEdge(p1, p2);
builder.Generated(edge.Shape()); // add the point object
return true;
}
auto SetOffset(long id, double x1, double y1, double z1, double x2, double y2, double z2)
{
auto label = m_root.FindChild(id); // id as tag
auto offset_label = label.FindChild(offset, Standard_True); // creat if does not exist
Handle(TDataStd_RealArray) offsets = TDataStd_RealArray::Set(offset_label, 0, 6);
offsets->SetValue(0, x1);
offsets->SetValue(1, y1);
offsets->SetValue(2, z1);
offsets->SetValue(3, x2);
offsets->SetValue(4, y2);
offsets->SetValue(5, z2);
}
auto GetOffset(long id)
{}
private:
TDF_Label m_root;
static const int offset = 10;
};
 

Quaoar

Administrator
Staff member
I think the idea of @Jonathan was to avoid numeric IDs in the invocations like TDF_Label::FindChild(). Personally, I totally share his opinion on that matter and use the same technique in Active Data (the OCAF interfacing part of Analysis Situs). It seems that in your code it's long id that acts like an enum. Do you have somewhere this enum declared?

Other than that, your code looks fine. As long as you manage to hide low-level OCAF manipulations in the interface object, it should be Okay.
 

karim

CAD community veteran
I have my section tags fixed. inside these sections, the tags are can be read from the input file. The newly added items from GUI will be added to the document using newly generated tags.
 

Jonathan

CAD community veteran
I think the idea of @Jonathan was to avoid numeric IDs in the invocations like TDF_Label::FindChild(). Personally, I totally share his opinion on that matter and use the same technique in Active Data (the OCAF interfacing part of Analysis Situs). It seems that in your code it's long id that acts like an enum. Do you have somewhere this enum declared?

Other than that, your code looks fine. As long as you manage to hide low-level OCAF manipulations in the interface object, it should be Okay.
You are right, the purpose was to avoid using integers and use 'names' instead.
 
Top