/***********************************************************************
*
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1