// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkHyperTreeGridGeometry3DImpl.h" #include "vtkBitArray.h" #include "vtkCellArray.h" #include "vtkDataSetAttributes.h" #include "vtkHyperTreeGrid.h" #include "vtkHyperTreeGridNonOrientedVonNeumannSuperCursor.h" #include "vtkMathUtilities.h" #include "vtkMergePoints.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkUnsignedCharArray.h" #include #include VTK_ABI_NAMESPACE_BEGIN namespace { constexpr unsigned int NUMBER_OF_POINTS = 8; constexpr unsigned int NUMBER_OF_EDGES = 12; constexpr unsigned int MAX_NUMBER_OF_INTERFACE_EDGES = 8; // Point Ids for each face of a cell constexpr unsigned int FACE_PTS_IDS[6][4] = { { 0, 1, 3, 2 }, { 0, 4, 5, 1 }, { 0, 2, 6, 4 }, { 1, 3, 7, 5 }, { 2, 6, 7, 3 }, { 4, 5, 7, 6 } }; // Edge Ids for each face of a cell constexpr unsigned int FACE_EDGES_IDS[6][4] = { { 1, 5, 3, 0 }, { 0, 4, 8, 2 }, { 2, 9, 6, 1 }, { 3, 7, 10, 4 }, { 6, 11, 7, 5 }, { 8, 10, 11, 9 } }; // Point Ids for each edge of a cell const std::vector> EDGE_PTS_IDS = { { 0, 1 } /* 0 */, { 0, 2 } /* 1 */, { 0, 4 } /* 2 */, { 1, 3 } /* 3 */, { 1, 5 } /* 4 */, { 2, 3 } /* 5 */, { 2, 6 } /* 6 */, { 3, 7 } /* 7 */, { 4, 5 } /* 8 */, { 4, 6 } /* 9 */, { 5, 7 } /* 10 */, { 6, 7 } /* 11 */ }; // Orientation axis for each edge of a cell // 0:X, 1:Y, 2:Z constexpr unsigned int EDGE_AXIS[12] = { 0, 1, 2, 1, 2, 0, 2, 2, 0, 1, 1, 0 }; // Flag used to indicate to treat all faces of a coarse cell // All bytes are set to 1 : all faces should be considered constexpr unsigned char TREAT_ALL_FACES = std::numeric_limits::max(); // Neighbor ids of the VonNeumann cursor constexpr unsigned int VON_NEUMANN_NEIGH_ID[] = { 0, 1, 2, 4, 5, 6 }; // Orientation (normal of the plane) for each face of a cell : 0:YZ, 1:XZ, 2:XY constexpr unsigned int FACE_ORIENTATION[] = { 2, 1, 0, 0, 1, 2 }; // Indicate if an offset (cell size) should be applied for a given face // Concerns the faces that do not share the cell origin constexpr unsigned int FACE_OFFSET[] = { 0, 0, 0, 1, 1, 1 }; // Arbitrary default edge index // Should be superior to 12, i.e. the number of edges of a given cell constexpr unsigned int VTK_DEFAULT_EDGE_INDEX = 42; } //------------------------------------------------------------------------------ struct vtkHyperTreeGridGeometry3DImpl::HTG3DPoint { double Coords[3] = { 0., 0., 0. }; bool IsValid = false; vtkIdType Id = -1; bool HasInterfaceA = false; bool HasInterfaceB = false; double DistanceToInterfaceA = 0.; double DistanceToInterfaceB = 0.; }; //------------------------------------------------------------------------------ vtkHyperTreeGridGeometry3DImpl::vtkHyperTreeGridGeometry3DImpl(bool mergePoints, vtkHyperTreeGrid* input, vtkPoints* outPoints, vtkCellArray* outCells, vtkDataSetAttributes* inCellDataAttributes, vtkDataSetAttributes* outCellDataAttributes, bool passThroughCellIds, const std::string& originalCellIdArrayName, bool fillMaterial) : vtkHyperTreeGridGeometryImpl(input, outPoints, outCells, inCellDataAttributes, outCellDataAttributes, passThroughCellIds, originalCellIdArrayName, fillMaterial) { if (mergePoints) { this->Locator = vtkSmartPointer::New(); this->Locator->InitPointInsertion(outPoints, input->GetBounds()); } this->BranchFactor = static_cast(this->Input->GetBranchFactor()); this->InPureMaskArray = this->Input->GetPureMask(); } //---------------------------------------------------------------------------------------------- vtkHyperTreeGridGeometry3DImpl::~vtkHyperTreeGridGeometry3DImpl() = default; //---------------------------------------------------------------------------------------------- void vtkHyperTreeGridGeometry3DImpl::GenerateGeometry() { vtkHyperTreeGrid::vtkHyperTreeGridIterator it; this->Input->InitializeTreeIterator(it); vtkIdType hyperTreeId; vtkNew cursor; // Recursively process all HyperTrees while (it.GetNextTree(hyperTreeId)) { this->Input->InitializeNonOrientedVonNeumannSuperCursor(cursor, hyperTreeId); this->RecursivelyProcessTree(cursor, ::TREAT_ALL_FACES); } } //---------------------------------------------------------------------------------------------- void vtkHyperTreeGridGeometry3DImpl::RecursivelyProcessTree( vtkHyperTreeGridNonOrientedVonNeumannSuperCursor* cursor, unsigned char coarseCellFacesToBeTreated) { vtkIdType cellId = cursor->GetGlobalNodeIndex(); // For a given cell, we can generate faces if the cell is a leaf or if the cell is masked if (cursor->IsLeaf() || this->IsMaskedOrGhost(cellId)) { // Improvement: coarseCellFacesToBeTreated can also be used there (not used for now) this->GenerateCellSurface(cursor, coarseCellFacesToBeTreated, cellId); return; } // Case of a pure, non-masked coarse cell (optimisation) if (this->InPureMaskArray && this->InPureMaskArray->GetValue(cellId) == 0) { // All child cells are in the same material, so we can only treat // the ones at the border of the coarse cell std::set childList; std::vector childCellFacesToBeTreated(cursor->GetNumberOfChildren(), 0); for (unsigned int f = 0; f < 3; ++f) // dimension { for (unsigned int o = 0; o < 2; ++o) // left, center, right { int neighborIdx = (2 * o - 1) * (f + 1); if ((coarseCellFacesToBeTreated & (1 << (3 + neighborIdx)))) { bool isValidN = cursor->HasTree(3 + neighborIdx); vtkIdType neighboringCellId = 0; if (isValidN) { neighboringCellId = cursor->GetGlobalNodeIndex(3 + neighborIdx); } if (!isValidN || this->InPureMaskArray->GetValue(neighboringCellId)) { // If the neighboring cells do not exist or are not pure, // we have border children int iMin = (f == 0 && o == 1) ? this->BranchFactor - 1 : 0; int iMax = (f == 0 && o == 0) ? 1 : this->BranchFactor; int jMin = (f == 1 && o == 1) ? this->BranchFactor - 1 : 0; int jMax = (f == 1 && o == 0) ? 1 : this->BranchFactor; int kMin = (f == 2 && o == 1) ? this->BranchFactor - 1 : 0; int kMax = (f == 2 && o == 0) ? 1 : this->BranchFactor; for (int i = iMin; i < iMax; ++i) { for (int j = jMin; j < jMax; ++j) { for (int k = kMin; k < kMax; ++k) { unsigned int ichild = i + this->BranchFactor * (j + this->BranchFactor * k); // We can request a border child cell more than one time, // once for each of it's "exposed" face childList.insert(ichild); childCellFacesToBeTreated[ichild] |= (1 << (3 + neighborIdx)); } // k } // j } // i } // if ... } } // o } // f for (std::set::iterator it = childList.begin(); it != childList.end(); ++it) { cursor->ToChild(*it); this->RecursivelyProcessTree(cursor, childCellFacesToBeTreated[*it]); cursor->ToParent(); } // ichild return; } // Search for any child cell that is not present in the coarse cell material for (unsigned int ichild = 0; ichild < cursor->GetNumberOfChildren(); ++ichild) { cursor->ToChild(ichild); this->RecursivelyProcessTree(cursor, ::TREAT_ALL_FACES); cursor->ToParent(); } } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::GenerateCellSurface( vtkHyperTreeGridNonOrientedVonNeumannSuperCursor* cursor, unsigned char vtkNotUsed(coarseCellFacesToBeTreated), vtkIdType cellId) { // Determine if the current cell contains an interface and // fill the related member variables accordingly this->ProbeForCellInterface(cellId); // Retrieve info about the current cell unsigned level = cursor->GetLevel(); bool masked = cursor->IsMasked(); const double* cellOrigin = cursor->GetOrigin(); const double* cellSize = cursor->GetSize(); std::vector cellPoints; cellPoints.resize(NUMBER_OF_POINTS); std::vector> edgePoints; edgePoints.resize(NUMBER_OF_EDGES + MAX_NUMBER_OF_INTERFACE_EDGES); std::map> internalFaceA; std::map> internalFaceB; // Iterate over all neighboring cells using the Von Neumann neighborhood for (unsigned int faceId = 0; faceId < 6; ++faceId) { const unsigned int& neighborId = ::VON_NEUMANN_NEIGH_ID[faceId]; const unsigned int& faceOrientation = ::FACE_ORIENTATION[faceId]; const unsigned int& faceOffset = ::FACE_OFFSET[faceId]; // Retrieve cursor to neighbor across face // Retrieve tree, leaf flag, and mask of neighbor cursor bool leafN = false; vtkIdType neighborCellId = 0; unsigned int levelN = 0; vtkHyperTree* treeN = cursor->GetInformation(neighborId, levelN, leafN, neighborCellId); int maskedN = cursor->IsMasked(neighborId); bool hasInterfaceCellN = this->GetHasInterface(cursor->GetGlobalNodeIndex(neighborId)); // We generate a face if one of the following conditions are fulfilled: // - The current cell is unmasked, and the neighboring cell is masked // - The current cell is unmasked, and has no neighbouring cell // - The current cell is unmasked, and has an interface // - The current cell is unmasked, and has the neighboring cell has an interface // - The current cell is masked, and has a neighbor that is a non-masked leaf of lower level // This ensures that faces between unmasked and masked cells will be generated once and only // once. if ((!masked && (!treeN || maskedN || this->HasInterfaceOnThisCell || hasInterfaceCellN)) || (masked && treeN && leafN && levelN < level && !maskedN)) { // Generate face with corresponding normal and offset // Here we differentiate the case where the current cell is masked. // In that case, we must copy the data from the neighboring cell to the created face, // and not from the current cell. this->GenerateOneCellFace(cellPoints, edgePoints, faceId, (masked ? neighborCellId : cellId), cellOrigin, cellSize, faceOffset, faceOrientation, internalFaceA, internalFaceB); } } auto createInterfacePoints = [&](std::map>& interface) { if (!interface.empty() && interface.size() >= 3) { std::vector newOutputPointIds; unsigned int firstEdge = interface.begin()->first; if (firstEdge == VTK_DEFAULT_EDGE_INDEX) { vtkWarningWithObjectMacro(nullptr, "Uninitialized edge encountered"); return; } else { HTG3DPoint* pt = interface[firstEdge].first; newOutputPointIds.emplace_back(pt->Id); unsigned int next = interface[firstEdge].second; while (next != firstEdge && next != VTK_DEFAULT_EDGE_INDEX) { // XXX: think adding an "emergency" breaking condition to // avoid potential infinite looping pt = interface[next].first; newOutputPointIds.emplace_back(pt->Id); next = interface[next].second; } if (next == VTK_DEFAULT_EDGE_INDEX) { vtkWarningWithObjectMacro(nullptr, "Uninitialized edge encountered"); return; } } // XXX: We need to clarify a criterion for valid cells // (i.e. that can be added to the output) if (!newOutputPointIds.empty()) { this->CreateNewCellAndCopyData(newOutputPointIds, cellId); } } }; // Create interface points for interface A and B if they are defined createInterfacePoints(internalFaceA); createInterfacePoints(internalFaceB); } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::GenerateOneCellFace(std::vector& cellPoints, std::vector>& edgePoints, unsigned int faceId, vtkIdType cellId, const double* cellOrigin, const double* cellSize, unsigned int offset, unsigned int orientation, std::map>& internalFaceA, std::map>& internalFaceB) { double pt[3] = { 0., 0., 0. }; // We compute the current cell points coordinates only if we didn't do it before // (i.e. if IsValid == false for a given point) // XXX: The code below can be reworked. We can think about computing all // cell points once and for all before calling this function and remove all // the logic below. HTG3DPoint* currentPt = &cellPoints[::FACE_PTS_IDS[faceId][0]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][1]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][2]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { memcpy(pt, cellOrigin, 3 * sizeof(double)); if (offset) { pt[orientation] += cellSize[orientation]; } unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); } } else { memcpy(pt, cellOrigin, 3 * sizeof(double)); if (offset) { pt[orientation] += cellSize[orientation]; } unsigned int axis1 = (orientation + 1) % 3; unsigned int axis2 = (orientation + 2) % 3; pt[axis1] += cellSize[axis1]; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { pt[axis1] = cellOrigin[axis1]; this->SetXYZ(*currentPt, pt); } } } else { memcpy(pt, cellOrigin, 3 * sizeof(double)); if (offset) { pt[orientation] += cellSize[orientation]; } unsigned int axis1 = (orientation + 1) % 3; pt[axis1] += cellSize[axis1]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][2]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); } } else { unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { pt[axis1] = cellOrigin[axis1]; this->SetXYZ(*currentPt, pt); } } } } else { memcpy(pt, cellOrigin, 3 * sizeof(double)); if (offset) { pt[orientation] += cellSize[orientation]; } this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][1]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][2]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); } } else { unsigned int axis1 = (orientation + 1) % 3; unsigned int axis2 = (orientation + 2) % 3; pt[axis1] += cellSize[axis1]; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { pt[axis1] = cellOrigin[axis1]; this->SetXYZ(*currentPt, pt); } } } else { unsigned int axis1 = (orientation + 1) % 3; pt[axis1] += cellSize[axis1]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][2]]; if (currentPt->IsValid) { currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); } } else { unsigned int axis2 = (orientation + 2) % 3; pt[axis2] += cellSize[axis2]; this->SetXYZ(*currentPt, pt); currentPt = &cellPoints[::FACE_PTS_IDS[faceId][3]]; if (!currentPt->IsValid) { pt[axis1] = cellOrigin[axis1]; this->SetXYZ(*currentPt, pt); } } } } // Storage for new face vertex IDs std::vector outputIndexPoints; unsigned int currentEdgePointA = VTK_DEFAULT_EDGE_INDEX; vtkIdType lastId = -1; unsigned int currentEdgePointB = VTK_DEFAULT_EDGE_INDEX; // Iterate over the edges of the current face to add face points. // If there is no interface, simply insert the 4 points of the face. // If one or two interfaces pass through the cell, also compute the // additional points of the interface (intersection between the interface and // the edges of the cell). for (unsigned int edgeId = 0; edgeId < 4; ++edgeId) { unsigned int faceEdgeId = ::FACE_EDGES_IDS[faceId][edgeId]; std::pair edgePointId = ::EDGE_PTS_IDS[faceEdgeId]; this->ComputeEdge(cellPoints[edgePointId.first], cellPoints[edgePointId.second], edgePoints, ::EDGE_AXIS[faceEdgeId], faceEdgeId, internalFaceA, internalFaceB, currentEdgePointA, currentEdgePointB); // The order of points insertion is important in order to considerate all face points. // Regarding the way IDs are stored in FACE_PTS_IDS, FACE_EDGES_IDS and EDGE_PTS_IDS, // we have to retrieve the first point of the edge for the 1st and 2nd edges, // and the second point for the 3rd and 4th edges of the face. std::vector points; if (edgeId < 2) { points.emplace_back(&cellPoints[edgePointId.first]); // first vertex quad face points.emplace_back(&edgePoints[faceEdgeId].first); // first point one edge points.emplace_back(&edgePoints[faceEdgeId].second); // second point one edge } else { points.emplace_back(&cellPoints[edgePointId.second]); // second vertex quad face points.emplace_back(&edgePoints[faceEdgeId].second); // second point one edge points.emplace_back(&edgePoints[faceEdgeId].first); // first point one edge } for (auto point : points) { if (point->IsValid) { vtkIdType pointId = -1; if (this->IsInside(*point)) { pointId = this->InsertUniquePoint(*point); } if (pointId >= 0 && pointId != lastId) // lastId is used to avoid repetitions { outputIndexPoints.emplace_back(pointId); lastId = pointId; } } } } // Insert a new face if (outputIndexPoints.size() > 2) { this->CreateNewCellAndCopyData(outputIndexPoints, cellId); } } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::SetInterfaceFace(unsigned int edgeId, std::map>& internalFace, vtkHyperTreeGridGeometry3DImpl::HTG3DPoint* point) { if (internalFace.count(edgeId) <= 0) { internalFace[edgeId].first = point; internalFace[edgeId].second = VTK_DEFAULT_EDGE_INDEX; } } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::CompleteLinkage( std::map>& internalFace, unsigned int edgePointId1, unsigned int edgePointId2) { if (edgePointId1 == VTK_DEFAULT_EDGE_INDEX || edgePointId2 == VTK_DEFAULT_EDGE_INDEX) { // XXX: this seems to happen quite often. // We must clarify if it is intended return; } if (edgePointId1 == edgePointId2) { vtkErrorWithObjectMacro(nullptr, "Edge with 2 identical points found !"); return; } // Build link between edgePointId1 et edgePointId2 // XXX: need to clarify naming for nextEdgePointId1 and nextEdgePointId2 unsigned int nextEdgePointId1 = internalFace[edgePointId1].second; unsigned int nextEdgePointId2 = internalFace[edgePointId2].second; if (nextEdgePointId1 == VTK_DEFAULT_EDGE_INDEX) { if (nextEdgePointId2 == VTK_DEFAULT_EDGE_INDEX || nextEdgePointId2 != edgePointId1) { // Arbitrary choice of linking direction, // or nextEdgePointId2 already describes a different linking internalFace[edgePointId1].second = edgePointId2; } } else if (nextEdgePointId2 == VTK_DEFAULT_EDGE_INDEX) { // Arbitrary choice of linking direction internalFace[edgePointId2].second = edgePointId1; } else if (nextEdgePointId2 != edgePointId1) { // If nextEdgePointId1 is involved in a different linkage, this is also the case for // nextEdgePointId2 We have to invert both linkages and connect them together std::vector chainette; chainette.emplace_back(edgePointId1); unsigned int next = internalFace[edgePointId1].second; while (next != VTK_DEFAULT_EDGE_INDEX) { // XXX: think adding an "emergency" breaking condition to // avoid potential infinite looping chainette.emplace_back(next); next = internalFace[next].second; } unsigned int current = VTK_DEFAULT_EDGE_INDEX; for (std::vector::reverse_iterator it = chainette.rbegin(); it != chainette.rend(); ++it) { if (current == VTK_DEFAULT_EDGE_INDEX) { current = *it; } else { next = *it; internalFace[current].second = next; current = next; } } if (current != edgePointId1) { vtkWarningWithObjectMacro(nullptr, << "Unexpected edge encountered. Expected " << edgePointId1 << ", got " << current << "."); } internalFace[current].second = edgePointId2; } } //------------------------------------------------------------------------------ bool vtkHyperTreeGridGeometry3DImpl::ComputeEdgeInterface(const HTG3DPoint& firstPoint, const HTG3DPoint& secondPoint, std::vector>& edgePoints, unsigned int edgeAxis, unsigned int edgeId, std::map>& internalFace, HTG3DPoint& pointInter, unsigned int& edgePointId, bool isInterfaceA) { if (!firstPoint.IsValid) { vtkWarningWithObjectMacro(nullptr, "First edge point is invalid."); } if (!secondPoint.IsValid) { vtkWarningWithObjectMacro(nullptr, "Second edge point is invalid."); } auto firstPointDist = (isInterfaceA ? firstPoint.DistanceToInterfaceA : firstPoint.DistanceToInterfaceB); auto secondPointDist = (isInterfaceA ? secondPoint.DistanceToInterfaceA : secondPoint.DistanceToInterfaceB); if (firstPointDist == 0.) { if (secondPointDist == 0.) { // Edge case : the interface corresponds to the edge // XXX: need to clarify naming for iEdgePoint1 and iEdgePoint2 unsigned int iEdgePoint1 = ::EDGE_PTS_IDS[edgeId].first + NUMBER_OF_EDGES; edgePoints[iEdgePoint1].first = firstPoint; edgePoints[iEdgePoint1].second.IsValid = false; this->SetInterfaceFace(iEdgePoint1, internalFace, &edgePoints[iEdgePoint1].first); unsigned int iEdgePoint2 = ::EDGE_PTS_IDS[edgeId].second + NUMBER_OF_EDGES; edgePoints[iEdgePoint2].first = secondPoint; edgePoints[iEdgePoint2].second.IsValid = false; this->SetInterfaceFace(iEdgePoint2, internalFace, &edgePoints[iEdgePoint2].first); this->CompleteLinkage(internalFace, iEdgePoint1, iEdgePoint2); return true; } // The interface point is a the first point of the cell pointInter = firstPoint; edgePointId = ::EDGE_PTS_IDS[edgeId].first + NUMBER_OF_EDGES; } else if (secondPointDist == 0.) { // The interface point is a the second point of the cell pointInter = secondPoint; edgePointId = ::EDGE_PTS_IDS[edgeId].second + NUMBER_OF_EDGES; } else if (firstPointDist * secondPointDist < 0.) { // Compute the position of the interface point on the edge, // between the first and the second point double xyz[3] = { 0., 0., 0. }; memcpy(xyz, firstPoint.Coords, 3 * sizeof(double)); xyz[edgeAxis] = (secondPointDist * firstPoint.Coords[edgeAxis] - firstPointDist * secondPoint.Coords[edgeAxis]) / (secondPointDist - firstPointDist); this->SetIntersectXYZ(pointInter, xyz, isInterfaceA); if (pointInter.Coords[edgeAxis] == firstPoint.Coords[edgeAxis] || pointInter.Coords[edgeAxis] == secondPoint.Coords[edgeAxis]) { vtkWarningWithObjectMacro(nullptr, "Interface point coincide with an edge point"); pointInter.IsValid = false; } edgePointId = edgeId; } return false; } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::ComputeEdge(const HTG3DPoint& firstPoint, const HTG3DPoint& secondPoint, std::vector>& edgePoints, unsigned int edgeAxis, unsigned int edgeId, std::map>& internalFaceA, std::map>& internalFaceB, unsigned int& currentEdgePointA, unsigned int& currentEdgePointB) { HTG3DPoint pointA, pointB; unsigned int iEdgePointA = edgeId, iEdgePointB = edgeId; // Compute the intersection point for the first interface, if any. if (firstPoint.HasInterfaceA && this->ComputeEdgeInterface(firstPoint, secondPoint, edgePoints, edgeAxis, edgeId, internalFaceA, pointA, iEdgePointA, true)) { return; } // Compute the intersection point for the second interface, if any. if (firstPoint.HasInterfaceB && this->ComputeEdgeInterface(firstPoint, secondPoint, edgePoints, edgeAxis, edgeId, internalFaceB, pointB, iEdgePointB, false)) { return; } // If intersection points are computed, add them to the construction (linkage) // of the interface faces if (pointA.IsValid) { if (pointB.IsValid) { if (pointA.Coords[edgeAxis] < pointB.Coords[edgeAxis]) { if (edgeId == iEdgePointA && edgeId == iEdgePointB) { edgePoints[edgeId].first = pointA; this->SetInterfaceFace(edgeId, internalFaceA, &edgePoints[edgeId].first); this->CompleteLinkage(internalFaceA, currentEdgePointA, edgeId); currentEdgePointA = edgeId; edgePoints[edgeId].second = pointB; this->SetInterfaceFace(edgeId, internalFaceB, &edgePoints[edgeId].second); this->CompleteLinkage(internalFaceB, currentEdgePointB, edgeId); currentEdgePointB = edgeId; } else { edgePoints[iEdgePointA].first = pointA; this->SetInterfaceFace(iEdgePointA, internalFaceA, &edgePoints[iEdgePointA].first); this->CompleteLinkage(internalFaceA, currentEdgePointA, iEdgePointA); currentEdgePointA = iEdgePointA; edgePoints[iEdgePointB].second = pointB; this->SetInterfaceFace(iEdgePointB, internalFaceB, &edgePoints[iEdgePointB].second); this->CompleteLinkage(internalFaceB, currentEdgePointB, iEdgePointB); currentEdgePointB = iEdgePointB; } } if (pointA.Coords[edgeAxis] > pointB.Coords[edgeAxis]) { if (edgeId == iEdgePointA && edgeId == iEdgePointB) { edgePoints[edgeId].first = pointB; this->SetInterfaceFace(edgeId, internalFaceB, &edgePoints[edgeId].first); this->CompleteLinkage(internalFaceB, currentEdgePointB, edgeId); currentEdgePointB = edgeId; edgePoints[edgeId].second = pointA; this->SetInterfaceFace(edgeId, internalFaceA, &edgePoints[edgeId].second); this->CompleteLinkage(internalFaceA, currentEdgePointA, edgeId); currentEdgePointA = edgeId; } else { edgePoints[iEdgePointA].first = pointA; edgePoints[iEdgePointA].second.IsValid = false; this->SetInterfaceFace(iEdgePointA, internalFaceA, &edgePoints[iEdgePointA].first); this->CompleteLinkage(internalFaceA, currentEdgePointA, iEdgePointA); currentEdgePointA = iEdgePointA; edgePoints[iEdgePointB].second = pointB; edgePoints[iEdgePointB].second.IsValid = false; this->SetInterfaceFace(iEdgePointB, internalFaceB, &edgePoints[iEdgePointB].second); this->CompleteLinkage(internalFaceB, currentEdgePointB, iEdgePointB); currentEdgePointB = iEdgePointB; } } } else { edgePoints[iEdgePointA].first = pointA; this->SetInterfaceFace(iEdgePointA, internalFaceA, &edgePoints[iEdgePointA].first); this->CompleteLinkage(internalFaceA, currentEdgePointA, iEdgePointA); currentEdgePointA = iEdgePointA; } } else if (pointB.IsValid) { edgePoints[iEdgePointB].first = pointB; this->SetInterfaceFace(iEdgePointB, internalFaceB, &edgePoints[iEdgePointB].first); this->CompleteLinkage(internalFaceB, currentEdgePointB, iEdgePointB); currentEdgePointB = iEdgePointB; } } //------------------------------------------------------------------------------ bool vtkHyperTreeGridGeometry3DImpl::IsInside(const HTG3DPoint& point) { if (!point.IsValid) { return false; } if (this->CellInterfaceType == -1) { if (point.HasInterfaceA) { if (point.DistanceToInterfaceA < 0) { return false; } } return true; } else if (this->CellInterfaceType == 0) { if (point.DistanceToInterfaceA > 0) { return false; } if (point.DistanceToInterfaceB < 0) { return false; } return true; } else if (this->CellInterfaceType == 1) { if (point.HasInterfaceB) { if (point.DistanceToInterfaceB > 0) { return false; } } return true; } else { // Pure cell return true; } } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::SetXYZ(HTG3DPoint& point, const double* coords) { point.Coords[0] = coords[0]; point.Coords[1] = coords[1]; point.Coords[2] = coords[2]; point.Id = -1; if (this->HasInterfaceOnThisCell) { if (this->CellInterfaceType != 1.) { point.HasInterfaceA = true; point.DistanceToInterfaceA = this->ComputeDistanceToInterfaceA(point.Coords); } if (this->CellInterfaceType != -1.) { point.HasInterfaceB = true; point.DistanceToInterfaceB = this->ComputeDistanceToInterfaceB(point.Coords); } } point.IsValid = true; } //------------------------------------------------------------------------------ void vtkHyperTreeGridGeometry3DImpl::SetIntersectXYZ( HTG3DPoint& point, const double* coords, bool isInterfaceA) { point.Coords[0] = coords[0]; point.Coords[1] = coords[1]; point.Coords[2] = coords[2]; point.Id = -1; if (isInterfaceA) { point.HasInterfaceA = true; point.DistanceToInterfaceA = 0.; if (this->HasInterfaceOnThisCell && this->CellInterfaceType != -1.) { point.HasInterfaceB = true; point.DistanceToInterfaceB = this->ComputeDistanceToInterfaceB(point.Coords); } else { point.HasInterfaceB = false; } } else { point.HasInterfaceB = true; point.DistanceToInterfaceB = 0.; if (this->HasInterfaceOnThisCell && this->CellInterfaceType != 1.) { point.HasInterfaceA = true; point.DistanceToInterfaceA = this->ComputeDistanceToInterfaceA(point.Coords); // 1 is A } } point.IsValid = true; } //------------------------------------------------------------------------------ vtkIdType vtkHyperTreeGridGeometry3DImpl::InsertUniquePoint(HTG3DPoint& point) { if (point.IsValid && point.Id < 0) { // Insert a point if (this->Locator) { this->Locator->InsertUniquePoint(point.Coords, point.Id); } else { point.Id = this->OutPoints->InsertNextPoint(point.Coords); } } return point.Id; } //---------------------------------------------------------------------------------------------- bool vtkHyperTreeGridGeometry3DImpl::GetHasInterface(vtkIdType cellId) const { // Only useful in 3D, this method makes it possible to know if the neighboring cell // of _inputCellIndex offset is pure or describes an interface. // It is pure if: // - there is no defined interface (m_hasInterface); // - there is no description of the interfaces (m_inputIntercepts); // - there is a description of the interfaces but the mixed cell type is not 2 // (pure cell) (m_inputIntercepts[2]); -1 and 1 describes a case of a mixed // cell of a material with a single interface, 0 a case of a mixed cell of a // material with a double interface; // - there is no description of the normals (m_inputNormals); // - there is a description of the normals but not zero. if (cellId < 0) { return false; } if (!this->HasInterface) { return false; } bool ret = (this->InIntercepts && ((this->InIntercepts->GetTuple(cellId))[2] < 2) && this->InNormals); if (ret) { double* normal = this->InNormals->GetTuple(cellId); ret = !(normal[0] == 0. && normal[1] == 0. && normal[2] == 0.); } return ret; } VTK_ABI_NAMESPACE_END