/*********************************************************************** * * ELMER, A Computational Fluid Dynamics Program. * * Copyright 1st April 1995 - , Center for Scientific Computing, * Finland. * * All rights reserved. No part of this program may be used, * reproduced or transmitted in any form or by any means * without the written permission of CSC. * * Address: Center for Scientific Computing * Tietotie 6, P.O. BOX 405 * 02101 Espoo, Finland * Tel. +358 0 457 2001 * Telefax: +358 0 457 2302 * EMail: Jari.Jarvinen@csc.fi ************************************************************************/ /*********************************************************************** Program: ELMER Front Module: ecif_bodyelement3D.cpp Language: C++ Date: 01.10.98 Version: 1.00 Author(s): Martti Verho Revisions: Abstract: Implementation ************************************************************************/ #include "ecif_body.h" #include "ecif_bodyElement2D.h" #include "ecif_bodyElement3D.h" #include "ecif_control.h" #include "ecif_geometry.h" #include "ecif_mesh.h" #include "ecif_renderer.h" extern Control* theControlCenter; //Initialize static class variables. int BodyElement3D::last_tag = 0; // Constructors. BodyElement3D::BodyElement3D() : BodyElement() { tag = newTag(); init(); update(); } //-Geometry directly defined BodyElement3D::BodyElement3D(int nof_vertices, int* vertex_ids, Geometry* pG, int cd, char* nm) : BodyElement(cd, nm) { tag = newTag(); init(); ptrGmtr = pG; calcBoundaryPoints(); update(); } //-Create a plane-element BodyElement3D::BodyElement3D(int nof_vertices, int* vertex_ids) : BodyElement() { tag = newTag(); if (nof_vertices != 4 ) return; init(); GcPoint* points[4]; modelData->nofSubElements = 4; modelData->nofVertices = 4; for (int i = 1; i < 4; i++) { modelData->subElementIds->push_back(vertex_ids[i]); modelData->vertexIds->push_back(vertex_ids[i]); BodyElement* be = model->getVertexById(vertex_ids[i]); points[i] = (GcPoint*)be->getGeometry(); } // Geometry, plane Geometry* pg = new GcPlane(4, points); ptrGmtr = pg; calcBoundaryPoints(); update(); } //-Create a nurbs-srf element BodyElement3D::BodyElement3D(int nof_vertices, int* vertex_ids, ecif_FaceGeometry_X* params) : BodyElement() { tag = newTag(); if (nof_vertices != 4 ) return; init(); GcPoint* points[4]; modelData->nofSubElements = 4; modelData->nofVertices = 4; for (int i = 1; i < 4; i++) { modelData->subElementIds->push_back(vertex_ids[i]); modelData->vertexIds->push_back(vertex_ids[i]); BodyElement* be = model->getVertexById(vertex_ids[i]); points[i] = (GcPoint*)be->getGeometry(); } // Geometry, nurbs-curve Geometry* pg = new GcNurbsSurface(params); ptrGmtr = pg; calcBoundaryPoints(); update(); } //-Create a bodyelement from model-file structure. BodyElement3D::BodyElement3D(ecif_Element_X& tx) : BodyElement(tx) { int i; IdList all_vertex_tags; if ( tag == NO_INDEX ) { tag = newTag(); } checkLastTag(); init(tx.name); ptrGmtr = NULL; // If we have some eal geometry // // NOTE: Currently we cannot have any 3D Cad-geometry! // if ( tx.nof_components > 0 ) { switch (tx.components[0]->gmtr_type) { case ECIF_LINE: ptrGmtr = new GcPlane(*tx.components[0], all_vertex_tags); break; case ECIF_NURBS: ptrGmtr = new GcNurbsSurface(*tx.components[0], all_vertex_tags); break; default: ptrGmtr = NULL; // Mesh "geometry" break; } } // NOTE: This is actually incorrect!!! // Subelements should be edges, but currently we cannot have 3D cad-geometry, so this // does not matter! // Vertices // ======== modelData->nofSubElements = all_vertex_tags.size(); modelData->nofVertices = all_vertex_tags.size();; IdList::iterator itr = all_vertex_tags.begin(); // NOTE: Tags will be converted later to ids! while ( itr != all_vertex_tags.end() ) { modelData->subElementIds->push_back(*itr); modelData->vertexIds->push_back(*itr); itr++; } //formLinearizedGeometry(); calcBoundaryPoints(); update(); } //-Empty mesh boundary with given id BodyElement3D::BodyElement3D(int int_tag, int parent1_tag, int parent2_tag,int nof_mesh_elements) : BodyElement(int_tag) { checkLastTag(tag); init(); modelData->parent1Tag = parent1_tag; modelData->parent2Tag = parent2_tag; allocateMeshElements(nof_mesh_elements); update(); } //-Mesh boundary (not fem elements added yet) BodyElement3D::BodyElement3D(int parent1_tag, int parent2_tag, int nof_mesh_elements) : BodyElement() { tag = newTag(); init(); modelData->parent1Tag = parent1_tag; modelData->parent2Tag = parent2_tag; allocateMeshElements(nof_mesh_elements); update(); } BodyElement3D::~BodyElement3D() { delete ptrGmtr; delete gridHData; delete modelData; delete meshData; } // Add all pending element ids // Returns nof elements added int BodyElement3D::addAllPendingSubElements() { int i; int nof_elements = 0; for (i = 0; i < pendingEdgeTags.size(); i++) { int be_tag = pendingEdgeTags[i]; BodyElement* be = model->getBodyElementByTag(OT_EDGE, be_tag); if (be != NULL) { addSubElement(be->Id()); nof_elements++; } } pendingEdgeTags.clear(); for (i = 0; i < pendingVertexTags.size(); i++) { int be_tag = pendingVertexTags[i]; BodyElement* be = model->getBodyElementByTag(OT_VERTEX, be_tag); if (be != NULL) { addSubElement(be->Id()); nof_elements++; } } pendingVertexTags.clear(); return nof_elements; } // Method updates the list which contains info on all bodyelements // into which this bodyelement is divided (ie. inner & outer boundaries). // Normally we first add inner boundaries into this list and outer boundaries // are added as a 'residual'. List is kept in 'consecutive' order. // This method is called (at least) from the MODEL when // inner boundaries are searched and created. // Element is marked 'unchecked' if a new common boundary is added // because we have to check outer-boundary etc. after that. void BodyElement3D::addCoveringElement(BodyElement* covering_elem, beStatus se_stat) { BodyElement3D* se = (BodyElement3D*) covering_elem; IdList* se_list = model->getCoveringElementList(this->id); // If this is first sub-element. if (se_list == 0) { se_list = new IdList; model->addCoveringElementList(this->tag, se_list); modelData->status |= BE_DEVIDED; } // Sub-element status is updated. se->addStatus(se_stat); // sub-element's direction compared to owner element (*this*) int se_direction = calcDirection(se); // New element is added in 'ascending' oder. IdList::iterator pos = se_list->begin(); bool inserted = false; // If we didn't insert above, we have to add to tail. if (!inserted) se_list->push_back(se_direction * se->Id()); // Element must be checked after an add-operation! modelData->status = modelData->status & ~BE_OUTER_CHECKED; } void BodyElement3D::addMeshBorderAsSubElement() { if ( meshData == NULL || meshData->nofMeshBorderElements == 0 ) { return; } // Create new body element, set this element's parent info also // inot new element! // int nof_elems = meshData->nofMeshBorderElements; BodyElement* se = new BodyElement2D(getParentTag(1), NO_INDEX, nof_elems); se->setParentId(1, getParentId(1)); se->setParentLayer(1, getParentLayer(1)); // Add into this element and into the model // addSubElement(se); model->addBodyElement(se); // Add border elements as mesh elements into new sub element // for (int i = 0; i < nof_elems; i++) { se->addMeshElement(meshData->meshBorderElementIds[i], meshData->meshBorderElementDirs[i]); } } // NOTE: Take care that pending edges tags are // finally added as edge ids to the element // int BodyElement3D::addPendingEdge(int edge_tag) { pendingEdgeTags.push_back(edge_tag); return 0; } // NOTE: Take care that pending vertex tags are // finally added as vertex ids to the element // int BodyElement3D::addPendingVertex(int vertex_tag) { pendingVertexTags.push_back(vertex_tag); return 0; } // Method calculates the direction of the sub-element // compared to *this* element. // Conclusion is done by calculating the parametric-values for subelement's // start- and end-points in the geometry of the parent element. int BodyElement3D::calcDirection(BodyElement* sub_elm) { int direction = 1; // Note: parent's geometry id given as the parmeter. ParamValues* pv = sub_elm->getParamValues(ptrGmtr); // If we don't have an edge, or edges are not // adjacent, we can't say anything definite if (pv->count != 2 || pv->values[0] == NULL || pv->values[1] == NULL) return 0; //Ok, direction are comparable //double u1 = pv->values[0]->u; //double u2 = pv->values[1]->u; //direction = (u1 <= u2)?1:-1; return direction; } // Method checks if element is Ok and if there is some outer boundary. // Outer boundary elements are created if necessary between two // inner boundary sub-elements.!!! // Outer-boundary info is updated into status-attribute // Returns true if outer-boundies exist. bool BodyElement3D::checkOuterBoundaries() { if ( modelData->status & (BE_OUTER_CHECKED | !BE_INCLUDES_OUTER) ) return false; // Mark that I'm outer-boundary checked! modelData->status |= BE_OUTER_CHECKED; // If this is an inner boundary, it is a single // element and cannot include (or be itself such) // outer-boundaries if ( modelData->status & BE_INNER ) return false; // No sub-elements, no inner boundary: // --> element itself is a single outer boundary; if ( !(modelData->status & (BE_DEVIDED | BE_INNER)) ) { modelData->status |= (BE_INCLUDES_OUTER | BE_OUTER); return true; } // Return value based on checked, current outer-boundary status return ( 0 != modelData->status & BE_INCLUDES_OUTER ); } // This doesn't do anything useful currently !!!###!!! // It should check if two body-elements are similarly // oriented in the space (normals are in same side etc.) int BodyElement3D::compareOrientation(BodyElement* other) { return 0; } // Method creates a new 3D face from vertices. BodyElement* BodyElement3D::createElement(int nof_vertices, int* vertex_ids, ecif_geometryType gt) { if (nof_vertices < 3) return NULL; GcPoint** points = new GcPoint*[nof_vertices]; for (int i = 0; i < nof_vertices; i++) { BodyElement* v = model->getVertexById(vertex_ids[i]); points[i] = (GcPoint*)v->getGeometry(); } BodyElement* be = NULL; Geometry* gmtr = NULL; switch (nof_vertices) { // Triangular case 3: break; // Quadrilateral case 4: // Here we should create first edges from vertices //tplg = new TcFace(nof_vertices, vertices); switch (gt) { case ECIF_LINE: gmtr = new GcPlane(4, points); break; default: gmtr = new GcPlane(4, points); break; } default: break; } if ( gmtr != NULL ) be = new BodyElement3D(nof_vertices, vertex_ids, gmtr); return be; } #if 0 void BodyElement3D::draw(Renderer* renderer, flagName geometry_type, int body_id, int direction) { // If this is a mesh boundary if (geometry_type == DRAW_SOURCE_MESH) { drawMesh(renderer, body_id, direction); return; } // Topology is here always supposed to be face-topology!!! int* vertices = 0; // There are no sub-elements. Only element itself is drawn. if ( !( modelData->status & BE_DEVIDED ) ) { ptrGmtr->draw(renderer, drawMode, direction, this->id); return; } // There are covering elements. Each covering element must be drawn separately // so that its geometry and direction is taken into account. else { IdList* se_list = model->getCoveringElementList(this->id); Geometry* pg; beStatus sub_direction; BodyElement* se; int se_id; int se_sign; // Because covering elements are stored according to parent-element's // direction, we have to take into account parent-element's direction // in the body's edege-loop when reading covering elements. IdList::iterator pos = se_list->begin(); IdList::reverse_iterator rpos = se_list->rbegin(); while (pos != se_list->end() && rpos != se_list->rend()) { // Reading forwards if (direction == 1) se_id = *pos++; // Reading backwards else se_id = *rpos++; // Check sign i.e. direction compared to parent element. se_sign = (se_id < 0)?-1:1; // Find sub-element se = model->getFace(se_sign * se_id); // Sub-element is handled pg = se->getGeometry(); // store element name renderer->name_replace(se->Id()); // NOTE positive direction is originally ccw-direction and this // is how OpenGL wants to see edge-loop data. sub_direction = direction * se_sign; pg->draw(renderer, drawMode, sub_direction, this->id); } } } #endif int BodyElement3D::findMeshBorderNodes(int buf_size, int* ids_buffer) { // In 3D border elements are edges! MeshElementTable* elements = model->getMeshBoundaryElementEdges(); int node_count = 0; // Loop all border elements for (int i = 0; i < meshData->nofMeshBorderElements; i++) { int elem_id = meshData->meshBorderElementIds[i]; meshElementCode elem_code = elements->getElementCode(elem_id); int nof_nodes = MeshElementDesc[elem_code][DESC_NOF_NODES]; const int* node_ids = elements->getNodeIds(elem_id); // Loop all nodes in each element for (int j = 0; j < nof_nodes; j++) { int node_id = node_ids[j]; // If already stored if ( ids_buffer[node_id] != NO_INDEX ) { continue; } node_count++; ids_buffer[node_id] = node_id; } } return node_count; } // Method picks outer-boundary (sub)elements within a bodyelement. // Returns a list of bodyelements. // BodyElementList* BodyElement3D::findOuterBoundary() { if ( !(modelData->status & (BE_INCLUDES_OUTER)) || modelData->status & BE_INNER ) return 0; BodyElementList* oe_list = new BodyElementList; // If bodyelement itself is an outer boundary ... if ( modelData->status & BE_OUTER ) { oe_list->push_front(this); return oe_list; } // ... otherwise we select outer-boundaries from subElements-list. IdList* se_list = model->getCoveringElementList(this->tag); IdList::iterator pos = se_list->begin(); while (pos != se_list->end()) { int se_id = *pos++; if (se_id < 0) se_id = -1 * se_id; BodyElement3D* se = (BodyElement3D*)model->getFaceById(se_id); if ( se->getStatus() & BE_OUTER ) oe_list->push_back(se); } return oe_list; } BodyElement* BodyElement3D::getSubElement(int index) { if ( modelData == NULL || modelData->nofSubElements == 0 ) return NULL; if ( index < 0 || index >= modelData->nofSubElements ) return NULL; return model->getBodyElementById((*modelData->subElementIds)[index]); } void BodyElement3D::init(char* be_name) { model->addModelObject(this, OT_FACE); initName(be_name); modelData = new BodyElementModelData; meshData = new BodyElementMeshData; } void BodyElement3D::initClass(Model* model) { BodyElement3D::last_tag = 0; } // Method checks if bodyelement is Ok. bool BodyElement3D::isOk() { // if ( ! (status & BE_OUTER_CHECKED) ) // checkOuterBoundary(); return ( 0 != modelData->status & BE_OUTER_CHECKED ); } // NOTE: Not working!!! matchType BodyElement3D::matchToLinear(BodyElement* be2, BodyElement*& common) { return MATCH_NONE; } // NOTE: Not working!!! matchType BodyElement3D::matchToNurbs(BodyElement* be2, BodyElement*& common) { return MATCH_NONE; }