====== Typové informace v knihovně Open Inventor ====== V jazyce C++ si rozsáhlé knihovny sami vytvářejí informace o svých typech (a každá knihovna používá odlišné datové struktury pro typové informace) Předvedeme si typové informace na příkladu třírozměrných objektů zobrazovaných pomocí knihovny [[https://cs.wikipedia.org/wiki/Open_Inventor|Open Inventor]] nebo [[https://cs.wikipedia.org/wiki/Coin3D|Coin3D]] ===== Instalace a první pokus ===== [[coin|instalace a prvni pokus]] ===== Jednoduchá scéna ===== Vytvoříme scénu obsahující kužel, kouli a krychli. \\ Výsledek je vidět na obrázku na konci kapitoly spolu se stromem objektů. * **group1** je větev stromu zobrazující červený kužel * pod //group1// je **shift1**, posun o 2 jednotky doleva * níže je **redMaterial** * a pod ním je **cone** (který by sám byl šedý) void MainWindow::simpleScene (SoSeparator * top) { // cone SoSeparator * group1 = new SoSeparator; SoTransform * shift1 = new SoTransform; shift1->translation.setValue (-2.0, 0.0, 0.0); group1->addChild (shift1); SoMaterial * redMaterial = new SoMaterial; redMaterial->diffuseColor.setValue (1.0, 0.0, 0.0); group1->addChild (redMaterial); SoCone * cone = new SoCone (); group1->addChild (cone); top->addChild (group1); // sphere SoSeparator * group2 = new SoSeparator; SoMaterial * greenMaterial = new SoMaterial; greenMaterial->diffuseColor.setValue (0.0, 1.0, 0.0); group2->addChild (greenMaterial); SoSphere * sphere = new SoSphere (); group2->addChild (sphere); top->addChild (group2); // cube SoSeparator * group3 = new SoSeparator; SoTransform * shift3 = new SoTransform (); shift3->translation.setValue (1.6, 0.0, 1.6); shift3->scaleFactor.setValue (0.7, 0.7, 0.7); group3->addChild (shift3); SoMaterial * blueMaterial = new SoMaterial; blueMaterial->diffuseColor.setValue (0.0, 0.0, 1.0); group3->addChild (blueMaterial); SoCube * cube = new SoCube (); group3->addChild (cube); top->addChild (group3); } ===== Strom zobrazující strukturu grafických objektů ===== Objekty naší grafické scény scény jsou odvozeny ze z třídy [[https://coin3d.github.io/Coin/html/classSoNode.html|SoNode]] \\ Pro každou instanci **SoNode** vytvoříme **branch** typu **TreeItem**, která bude zobrazena ve stromy typu **QTreeWidget** na levé straně okna. \\ Třída //TreeItem// je rošířením **QTreeWidgetItem** o položku **node** typu //SoNode// class TreeItem : public QTreeWidgetItem { public: SoNode * node; }; Z objektu typu //SoNode// získáme **typové informace** poskytované knihovnou //OpenInventor// SoType type = node->getTypeId (); Z typových informací získáme jméno skutečného typu objektu type.getName().getString() Zajímalo by nás, zda objekt typy //SoNode// není také odvozeného typu [[https://coin3d.github.io/Coin/html/classSoGroup.html|SoGroup]], který umí skladovat podřízené prvky typu //SoNode// Zda //SoNode// je také //SoGroup// můžeme zjistit z typových informací if ( type.isDerivedFrom ( SoGroup::getClassTypeId() ) ) Také můžeme použít **dynamic_cast** a otestovat zda výsledek není nulový ukazatel SoGroup * group = dynamic_cast (node); Pokud je objekt typy //SoGroup// získáme jednotlivé prvky a opět je zobrazíme v Qt stromu int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); } Celá funkce **displayBranch** rekuzivně zobrazující strom objektů pro zadané //node// typu //SoNode// void MainWindow::displayBranch (QTreeWidgetItem * target, SoNode * node) { TreeItem * branch = new TreeItem; branch->node = node; SoType type = node->getTypeId (); branch->setText (0, type.getName().getString()); if ( type.isDerivedFrom ( SoGroup::getClassTypeId() ) ) { SoGroup * group = dynamic_cast (node); int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); } } target->addChild (branch); } ===== Zobrazení atributů grafického objektu na základě typových informací ===== Pokud "klikneme" na uzel ve stromu na levé straně okna, zobrazíme parametry grafického objektu v tabulce na pravé straně. Pomocí **dynamic_cast** ověříme, že uzel stromu je našeho typy //TreeItem// a v něm nalezneme //node// typu //SoNode// void MainWindow::on_tree_itemActivated (QTreeWidgetItem * param, int column) { TreeItem * item = dynamic_cast (param); if (item != nullptr) displayNode (item->node); } Objekt typu //SoNode// lze přetypovat na nadřazenou třídu **SoFieldContainer**, která obsahuje **SoFieldList** se seznamem atributů. Z jednotlivých atributů **SoField** získáme jméno a hodnotu, kterou zobrazíme v tabulce SoField * field = list [i]; SbName name; obj->getFieldName (field, name); SbString value; field->get (value); displayLine (name.getString (), value.getString ()); Celá funkce **displayNode** zobrazující tabulku atributů void MainWindow::displayNode (SoNode * node) { ui->prop->clear (); SoFieldContainer * obj = node; SoFieldList list; obj->getFields (list); int len = list.getLength (); // display fields for (int i = 0; i < len; i++) { SoField * field = list [i]; // retrieve field name SbName name; obj->getFieldName (field, name); // retrieve field value SbString value; field->get (value); displayLine (name.getString (), value.getString ()); } } void MainWindow::displayLine (QString name, QString value) { QTreeWidgetItem * item = new QTreeWidgetItem (ui->prop); item->setText (0, name); item->setText (1, value); } ===== Hlavičkový soubor ===== #ifndef QT_INVENTOR_H #define QT_INVENTOR_H #include #include #include namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow ( ); private: void displayBranch (QTreeWidgetItem * target, SoNode *node); void displayNode (SoNode *node); void displayLine (QString name, QString value); void simpleScene (SoSeparator * top); private slots: void on_tree_itemActivated(QTreeWidgetItem *item, int column); private: Ui::MainWindow *ui; }; #endif // QT_INVENTOR_H ===== Program ===== #include "qt-inventor.h" #include "ui_qt-inventor.h" #include #include #include #include #include #include #include #include #include class TreeItem : public QTreeWidgetItem { public: SoNode * node; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->splitter->setStretchFactor (0, 1); ui->splitter->setStretchFactor (1, 5); ui->splitter->setStretchFactor (2, 1); ui->prop->setColumnCount (2); ui->prop->setHeaderLabels (QStringList () << "name" << "value"); SoQtExaminerViewer * examiner = new SoQtExaminerViewer (ui->widget); SoSeparator * root = new SoSeparator; simpleScene (root); // SoCone * cone = new SoCone; // root->addChild(cone); examiner->setSceneGraph(root); displayBranch (ui->tree->invisibleRootItem (), root); } MainWindow::~MainWindow() { delete ui; } void MainWindow::displayBranch (QTreeWidgetItem * target, SoNode * node) { TreeItem * branch = new TreeItem; branch->node = node; SoType type = node->getTypeId (); branch->setText (0, type.getName().getString()); if (type.isDerivedFrom (SoGroup::getClassTypeId ()) ) { SoGroup * group = dynamic_cast (node); int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); } } target->addChild (branch); } void MainWindow::displayLine (QString name, QString value) { QTreeWidgetItem * item = new QTreeWidgetItem (ui->prop); item->setText (0, name); item->setText (1, value); } void MainWindow::displayNode (SoNode * node) { ui->prop->clear (); SoFieldContainer * obj = node; SoFieldList list; obj->getFields (list); int len = list.getLength (); // display fields for (int i = 0; i < len; i++) { SoField * field = list [i]; // retrieve field name SbName name; obj->getFieldName (field, name); // retrieve field value SbString value; field->get (value); displayLine (name.getString (), value.getString ()); } } void MainWindow::on_tree_itemActivated (QTreeWidgetItem * param, int column) { TreeItem * item = dynamic_cast (param); if (item != nullptr) displayNode (item->node); } void MainWindow::simpleScene (SoSeparator * top) { // cone SoSeparator * group1 = new SoSeparator; SoTransform * shift1 = new SoTransform; shift1->translation.setValue (-2.0, 0.0, 0.0); group1->addChild (shift1); SoMaterial * redMaterial = new SoMaterial; redMaterial->diffuseColor.setValue (1.0, 0.0, 0.0); group1->addChild (redMaterial); SoCone * cone = new SoCone (); group1->addChild (cone); top->addChild (group1); // sphere SoSeparator * group2 = new SoSeparator; SoMaterial * greenMaterial = new SoMaterial; greenMaterial->diffuseColor.setValue (0.0, 1.0, 0.0); group2->addChild (greenMaterial); SoSphere * sphere = new SoSphere (); group2->addChild (sphere); top->addChild (group2); // cube SoSeparator * group3 = new SoSeparator; SoTransform * shift3 = new SoTransform (); shift3->translation.setValue (1.6, 0.0, 1.6); shift3->scaleFactor.setValue (0.7, 0.7, 0.7); group3->addChild (shift3); SoMaterial * blueMaterial = new SoMaterial; blueMaterial->diffuseColor.setValue (0.0, 0.0, 1.0); group3->addChild (blueMaterial); SoCube * cube = new SoCube (); group3->addChild (cube); top->addChild (group3); } int main (int argc, char ** argv) { QApplication app (argc, argv); SoQt::init ((QWidget*)NULL); MainWindow * win = new MainWindow(); win->show(); QObject::connect (&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); SoQt::mainLoop(); return 0; } Do .pro doplnit CONFIG += link_pkgconfig PKGCONFIG += SoQt LIBS += -lSoQt {{qtinventor.png}} [[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/tree/master/QtInventor|gitlab]] [[http://kmlinux.fjfi.cvut.cz/~culikzde/uop/QtInventor2020.zip|zip]]