int Line :: computeNumberOfIntersectionPoints(Element *element) { int numIntersec = 0; const double relTol = 1.0e-3; const double LineLength = giveLength(); const double absTol = relTol*std::max(LineLength, XfemTolerances::giveCharacteristicElementLength() ); const int numEdges = element->giveInterpolation()->giveNumberOfEdges(); for ( int edgeIndex = 1; edgeIndex <= numEdges; edgeIndex++ ) { IntArray bNodes; element->giveInterpolation()->boundaryGiveNodes(bNodes, edgeIndex); const int nsLoc = bNodes.at(1); const int neLoc = bNodes.at( bNodes.giveSize() ); FloatArray xS = *(element->giveNode(nsLoc)->giveCoordinates() ); xS.resizeWithValues(2); FloatArray xE = *(element->giveNode(neLoc)->giveCoordinates() ); xE.resizeWithValues(2); const double dist = BasicGeometry :: computeLineDistance(xS, xE, mVertices[0], mVertices[1]); if(dist < absTol) { numIntersec++; } } return numIntersec; }
int StructuralInterfaceMaterial :: giveIPValue(FloatArray &answer, GaussPoint *gp, InternalStateType type, TimeStep *tStep) { StructuralInterfaceMaterialStatus *status = static_cast< StructuralInterfaceMaterialStatus * >( this->giveStatus(gp) ); if ( type == IST_InterfaceJump ) { answer = status->giveJump(); answer.resizeWithValues(3); // In case some model is not storing all components. return 1; } else if ( type == IST_InterfaceTraction ) { answer = status->giveTraction(); answer.resizeWithValues(3); return 1; } else if ( type == IST_InterfaceFirstPKTraction ) { answer = status->giveFirstPKTraction(); answer = status->giveTempFirstPKTraction(); answer.resizeWithValues(3); return 1; } else if ( type == IST_DeformationGradientTensor ) { answer.beVectorForm( status->giveF() ); return 1; } else if ( type == IST_InterfaceNormal ) { answer = status->giveNormal(); return 1; } else { return Material :: giveIPValue(answer, gp, type, tStep); } }
void DofManager :: giveUnknownVector(FloatArray &answer, const IntArray &dofIDArry, PrimaryField &field, ValueModeType mode, TimeStep *tStep, bool padding) { answer.resize( dofIDArry.giveSize() ); int k = 0; for ( auto &dofid: dofIDArry ) { auto pos = this->findDofWithDofId( ( DofIDItem ) dofid ); if ( pos == this->end() ) { if ( padding ) { answer.at(++k) = 0.; continue; } else { continue; } } answer.at(++k) = (*pos)->giveUnknown(field, mode, tStep); } answer.resizeWithValues(k); // Transform to global c.s. FloatMatrix L2G; if ( this->computeL2GTransformation(L2G, dofIDArry) ) { answer.rotatedWith(L2G, 'n'); } }
double Tetrah1_ht :: SpatialLocalizerI_giveDistanceFromParametricCenter(const FloatArray &coords) { FloatArray lcoords(3), gcoords; double dist; int size, gsize; lcoords.zero(); this->computeGlobalCoordinates(gcoords, lcoords); if ( ( size = coords.giveSize() ) < ( gsize = gcoords.giveSize() ) ) { OOFEM_ERROR("coordinates size mismatch"); } if ( size == gsize ) { dist = coords.distance(gcoords); } else { FloatArray helpCoords = coords; helpCoords.resizeWithValues(gsize); dist = helpCoords.distance(gcoords); } return dist; }
void IntElPoint :: computeLocalSlipDir(FloatArray &normal) { normal.resizeWithValues(3); if ( this->referenceNode ) { // normal normal.beDifferenceOf(*domain->giveNode(this->referenceNode)->giveCoordinates(), *this->giveNode(1)->giveCoordinates()); } else { if ( normal.at(1) == 0 && normal.at(2) == 0 && normal.at(3) == 0 ) { OOFEM_ERROR("Normal is not defined (referenceNode=0,normal=(0,0,0))"); } } normal.normalize(); }
int TransportMaterial :: giveIPValue(FloatArray &answer, GaussPoint *gp, InternalStateType type, TimeStep *tStep) // IST_Humidity must be overriden! { TransportMaterialStatus *ms = static_cast< TransportMaterialStatus * >( this->giveStatus(gp) ); if ( type == IST_Temperature || type == IST_MassConcentration_1 || type == IST_Humidity ) { FloatArray vec = ms->giveField(); answer = FloatArray{vec.at( ( type == IST_Temperature ) ? 1 : 2 ) }; return 1; } else if ( type == IST_TemperatureFlow ) { TransportElement *transpElem = static_cast< TransportElement * >( gp->giveElement() ); transpElem->computeFlow(answer, gp, tStep); return 1; } else if ( type == IST_Velocity ) { ///@todo Shouldn't be named velocity.. instead, "MassFlow" or something suitable like that. answer = ms->giveFlux(); answer.resizeWithValues(3); return 1; } else if ( type == IST_PressureGradient ) { answer = ms->giveGradient(); answer.resizeWithValues(3); return 1; } else if ( type == IST_Density ) { answer = FloatArray{ this->give('d', gp) }; return 1; } else if ( type == IST_HeatCapacity ) { answer = FloatArray{ this->give('c', gp) }; return 1; } else if ( type == IST_ThermalConductivityIsotropic ) { answer = FloatArray{ this->give('k', gp) }; return 1; } else if ( type == IST_Maturity ) { answer = FloatArray{ ms->giveMaturity() }; return 1; } return Material :: giveIPValue(answer, gp, type, tStep); }
void GradDpElement :: computeNonlocalDegreesOfFreedom(FloatArray &answer, TimeStep *tStep) { ///@todo Just use this instead! (should work, but I don't have any tests right now) #if 0 this->giveStructuralElement()->computeVectorOf({G_0}, VM_Total, tStep, answer); #else StructuralElement *elem = this->giveStructuralElement(); FloatArray u; answer.resize(nlSize); elem->computeVectorOf(VM_Total, tStep, u); u.resizeWithValues(totalSize); for ( int i = 1; i <= nlSize; i++ ) { answer.at(i) = u.at( locK.at(i) ); } #endif }
void InterfaceElem1d :: computeLocalSlipDir(FloatArray &normal) { normal.resizeWithValues(3); if ( this->referenceNode ) { // normal normal.at(1) = domain->giveNode(this->referenceNode)->giveCoordinate(1) - this->giveNode(1)->giveCoordinate(1); normal.at(2) = domain->giveNode(this->referenceNode)->giveCoordinate(2) - this->giveNode(1)->giveCoordinate(2); normal.at(3) = domain->giveNode(this->referenceNode)->giveCoordinate(3) - this->giveNode(1)->giveCoordinate(3); } else { if ( normal.at(1) == 0 && normal.at(2) == 0 && normal.at(3) == 0 ) { _error("computeLocalSlipDir: normal is not defined (referenceNode=0,normal=(0,0,0))"); } } normal.normalize(); }
void PrescribedGradientBCWeak :: createTractionMesh(bool iEnforceCornerPeriodicity, int iNumSides) { bool split_at_holes = true; const double l_s = mUC[0] - mLC[0]; const double minPointDist = 1.0e-4*l_s; // Find holes intersecting the RVE boundary so that these can be excluded std::vector<FloatArray> holeCoordUnsorted, allCoordUnsorted; findHoleCoord(holeCoordUnsorted, allCoordUnsorted); // Add corner points holeCoordUnsorted.push_back( {mUC[0], mLC[1]} ); allCoordUnsorted.push_back( {mUC[0], mLC[1]} ); holeCoordUnsorted.push_back( {mUC[0], mUC[1]} ); allCoordUnsorted.push_back( {mUC[0], mUC[1]} ); holeCoordUnsorted.push_back( {mLC[0], mUC[1]} ); allCoordUnsorted.push_back( {mLC[0], mUC[1]} ); // Add crack-boundary intersections findCrackBndIntersecCoord(holeCoordUnsorted); // Add periodicity points findPeriodicityCoord(holeCoordUnsorted); // Sort arrays in terms of arc length along the RVE boundary std :: sort( holeCoordUnsorted.begin(), holeCoordUnsorted.end(), ArcPosSortFunction4( mLC, mUC, 1.0e-4 ) ); std :: sort( allCoordUnsorted.begin(), allCoordUnsorted.end(), ArcPosSortFunction4( mLC, mUC, 1.0e-4 ) ); // Remove points that are too close to each other removeClosePoints(holeCoordUnsorted, minPointDist); removeClosePoints(allCoordUnsorted, minPointDist); // Create two arrays of segments, where each array represents the coarsest possible traction // mesh on one side of the RVE ArcPosSortFunction4 arcPosFunc( mLC, mUC, 1.0e-4 ); std :: vector< TracSegArray * > tracElNew0, tracElNew1; tracElNew0.push_back(new TracSegArray()); tracElNew1.push_back(new TracSegArray()); for(size_t i = 1; i < holeCoordUnsorted.size(); i++) { FloatArray xS = holeCoordUnsorted[i-1]; xS.resizeWithValues(2); FloatArray xE = holeCoordUnsorted[i]; xE.resizeWithValues(2); const FloatArray xC = {0.5*(xS[0]+xE[0]), 0.5*(xS[1]+xE[1])}; if( arcPosFunc.calcArcPos(xC) < 2.*l_s) { tracElNew0[0]->mInteriorSegments.push_back( Line(xS, xE) ); } else { tracElNew1[0]->mInteriorSegments.push_back( Line(xS, xE) ); } } // Remove segments located in holes removeSegOverHoles(*(tracElNew0[0]), 1.0e-4); removeSegOverHoles(*(tracElNew1[0]), 1.0e-4); // TODO: Refinement. if(split_at_holes) { splitSegments(tracElNew0); splitSegments(tracElNew1); } // Identify additional points that can be used to refine the traction mesh ////////////////////////////////////////////////// // Create traction dofs int numNodes = domain->giveNumberOfDofManagers(); int totNodesCreated = 0; // For each side (0 and 1), loop over over elements // We may always create the first node on the element. // For the linear approximation, it may need to be a slave node, // depending on which element it is. // For now, consider only piecewise constant approximations. Then, // we can always create on node on each element. // RVE side at x=L for( TracSegArray* el : tracElNew0 ) { ////////////////////////////////////////////////////// // Create first node totNodesCreated++; el->mFirstNode.reset( new Node(numNodes + 1, domain) ); el->mFirstNode->setGlobalNumber(numNodes + 1); for ( auto &dofId: giveTracDofIDs() ) { el->mFirstNode->appendDof( new MasterDof(el->mFirstNode.get(), ( DofIDItem ) dofId) ); } el->mFirstNode->setCoordinates( el->mInteriorSegments[0].giveVertex(1) ); numNodes++; } // RVE side at y=L for( TracSegArray* el : tracElNew1 ) { ////////////////////////////////////////////////////// // Create first node totNodesCreated++; el->mFirstNode.reset( new Node(numNodes + 1, domain) ); el->mFirstNode->setGlobalNumber(numNodes + 1); for ( auto &dofId: giveTracDofIDs() ) { el->mFirstNode->appendDof( new MasterDof(el->mFirstNode.get(), ( DofIDItem ) dofId) ); } el->mFirstNode->setCoordinates( el->mInteriorSegments[0].giveVertex(1) ); numNodes++; } if ( mMeshIsPeriodic ) { // Lock displacement in one node if we use periodic BCs int numNodes = domain->giveNumberOfDofManagers(); mpDisplacementLock = new Node(numNodes + 1, domain); mLockNodeInd = domain->giveElement(1)->giveNode(1)->giveGlobalNumber(); for ( auto &dofid: giveDispLockDofIDs() ) { mpDisplacementLock->appendDof( new MasterDof(mpDisplacementLock, ( DofIDItem ) dofid) ); } } mpTracElNew.reserve(tracElNew0.size() + tracElNew1.size()); std::move(tracElNew0.begin(), tracElNew0.end(), std::inserter(mpTracElNew, mpTracElNew.end())); std::move(tracElNew1.begin(), tracElNew1.end(), std::inserter(mpTracElNew, mpTracElNew.end())); tracElNew0.clear(); tracElNew1.clear(); //////////// // Segment arrays for Gauss quadrature size_t i = 0; for( TracSegArray* el : mpTracElNew ) { const FloatArray &xS = el->mInteriorSegments[0].giveVertex(1); const double arcPosXS = arcPosFunc.calcArcPos(xS); const FloatArray &xE = el->mInteriorSegments.back().giveVertex(2); const double arcPosXE = arcPosFunc.calcArcPos(xE); while(i < allCoordUnsorted.size()) { FloatArray x = allCoordUnsorted[i]; x.resizeWithValues(2); const double arcPosX = arcPosFunc.calcArcPos(x); if( arcPosX > (arcPosXS+minPointDist) && arcPosX < (arcPosXE-minPointDist) ) { el->mInteriorSegmentsPointsFine.push_back(x); } if( arcPosX > arcPosXE ) { break; } i++; } } // Now we have the necessary points on each traction element. // The next step is to create splitted segments. for( TracSegArray* el : mpTracElNew ) { i = 0; for( Line &line : el->mInteriorSegments ) { FloatArray xS = line.giveVertex(1); xS.resizeWithValues(2); const double arcPosXS = arcPosFunc.calcArcPos(xS); FloatArray xE = line.giveVertex(2); xE.resizeWithValues(2); const double arcPosXE = arcPosFunc.calcArcPos(xE); if( el->mInteriorSegmentsPointsFine.size() == 0 ) { Line newLine(xS, xE); el->mInteriorSegmentsFine.push_back(newLine); } else { while(i < el->mInteriorSegmentsPointsFine.size()) { const FloatArray &x = el->mInteriorSegmentsPointsFine[i]; const double arcPosX = arcPosFunc.calcArcPos(x); if(arcPosX < arcPosXS) { OOFEM_ERROR("Error in PrescribedGradientBCWeak :: createTractionMesh.") } if( arcPosX < arcPosXE ) { // Split from start pos to x Line newLine(xS, x); el->mInteriorSegmentsFine.push_back(newLine); xS = x; } else { // Split from x to end pos Line newLine(xS, xE); el->mInteriorSegmentsFine.push_back(newLine); break; } if( i == (el->mInteriorSegmentsPointsFine.size()-1) ) { // Split from x to end pos Line newLine(xS, xE); el->mInteriorSegmentsFine.push_back(newLine); } i++; } } } } // Create integration rules for( TracSegArray* el : mpTracElNew ) { el->setupIntegrationRuleOnEl(); } // Write discontinuity points to debug vtk std :: vector< FloatArray > discPoints; for( TracSegArray* el : mpTracElNew ) { discPoints.push_back( el->mInteriorSegments[0].giveVertex(1) ); discPoints.push_back( el->mInteriorSegments.back().giveVertex(2) ); } // std :: string fileName("DiscontPoints.vtk"); // XFEMDebugTools :: WritePointsToVTK(fileName, discPoints); }
void MPSDamMaterial :: giveRealStressVector(FloatArray &answer, GaussPoint *gp, const FloatArray &totalStrain, TimeStep *tStep) { if (this->E < 0.) { // initialize dummy elastic modulus E this->E = 1. / MPSMaterial :: computeCreepFunction(28.01, 28., gp, tStep); } MPSDamMaterialStatus *status = static_cast< MPSDamMaterialStatus * >( this->giveStatus(gp) ); MaterialMode mode = gp->giveMaterialMode(); FloatArray principalStress; FloatMatrix principalDir; StressVector tempEffectiveStress(mode); StressVector tempNominalStress(mode); double f, sigma1, kappa, tempKappa = 0.0, omega = 0.0; // effective stress computed by the viscoelastic MPS material MPSMaterial :: giveRealStressVector(tempEffectiveStress, gp, totalStrain, tStep); if ( !this->isActivated(tStep) ) { FloatArray help; help.resize( StructuralMaterial :: giveSizeOfVoigtSymVector( gp->giveMaterialMode() ) ); help.zero(); status->letTempStrainVectorBe(help); status->letTempStressVectorBe(help); status->letTempViscoelasticStressVectorBe(help); #ifdef supplementary_info status->setResidualTensileStrength(0.); status->setCrackWidth(0.); status->setTempKappa(0.); status->setTempDamage(0.); #endif return; } tempEffectiveStress.computePrincipalValDir(principalStress, principalDir); sigma1 = 0.; if ( principalStress.at(1) > 0.0 ) { sigma1 = principalStress.at(1); } kappa = sigma1 / this->E; f = kappa - status->giveKappa(); if ( f <= 0.0 ) { // damage does not grow tempKappa = status->giveKappa(); omega = status->giveDamage(); #ifdef supplementary_info double residualStrength = 0.; double e0; if ( ( this->timeDepFracturing ) && ( this->givee0(gp) == 0. ) ) { this->initDamagedFib(gp, tStep); } e0 = this->givee0(gp); if ( omega == 0. ) { residualStrength = E * e0; // undamaged material } else { double gf, ef, wf = 0., Le; gf = this->givegf(gp); Le = status->giveCharLength(); if ( softType == ST_Exponential_Cohesive_Crack ) { // exponential softening wf = gf / this->E / e0; // wf is the crack opening } else if ( softType == ST_Linear_Cohesive_Crack ) { // linear softening law wf = 2. * gf / this->E / e0; // wf is the crack opening } else { OOFEM_ERROR("Gf unsupported for softening type softType = %d", softType); } ef = wf / Le; //ef is the fracturing strain if ( this->softType == ST_Linear_Cohesive_Crack ) { residualStrength = E * e0 * ( ef - tempKappa ) / ( ef - e0 ); } else if ( this->softType == ST_Exponential_Cohesive_Crack ) { residualStrength = E * e0 * exp(-1. * ( tempKappa - e0 ) / ef); } else { OOFEM_ERROR("Unknown softening type for cohesive crack model."); } } if ( status ) { status->setResidualTensileStrength(residualStrength); } #endif } else { // damage grows tempKappa = kappa; FloatArray crackPlaneNormal(3); for ( int i = 1; i <= principalStress.giveSize(); i++ ) { crackPlaneNormal.at(i) = principalDir.at(i, 1); } this->initDamaged(tempKappa, crackPlaneNormal, gp, tStep); this->computeDamage(omega, tempKappa, gp); } answer.zero(); if ( omega > 0. ) { tempNominalStress = tempEffectiveStress; if ( this->isotropic ) { // convert effective stress to nominal stress tempNominalStress.times(1. - omega); answer.add(tempNominalStress); } else { // stress transformation matrix FloatMatrix Tstress; // compute principal nominal stresses by multiplying effective stresses by damage for ( int i = 1; i <= principalStress.giveSize(); i++ ) { if ( principalStress.at(i) > 0. ) { // convert principal effective stress to nominal stress principalStress.at(i) *= ( 1. - omega ); } } if ( mode == _PlaneStress ) { principalStress.resizeWithValues(3); givePlaneStressVectorTranformationMtrx(Tstress, principalDir, true); } else { principalStress.resizeWithValues(6); giveStressVectorTranformationMtrx(Tstress, principalDir, true); } principalStress.rotatedWith(Tstress, 'n'); if ( mode == _PlaneStress ) { // plane stress answer.add(principalStress); } else if ( this->giveSizeOfVoigtSymVector(mode) != this->giveSizeOfVoigtSymVector(_3dMat) ) { // mode = _PlaneStrain or axial symmetry StressVector redFormStress(mode); redFormStress.convertFromFullForm(principalStress, mode); answer.add(redFormStress); } else { // 3D answer.add(principalStress); } } } else { answer.add(tempEffectiveStress); } #ifdef supplementary_info if ( ( omega == 0. ) || ( sigma1 <= 0 ) ) { status->setCrackWidth(0.); } else { FloatArray principalStrains; this->computePrincipalValues(principalStrains, totalStrain, principal_strain); double crackWidth; //case when the strain localizes into narrow band and after a while the stresses relax double strainWithoutTemperShrink = principalStrains.at(1); strainWithoutTemperShrink -= status->giveTempThermalStrain(); strainWithoutTemperShrink -= status->giveTempDryingShrinkageStrain(); strainWithoutTemperShrink -= status->giveTempAutogenousShrinkageStrain(); crackWidth = status->giveCharLength() * omega * strainWithoutTemperShrink; status->setCrackWidth(crackWidth); } #endif // update gp status->letTempStrainVectorBe(totalStrain); status->letTempStressVectorBe(answer); status->letTempViscoelasticStressVectorBe(tempEffectiveStress); status->setTempKappa(tempKappa); status->setTempDamage(omega); }
void PolygonLine :: computeTangentialSignDist(double &oDist, const FloatArray &iPoint, double &oMinArcDist) const { FloatArray point = iPoint; point.resizeWithValues(2); const int numSeg = this->giveNrVertices() - 1; double xi = 0.0, xiUnbounded = 0.0; if(numSeg == 0) { FloatArray crackP1 = giveVertex ( 1 ); oDist = crackP1.distance(iPoint); oMinArcDist = 0.0; return; } if(numSeg == 1) { FloatArray crackP1 = giveVertex ( 1 ); crackP1.resizeWithValues(2); FloatArray crackP2 = giveVertex ( 2 ); crackP2.resizeWithValues(2); point.distance(crackP1, crackP2, xi, xiUnbounded); if( xiUnbounded < 0.0 ) { oDist = xiUnbounded*crackP1.distance(crackP2); oMinArcDist = 0.0; return; } if( xiUnbounded > 1.0 ) { oDist = -(xiUnbounded-1.0)*crackP1.distance(crackP2); oMinArcDist = 1.0; return; } const double L = computeLength(); double distToStart = xi*L; oDist = std::min(distToStart, (L - distToStart) ); oMinArcDist = distToStart/L; return; } bool isBeforeStart = false, isAfterEnd = false; double distBeforeStart = 0.0, distAfterEnd = 0.0; /////////////////////////////////////////////////////////////////// // Check first segment FloatArray crackP1_start = giveVertex ( 1 ); crackP1_start.resizeWithValues(2); FloatArray crackP2_start = giveVertex ( 2 ); crackP2_start.resizeWithValues(2); const double distSeg_start = point.distance(crackP1_start, crackP2_start, xi, xiUnbounded); if( xiUnbounded < 0.0 ) { isBeforeStart = true; distBeforeStart = xiUnbounded*crackP1_start.distance(crackP2_start); } double arcPosPassed = crackP1_start.distance(crackP2_start); double distToStart = xi*crackP1_start.distance(crackP2_start); double minGeomDist = distSeg_start; /////////////////////////////////////////////////////////////////// // Check interior segments for ( int segId = 2; segId <= numSeg-1; segId++ ) { FloatArray crackP1 = giveVertex ( segId ); crackP1.resizeWithValues(2); FloatArray crackP2 = giveVertex ( segId+1 ); crackP2.resizeWithValues(2); const double distSeg = point.distance(crackP1, crackP2, xi, xiUnbounded); if(distSeg < minGeomDist) { isBeforeStart = false; minGeomDist = distSeg; distToStart = arcPosPassed + xi*crackP1.distance(crackP2); } arcPosPassed += crackP1.distance(crackP2); } /////////////////////////////////////////////////////////////////// // Check last segment FloatArray crackP1_end = giveVertex ( numSeg ); crackP1_end.resizeWithValues(2); FloatArray crackP2_end = giveVertex ( numSeg+1 ); crackP2_end.resizeWithValues(2); const double distSeg_end = point.distance(crackP1_end, crackP2_end, xi, xiUnbounded); if(numSeg > 1) { if( xiUnbounded > 1.0 ) { arcPosPassed += xiUnbounded*crackP1_end.distance(crackP2_end); } else { arcPosPassed += xi*crackP1_end.distance(crackP2_end); } } if(distSeg_end < minGeomDist) { isBeforeStart = false; if( xiUnbounded > 1.0 ) { isAfterEnd = true; distAfterEnd = -(xiUnbounded-1.0)*crackP1_end.distance(crackP2_end); } distToStart = arcPosPassed; } /////////////////////////////////////////////////////////////////// // Return result if(isBeforeStart) { oDist = distBeforeStart; oMinArcDist = 0.0; return; } if(isAfterEnd) { oDist = distAfterEnd; oMinArcDist = 1.0; return; } const double L = computeLength(); oDist = std::min(distToStart, (L - distToStart) ); oMinArcDist = distToStart/L; }
bool Triangle :: pointIsInTriangle(const FloatArray &iP) const { FloatArray P(iP); const double tol2 = 1.0e-18; // Compute triangle normal FloatArray p1p2; p1p2.beDifferenceOf(mVertices [ 1 ], mVertices [ 0 ]); FloatArray p1p3; p1p3.beDifferenceOf(mVertices [ 2 ], mVertices [ 0 ]); // Edge 1 FloatArray t1; t1.beDifferenceOf(mVertices [ 1 ], mVertices [ 0 ]); if(t1.computeSquaredNorm() < tol2) { // The triangle is degenerated return false; } else { t1.normalize(); } FloatArray a1; // Edge 2 FloatArray t2; t2.beDifferenceOf(mVertices [ 2 ], mVertices [ 1 ]); if(t2.computeSquaredNorm() < tol2) { // The triangle is degenerated return false; } else { t2.normalize(); } FloatArray a2; // Edge 3 FloatArray t3; t3.beDifferenceOf(mVertices [ 0 ], mVertices [ 2 ]); if(t3.computeSquaredNorm() < tol2) { // The triangle is degenerated return false; } else { t3.normalize(); } FloatArray a3; // Project point onto triangle plane FloatArray pProj = P; if( p1p2.giveSize() == 2 ) { // 2D pProj.resizeWithValues(2); a1 = {-t1[1], t1[0]}; a2 = {-t2[1], t2[0]}; a3 = {-t3[1], t3[0]}; } else { // 3D FloatArray N; N.beVectorProductOf(p1p2, p1p3); if(N.computeSquaredNorm() < tol2) { // The triangle is degenerated return false; } else { N.normalize(); } // Compute normal distance from triangle to point FloatArray p1p; p1p.beDifferenceOf(P, mVertices [ 0 ]); double d = p1p.dotProduct(N); pProj.add(-d, N); a1.beVectorProductOf(N, t1); // if(a1.computeSquaredNorm() < tol2) { // // The triangle is degenerated // return false; // } // else { // a1.normalize(); // } a2.beVectorProductOf(N, t2); // if(a2.computeSquaredNorm() < tol2) { // // The triangle is degenerated // return false; // } // else { // a2.normalize(); // } a3.beVectorProductOf(N, t3); // if(a3.computeSquaredNorm() < tol2) { // // The triangle is degenerated // return false; // } // else { // a3.normalize(); // } } // Check if the point is on the correct side of all edges FloatArray p1pProj; p1pProj.beDifferenceOf(pProj, mVertices [ 0 ]); if ( p1pProj.dotProduct(a1) < 0.0 ) { return false; } FloatArray p2pProj; p2pProj.beDifferenceOf(pProj, mVertices [ 1 ]); if ( p2pProj.dotProduct(a2) < 0.0 ) { return false; } FloatArray p3pProj; p3pProj.beDifferenceOf(pProj, mVertices [ 2 ]); if ( p3pProj.dotProduct(a3) < 0.0 ) { return false; } return true; }