Skip to content

Commit 28353ae

Browse files
committed
Fixed calculation of eigenvectors of moment of inertia. Provided ability to calculate area-based MOI. Added tool to align eigenvectors of MOI with coordinate system axes.
1 parent e0fe68c commit 28353ae

File tree

7 files changed

+77
-88
lines changed

7 files changed

+77
-88
lines changed

FEBioStudio/MeasureCOMTool.cpp

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,3 @@ bool CMeasureCOMTool::OnApply()
4444
m_com = CalculateCOM(*mesh);
4545
return true;
4646
}
47-
48-
// This algorithm calculates the center of mass approximately,
49-
// by weighing the element centers by the element volumes.
50-
// TODO: I should look into bringing the numerical integration tools
51-
// from FEBio to FBS so this can be calculated accurately.
52-
vec3d CalculateCOM(FSMesh& mesh)
53-
{
54-
vec3d rc(0, 0, 0);
55-
double Vtotal = 0.0;
56-
57-
int selectedElements = mesh.CountSelectedElements();
58-
59-
// loop over all elements
60-
int NE = mesh.Elements();
61-
for (int iel = 0; iel < NE; ++iel)
62-
{
63-
FSElement& el = mesh.Element(iel);
64-
if ((selectedElements == 0) || el.IsSelected())
65-
{
66-
// Volume is calculated in global frame
67-
double V0 = mesh.ElementVolume(el);
68-
69-
// center of mass is calculated in local frame
70-
vec3d c_local = mesh.ElementCenter(el);
71-
vec3d c_global = mesh.LocalToGlobal(c_local);
72-
73-
rc += c_global * V0;
74-
Vtotal += V0;
75-
}
76-
}
77-
if (Vtotal != 0.0)
78-
rc /= Vtotal;
79-
80-
const double eps = 1e-12;
81-
double L = rc.Length();
82-
if (L > eps)
83-
{
84-
if (fabs(rc.x) < eps) rc.x = 0;
85-
if (fabs(rc.y) < eps) rc.y = 0;
86-
if (fabs(rc.z) < eps) rc.z = 0;
87-
}
88-
89-
return rc;
90-
}

FEBioStudio/MeasureCOMTool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2525
SOFTWARE.*/
2626

2727
#include "Tool.h"
28+
#include "MeasureTools.h"
2829

2930
class CMainWindow;
3031

FEBioStudio/MeasureMOITool.cpp

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ mat3d CalculateMOI(FSMesh& mesh);
3232
// constructor
3333
CMeasureMOITool::CMeasureMOITool(CMainWindow* wnd) : CBasicTool(wnd, "Moment of inertia", CBasicTool::HAS_APPLY_BUTTON)
3434
{
35+
m_area = false;
3536
m_moi.zero();
3637
m_evec.zero();
3738
m_eval = vec3d(0,0,0);
3839
addMat3Property(&m_moi, "moment of inertia:")->setFlags(CProperty::Visible);
3940
addMat3Property(&m_evec, "eigenvectors of MOI:")->setFlags(CProperty::Visible);
4041
addVec3Property(&m_eval, "eigenvalues of MOI:")->setFlags(CProperty::Visible);
42+
addBoolProperty(&m_area, "area");
4143

4244
SetInfo("Calculates the moment of inertia of a meshed object or element selection. The MOI is calculated with respect to the center of mass of the selection.");
4345
}
@@ -47,54 +49,16 @@ bool CMeasureMOITool::OnApply()
4749
{
4850
FSMesh* mesh = GetActiveMesh();
4951
if (mesh == nullptr) return false;
50-
m_moi = CalculateMOI(*mesh);
52+
m_moi = (m_area) ? CalculateAreaMOI(*mesh) : CalculateMOI(*mesh);
5153
double eval[3];
5254
vec3d evec[3];
5355
mat3ds mois = m_moi.sym();
5456
mois.eigen2(eval,evec);
55-
m_evec = mat3d(evec[0].x, evec[1].x, evec[2].x,
56-
evec[0].y, evec[1].y, evec[2].y,
57-
evec[0].z, evec[1].z, evec[2].z);
57+
for (int i=0; i<3; ++i) evec[i].Normalize();
58+
m_evec = mat3d(evec[0].x, evec[0].y, evec[0].z,
59+
evec[1].x, evec[1].y, evec[1].z,
60+
evec[2].x, evec[2].y, evec[2].z);
5861
m_eval = vec3d(eval[0],eval[1],eval[2]);
62+
m_evec = CleanUp(m_evec, 1e-12);
5963
return true;
6064
}
61-
62-
// This calculates the moment of inertia, but only approximately!
63-
// Requires a fine mesh for accurate results.
64-
mat3d CalculateMOI(FSMesh& mesh)
65-
{
66-
mat3dd I(1); // identity tensor
67-
68-
mat3d moi; moi.zero();
69-
70-
// calculate the COM
71-
vec3d com = CalculateCOM(mesh);
72-
73-
// loop over all elements
74-
int NE = mesh.Elements();
75-
for (int iel = 0; iel < NE; ++iel)
76-
{
77-
FSElement& el = mesh.Element(iel);
78-
79-
// ElementCenter is calculated in local frame
80-
vec3d c_local = mesh.ElementCenter(el);
81-
vec3d c_global = mesh.LocalToGlobal(c_local);
82-
83-
// Element volume is calculated in global frame
84-
double V0 = mesh.ElementVolume(el);
85-
86-
vec3d r = c_global - com;
87-
mat3d Iij = (r * r) * I - (r & r);
88-
moi += Iij * V0;
89-
}
90-
91-
const double eps = 1e-12;
92-
double L = moi.norm();
93-
if (L > eps)
94-
{
95-
for (int i = 0; i < 3; ++i)
96-
for (int j = 0; j < 3; ++j)
97-
if (fabs(moi[i][j]) < eps) moi[i][j] = 0.0;
98-
}
99-
return moi;
100-
}

