/*********************************************************************** * * 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_bodyelement_loop.cpp Language: C++ Date: 15.10.98 Version: 1.00 Author(s): Martti Verho Revisions: Abstract: Implementation ************************************************************************/ #include "ecif_bodyElementLoop.h" #include "ecif_bodyElement.h" #include "ecif_boundbox.h" #include "ecif_model.h" #include "ecif_userinterface.h" //Initialize static class variables. int BodyElementLoop::last_tag = 0; BodyElementLoop::BodyElementLoop() { tag = ++last_tag; init(); nofElements = 0; elementIds = NULL; elementTags = NULL; tplgType = CLOSED_LOOP; elementType = OT_BOUNDARY; } #if 0 BodyElementLoop::BodyElementLoop(int nof_elems, int* elem_tags, bool is_open, objectType etype) { tag = ++last_tag; init(); nofElements = nof_elems; elementIds = new int[nof_elems]; elementTags = new int[nof_elems]; for (int i = 0; i < nofElements; i++) { elementTags[i] = elem_tags[i]; elementIds[i] = NO_INDEX; } if ( is_open ) { tplgType = OPEN_LOOP; } elementType = etype; } #endif // Construct with predefined tag // BodyElementLoop::BodyElementLoop(int ext_tag, int nof_elems, int* elem_tags, bool is_open, objectType etype) { tag = ext_tag; if (last_tag < tag) last_tag = tag; init(); nofElements = nof_elems; elementIds = new int[nofElements]; elementTags = new int[nofElements]; for (int i = 0; i < nofElements; i++) { elementIds[i] = NO_INDEX; elementTags[i] = elem_tags[i]; } if ( is_open ) { tplgType = OPEN_LOOP; } elementType = etype; } BodyElementLoop::BodyElementLoop(IdList* elem_ids, bool is_open, objectType etype) { tag = ++last_tag; init(); nofElements = elem_ids->size(); elementIds = new int[nofElements]; elementTags = new int[nofElements]; int pos =0; for (IdList::iterator itr = elem_ids->begin(); itr != elem_ids->end(); itr++) { elementIds[pos] = *itr; BodyElement* be = model->getBodyElementById(*itr); if ( be != NULL ) { elementTags[pos] = be->Tag(); } else { elementTags[pos] = NO_INDEX; } pos++; } if ( is_open ) { tplgType = OPEN_LOOP; } elementType = etype; } BodyElementLoop::~BodyElementLoop() { delete[] elementIds; delete[] elementTags; delete boundbox; delete minimumbox; } bool BodyElementLoop::check() { enum geomError ge_rc = GE_NONE; // No need to check this, it is just a technical // container for elements // if ( type == LOGICAL_LOOP ) return true; update(); // If ok if ( check2D(ge_rc) ) return true; UserInterface* gui = (UserInterface*)model->getGui(); strstream strm1, strm2; switch (ge_rc) { case GE_NOT_CLOSED: strm1 << "***ERROR in geometry for the edge loop " << tag << ":" << ends; strm2 << "---Loop not closed!" << ends; gui->showMsg(strm1.str()); gui->showMsg(strm2.str(), 1); case GE_NOT_CONTINUOUS: strm1 << "***ERROR in geometry for the edge loop " << tag << ":" << ends; strm2 << "---Loop not continuous!" << ends; gui->showMsg(strm1.str()); gui->showMsg(strm2.str(), 1); } return false; } // Method checks that edge-loop is ccw oriented. bool BodyElementLoop::check2D(enum geomError& rc) { rc = GE_NONE; if ( !checkLoopSequence2D(rc) ) { return false; } // If loop is open, no need to check furtheer! // if ( tplgType == OPEN_LOOP ) return true; // We select a point which is outside the loop and from where // a ray which is parallel to x-axis won't hit any vertices // (and is not along some edge) of the loop. // Idea is to find the first bodyelement which intersect // with the ray, and from that info to conclude what is the // counter-clock-wise order for the loop. RangeVector rv; minimumbox->getRangeVector(rv); // Select a starting point whose: x-value is certainly smaller // than body's min-x-value, y-value is such that no vertices are hit. GcPoint* startp = new GcPoint(rv[0] -1, (rv[2] + rv[3]) / 2, 0); // Find the element which is hit first (looked along x-axis). double min_xval = NSVD; RayHit* isection; BodyElement* min_elm = NULL; bool negative_on_left = false; int index = 0; while (true) { BodyElement* be = getElement(index++); if (be==NULL) break; bool neg_on_left; isection = be->isectionsWithXdir(startp, neg_on_left); if (isection) { // Did we find a smaller x-value. if ( min_xval == NSVD || 1 == compare(min_xval, isection->min_value) ) { min_xval = isection->min_value; negative_on_left = neg_on_left; min_elm = be; } } } // We still have to check how element is oriented in the loop. // Without that information we can't decide which is cw- or ccw-direction. bool along = inLoopDirection(min_elm); // Default is ccw. int result = 1; // We have cw-order if: // element is in 'cw-order' (negative_on_left) and is along the loop order OR // element is in 'ccw-order' but is not along loop order. if ( ( negative_on_left && along ) || ( !negative_on_left && !along) ) { result = -1; } if ( result == -1 ) { reverseLoopOrder2D(); } return true; } // Check that elements are in correct sequence // Correct sequence if needed // Correct also element id signs if elements are in reverse // order with regard to the loop order, ie, if sign is inittially // positive althoug the second vertex should be the first // bool BodyElementLoop::checkLoopSequence2D(enum geomError& rc) { int i; rc = GE_NONE; if ( nofElements == 0 ) { rc = GE_EMPTY; return false; } if ( nofElements == 1 && tplgType != OPEN_LOOP ) { BodyElement* be = getElement(0); if ( !(be->isClosedU()) ) { rc = GE_NOT_CLOSED; return false; } else { return true; } } BodyElement* be; BodyElement* be2; BodyElement* vrtx11; BodyElement* vrtx12; BodyElement* vrtx21; BodyElement* vrtx22; int eid = elementIds[0]; int sign = (eid < 0)?-1:1; be = model->getBodyElementById(sign * eid); // Pick start vertex = end of first if ( sign == 1 ) { vrtx11 = be->getFirstSubElement(); vrtx12 = be->getLastSubElement(); } else { vrtx11 = be->getLastSubElement(); vrtx12 = be->getFirstSubElement(); } int start = 1; // Continue with other elements while ( start < nofElements ) { bool next_found = false; for (i = start; i < nofElements; i++) { int eid2= elementIds[i]; int sign2 = (eid2 < 0)?-1:1; be2 = model->getBodyElementById(sign2 * eid2); // Pick vertex to check = first of next if ( sign2 == 1 ) { vrtx21 = be2->getFirstSubElement(); vrtx22 = be2->getLastSubElement(); } else { vrtx21 = be2->getLastSubElement(); vrtx22 = be2->getFirstSubElement(); } // Next found if ( vrtx12 == vrtx21 || vrtx12 == vrtx22 ) { // If next is not in the correct palce, swap if ( start != i ) { int tmp = elementIds[start]; elementIds[start] = elementIds[i]; elementIds[i] = tmp; } // Check element2 sign and pick new start // if ( vrtx12 == vrtx21 ) { if ( sign2 == -1 ) { elementIds[start] = -1 * eid2 * sign2; } vrtx12 = vrtx22; } else if ( vrtx12 == vrtx22 ) { if ( sign2 == 1 ) { elementIds[start] = -1 * eid2 * sign2; } vrtx12 = vrtx21; } next_found = true; start++; break; } } if ( !next_found ) { rc = GE_NOT_CONTINUOUS; return false; } } // Check that loop is closed if it should be closed! // if ( tplgType == CLOSED_LOOP && vrtx11 != vrtx12 ) { rc = GE_NOT_CLOSED; return false; } return true; } bool BodyElementLoop::check3D(enum geomError& rc) { rc = GE_NONE; return true; } // Replace devided elements with their covering // elements bool BodyElementLoop::checkElements() { IdList ce_list; IdArray new_ids; int nof_elements = 0; int pos = -1; int index = 0; while (true) { BodyElement* be = getElement(index++); if (be==NULL) break; pos++; // Not devided, keep the element as it is if ( !(BE_DEVIDED & be->getStatus()) ) { new_ids.push_back(elementIds[pos]); nof_elements++; continue; } // Devided, skip the element and add the covering // elements instead // NOTE: Add them in right order! ce_list.clear(); model->getCoveringElementList(be->Id(), ce_list); int direction = (elementIds[pos] < 0)?-1:1; while ( !ce_list.empty() ) { int ce_id; // Same order, same signs if ( direction == 1 ) { ce_id = ce_list.front(); ce_list.pop_front(); // Reverse order and opposite signs } else { ce_id = -1 * ce_list.back(); ce_list.pop_back(); } new_ids.push_back(ce_id); nof_elements++; } } nofElements = nof_elements; // Find minimum tag (we start loops from minimum tag) int i; int min_tag = NO_INDEX; int min_pos = 0; for (i = 0; i < nofElements; i++) { int el_id = new_ids[i]; int be_id = (el_id < 0)?-el_id:el_id; int be_tag = NO_INDEX; BodyElement* be = model->getBodyElementById(be_id); if ( be != NULL ) { be_tag = be->Tag(); } if ( min_tag == NO_INDEX || be_tag < min_tag ) { min_tag = be_tag; min_pos = i; } } // Copy new ids delete[] elementIds; elementIds = new int[nofElements]; for (i = 0; i < nofElements; i++) { elementIds[i] = new_ids[(min_pos + i) % nofElements]; } return true; } bool BodyElementLoop::convertTags2Ids() { UserInterface* gui = (UserInterface*)model->getGui(); strstream strm1, strm2; for (int i = 0; i < nofElements; i++) { int be_tag = elementTags[i]; int sign = (be_tag < 0)?-1:1; BodyElement* be = NULL; switch (elementType) { case OT_BOUNDARY: be = model->getBoundaryByTag(sign * be_tag); break; case OT_FACE: be = model->getFaceByTag(sign * be_tag); break; case OT_EDGE: be = model->getEdgeByTag(sign * be_tag); break; case OT_VERTEX: be = model->getVertexByTag(sign * be_tag); break; } if ( be != NULL ) { // NOTE: Be ids and tags are stored unsigned // elementIds[i] = sign * be->Id(); elementTags[i] = sign * be_tag; } else { elementIds[i] = NO_INDEX; strm1 << "***ERROR in definition for the edge loop " << tag << ":" << ends; strm2 << "---Edge " << sign * be_tag << " not defined in the input!" << ends; gui->showMsg(strm1.str()); gui->showMsg(strm2.str(), 1); return false; } } return true; } BodyElement* BodyElementLoop::getBodyElementById(int be_id) { return model->getBodyElementById(be_id); } // Return signed) element id // int BodyElementLoop::getDirectedElementId(int index) { if ( index < 0 || index >= nofElements ) return 0; return elementIds[index]; } // Get body element by index // BodyElement* BodyElementLoop::getElement(int index) { if ( index < 0 || index >= nofElements ) { return NULL; } int elem_id = elementIds[index]; elem_id = (elem_id < 0)?-elem_id:elem_id; return getBodyElementById(elem_id); } // Get directed body element by index // void BodyElementLoop::getElement(int index, DirectedBodyElement& dbe) { int direction = 1; if ( index < 0 || index >= nofElements ) { dbe.element = NULL; } else { int elem_id = elementIds[index]; if (elem_id < 0) { direction = -1; elem_id = -1 * elem_id; } dbe.element = getBodyElementById(elem_id); } dbe.direction = direction; } // Return (real ie. unsigned) element id // int BodyElementLoop::getElementId(int index) { if ( index < 0 || index >= nofElements ) return NO_INDEX; int eid = elementIds[index]; return (eid < 0 )?-eid:eid; } // Return (real ie. unsigned) element tag // int BodyElementLoop::getElementTag(int index) { if ( index < 0 || index >= nofElements ) return NO_INDEX; int etg = elementTags[index]; return (etg < 0 )?-etg:etg; } BodyElement* BodyElementLoop::getLastElement() { return getElement(nofElements-1); } void BodyElementLoop::getLastElement(DirectedBodyElement& dbe) { getElement(nofElements-1, dbe); } // Get nof elements to be output into mif-file for // the body element loop // // NOTE: Normally this the number of elements in the bel, but // in multigeometry element case some bel elements may 'run out' // geometries before the first element (which defines the maximum // number of geometries to be taken into account) and the count // would be zero! // int BodyElementLoop::getNofElementMifTags(int gmtr_index) { if ( gmtr_index == NO_INDEX ) { return nofElements; } int count = 0; for (int i = 0; i < nofElements; i++) { BodyElement* be = getElement(i); if ( be == NULL ) return 0; if ( NO_INDEX != be->getMifGeometryTag(gmtr_index) ) { count += 1; } } return count; } // Total number of mif-file layer loops which must be created // for one body element loop // // NOTE: If bel starts with a single-geometry element (when we have one // layer and gmtr_index=NO_INDEX) a following muli-geometry elements creat // one mif-loop per each geoemtry component they have // // index: the multi-geometry (mif-layer index) index // int BodyElementLoop::getNofMifLoops(int gmtr_index) { int count = 0; BodyElement* be = getElement(0); if ( be == NULL ) return 0; if ( gmtr_index == NO_INDEX ) { count += be->getNofMifGeometries(); } else { if ( NO_INDEX != be->getMifGeometryTag(gmtr_index) ) { count = 1; } } return count; } void BodyElementLoop::init() { model->addModelObject(this, OT_ELEMENT_LOOP); boundbox = new BoundBox; minimumbox = new BoundBox(MAX_RANGE, MAX_RANGE); parentIds[0] = parentIds[1] = NO_INDEX; type = NORMAL_LOOP; } // Method checks if bodyelement is directed postively or negatively // in the elementloop. // True means according to loop-table order. bool BodyElementLoop::inLoopDirection(BodyElement* be) { for (int i = 0; i < nofElements; i++) { int eid = elementIds[i]; int sign = (eid < 0)?-1:1; BodyElement* my_be = model->getBodyElementById(sign * eid); if ( my_be == be ) { if ( sign < 0) return false; else return true; } } return false; } void BodyElementLoop::initClass(Model* mdl) { BodyElementLoop::last_tag = 0; } void BodyElementLoop::markActiveObjects() { for (int i = 0; i < nofElements; i++) { int be_id = elementIds[i]; int sign = (be_id < 0)?-1:1; model->markObjectActive(sign * be_id); } } // Get all element mif-tags for element loop // // NOTE: For a multi-geometry case it is possible that some elements // 'run outof' geometry components before the first multi-geometry // element and then we do not ouput any tags! // ostream& BodyElementLoop::outputDirectedElementMifTags(ostream& out, bool as_open, int indent_size, int gmtr_index, int max_per_line) { int nof_elements = getNofElementMifTags(gmtr_index); int counter = 0; int i; if ( nof_elements == 0 ) return out; for (i = 0; i < nof_elements; i++) { int eid = elementIds[i]; int sign = (eid < 0)?-1:1; BodyElement* be = model->getBodyElementById(sign * eid); int mif_tag = NO_INDEX; // Normal single-geometry call if ( gmtr_index == NO_INDEX ) { mif_tag = be->getMifGeometryTag(0); // Multi-geometry call, geometry index given } else { mif_tag = be->getMifGeometryTag(gmtr_index); } // If no geometry (component) any more available // if ( mif_tag == NO_INDEX ) continue; out << sign * mif_tag; counter++; if ( i < (nofElements - 1) ) { if ( indent_size > 0 && counter >= max_per_line ) { out << endl; indent(out, indent_size); counter = 0; } else { out << " "; } } } // NOTE: Open loops are printed like: // Edges: 6 1 2 3 -3 -2 -1 // so this will output the extra '-3 -2 -1' // if ( as_open ) { out << ' '; for (i = nof_elements-1; i >= 0; i--) { int eid = elementIds[i]; int sign = (eid < 0)?1:-1; BodyElement* be = model->getBodyElementById(-1 * sign * eid); int mif_tag = NO_INDEX; // Normal single-geometry call if ( gmtr_index == NO_INDEX ) { mif_tag = be->getMifGeometryTag(0); // Multi-geometry call, geometry index given } else { mif_tag = be->getMifGeometryTag(gmtr_index); } // If no geometry (component) any more available // if ( mif_tag == NO_INDEX ) continue; out << sign * mif_tag; counter++; if ( i > 0 ) { if ( indent_size > 0 && counter >= max_per_line ) { out << endl; indent(out, indent_size); counter = 0; } else { out << " "; } } } } return out; } ostream& BodyElementLoop::outputDirectedElementTags(ostream& out, int indent_size, int max_per_line) { int counter = 0; for (int i = 0; i < nofElements; i++) { int eid = elementIds[i]; int sign = (eid < 0)?-1:1; BodyElement* be = model->getBodyElementById(sign * eid); out << sign * be->Tag(); counter++; if ( i < (nofElements - 1) ) { if ( indent_size > 0 && counter >= max_per_line ) { out << endl; indent(out, indent_size); counter = 0; } else { out << " "; } } } return out; } ostream& BodyElementLoop::output_emf(ostream& out, short indent_size, short indent_level) { // NOTE: This is just a technical 'container' for // body elements, it is not output to emf // if ( type == LOGICAL_LOOP ) return out; short is = indent_size; short il = indent_level; // Header LibFront::output_scalar(out, is, il, "Element Loop", NULL, tag); LibFront::output_string(out, is, 1 + il, "Elements", false) << ' '; outputDirectedElementTags(out) << endl; return out; } void BodyElementLoop::reverseLoopOrder2D() { int i, j; for (i = 0, j = nofElements -1; i <= j; i++, j--) { int tmp = elementIds[i]; elementIds[i] = -1 * elementIds[j]; elementIds[j] = -1 * tmp; } } void BodyElementLoop::setParentId(int parent_index, int parent_id) { if ( parent_index < 0 | parent_index > 1 ) { return; } parentIds[parent_index] = parent_id; } void BodyElementLoop::swapElements(IdArray* ids1, IdArray* ids2, IdArray* relative_dirs) { int sign, eid, id1, id2, dir; int count = ids1->size(); for (int i = 0; i < nofElements; i++) { eid = elementIds[i]; sign = (eid < 0)?-1:1; for (int j = 0; j < count; j++) { id1 = (*ids1)[j]; id2 = (*ids2)[j]; if ( relative_dirs != NULL ) { dir = (*relative_dirs)[j]; } else { dir = 1; } if ( id1 == (sign * eid) ) { elementIds[i] = sign * dir * id2; } } } } void BodyElementLoop::update() { RangeVector rv; int index = 0; while (true) { BodyElement* be = getElement(index++); if (be==NULL) break; be->getRangeVector(rv); updateBoundBox(rv); updateMinimumBox(rv); } } void BodyElementLoop::updateBoundBox(RangeVector rv) { boundbox->extendByRange(rv); } void BodyElementLoop::updateMinimumBox(RangeVector rv) { minimumbox->restrictByRange(rv); }