FEBioStudio/MeasureMOITool.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2525
SOFTWARE.*/
2626

2727
#include "Tool.h"
28+
#include "MeasureTools.h"
2829

2930
class CMainWindow;
3031

@@ -42,4 +43,5 @@ class CMeasureMOITool : public CBasicTool
4243
mat3d m_moi; // center of mass
4344
mat3d m_evec; // eigenvectors of MOI
4445
vec3d m_eval; // eigenvalues of MOI
46+
bool m_area; // boolean flag to report area MOI
4547
};

FEBioStudio/MeshPanel.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ void ModifierThread::stop()
274274
REGISTER_CLASS(FEAddNode , CLASS_FEMODIFIER, "Add Node" , EDIT_MESH);
275275
REGISTER_CLASS(FEAddTriangle , CLASS_FEMODIFIER, "Add Triangle" , EDIT_MESH);
276276
REGISTER_CLASS(FEAlignNodes , CLASS_FEMODIFIER, "Align" , EDIT_NODE);
277+
REGISTER_CLASS(FEAlignMeshMOI , CLASS_FEMODIFIER, "Align MOI to CS", EDIT_MESH);
277278
REGISTER_CLASS(FEAutoPartition , CLASS_FEMODIFIER, "Auto Partition" , EDIT_MESH);
278279
REGISTER_CLASS(FEBoundaryLayerMesher , CLASS_FEMODIFIER, "Boundary Layer" , EDIT_FACE | EDIT_SAFE);
279280
REGISTER_CLASS(FEConvertMesh , CLASS_FEMODIFIER, "Convert Mesh" , EDIT_MESH | EDIT_SAFE);

MeshTools/FEModifier.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ SOFTWARE.*/
3939
#include <MeshLib/Intersect.h>
4040
#include <GeomLib/GGroup.h>
4141
#include <MeshTools/LaplaceSolver.h>
42+
#include <FEBioStudio/MeasureTools.h>
43+
#include <numeric>
4244

4345
FEModifier::FEModifier(const char* sz) { SetName(sz); }
4446
FEModifier::~FEModifier() {}
@@ -2048,3 +2050,56 @@ FSMesh* FEDetachElements::Apply(FSMesh* pm)
20482050

20492051
return newMesh;
20502052
}
2053+
2054+
//=============================================================================
2055+
// FEAlignMesh
2056+
//-----------------------------------------------------------------------------
2057+
2058+
FEAlignMeshMOI::FEAlignMeshMOI() : FEModifier("Align Mesh MOI")
2059+
{
2060+
AddBoolParam(true, "Use area MOI");
2061+
}
2062+
2063+
FSMesh* FEAlignMeshMOI::Apply(FSMesh* pm)
2064+
{
2065+
GObject* po = GObject::GetActiveObject();
2066+
if (po == nullptr) return nullptr;
2067+
2068+
bool use_area = GetBoolValue(0);
2069+
mat3d moi = (use_area) ? CalculateAreaMOI(*pm) : CalculateMOI(*pm);
2070+
double eval[3];
2071+
vec3d evec[3];
2072+
mat3ds mois = moi.sym();
2073+
mois.eigen2(eval,evec);
2074+
// sort eigenvalues in ascending order
2075+
std::vector<int> indices(3);
2076+
std::iota(indices.begin(), indices.end(),0);
2077+
std::vector<double> ev(eval,eval+3);
2078+
std::sort(indices.begin(), indices.end(), [&](int A, int B)->bool {return ev[A] < ev[B];});
2079+
// check handedness of eigenvectors and swap if needed
2080+
if (((evec[indices[0]] ^ evec[indices[1]])*evec[indices[2]]) < 0)
2081+
evec[indices[2]] = -evec[indices[2]];
2082+
2083+
2084+
mat3d fevec = mat3d(evec[indices[0]].x, evec[indices[0]].y, evec[indices[0]].z,
2085+
evec[indices[1]].x, evec[indices[1]].y, evec[indices[1]].z,
2086+
evec[indices[2]].x, evec[indices[2]].y, evec[indices[2]].z);
2087+
vec3d feval = vec3d(eval[indices[0]],eval[indices[1]],eval[indices[2]]);
2088+
2089+
// roundoff small numbers
2090+
const double eps = 1e-12;
2091+
fevec = CleanUp(fevec, eps);
2092+
2093+
// create a quaternion from the matrix of eigenvectors
2094+
quatd q(fevec);
2095+
q.MakeUnit();
2096+
2097+
po->GetTransform().Rotate(q,vec3d(0,0,0));
2098+
vec3d com = use_area ? CalculateAreaCOM(*pm) : CalculateCOM(*pm);
2099+
po->GetTransform().Translate(-com);
2100+
2101+
FSMesh* newMesh = new FSMesh(*pm);
2102+
FSMeshBuilder meshBuilder(*newMesh);
2103+
2104+
return newMesh;
2105+
}

MeshTools/FEModifier.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,19 @@ class FEInflateMesh: public FEModifier
551551
FSMesh* Apply(FSMesh* pm);
552552
};
553553

554+
//-----------------------------------------------------------------------------
554555
class FEDeleteElements : public FEModifier
555556
{
556557
public:
557558
FEDeleteElements();
558559
FSMesh* Apply(FSMesh* pm);
559560
};
561+
562+
//-----------------------------------------------------------------------------
563+
class FEAlignMeshMOI: public FEModifier
564+
{
565+
public:
566+
FEAlignMeshMOI();
567+
FSMesh* Apply(FSMesh* pm);
568+
};
569+

0 commit comments

Comments
 (0)