1 """Calculate the mass, center of gravity, and inertia matrix for common
2 shapes."""
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 import math
42 from pyffi.utils.mathutils import *
43
44
45
47 """Return mass and inertia matrix for a sphere of given radius and
48 density.
49
50 >>> mass, inertia_matrix = getMassInertiaSphere(2.0, 3.0)
51 >>> mass # doctest: +ELLIPSIS
52 100.53096...
53 >>> inertia_matrix[0][0] # doctest: +ELLIPSIS
54 160.84954..."""
55 if solid:
56 mass = density * (4 * math.pi * (radius ** 3)) / 3
57 inertia = (2 * mass * (radius ** 2)) / 5
58 else:
59 mass = density * 4 * math.pi * (radius ** 2)
60 inertia = (2 * mass * (radius ** 2)) / 3
61
62 return mass, tuple( tuple( (inertia if i == j else 0)
63 for i in xrange(3) )
64 for j in xrange(3) )
65
67 """Return mass and inertia matrix for a box of given size and
68 density.
69
70 >>> mass, inertia = getMassInertiaBox((1.0, 2.0, 3.0), 4.0)
71 >>> mass
72 24.0
73 >>> inertia
74 ((26.0, 0, 0), (0, 20.0, 0), (0, 0, 10.0))
75 """
76 assert(len(size) == 3)
77 if solid:
78 mass = density * size[0] * size[1] * size[2]
79 tmp = tuple(mass * (length ** 2) / 12.0 for length in size)
80 else:
81 mass = density * sum( x * x for x in size)
82 tmp = tuple(mass * (length ** 2) / 6.0 for length in size)
83 return mass, ( ( tmp[1] + tmp[2], 0, 0 ),
84 ( 0, tmp[2] + tmp[0], 0 ),
85 ( 0, 0, tmp[0] + tmp[1] ) )
86
88
89 if solid:
90 mass = density * (length * math.pi * (radius ** 2)
91 + (4 * math.pi * (radius ** 3)) / 3)
92
93
94
95 inertia_xx = mass * (3 * (radius ** 2) + (length ** 2)) / 12.0
96 inertia_yy = inertia_xx
97 inertia_zz = 0.5 * mass * (radius ** 2)
98 else:
99 mass = density * (length * 2 * math.pi * radius
100 + 2 * math.pi * (radius ** 2))
101 inertia_xx = mass * (6 * (radius ** 2) + (length ** 2)) / 12.0
102 inertia_yy = inertia_xx
103 inertia_zz = mass * (radius ** 2)
104
105 return mass, ( ( inertia_xx, 0, 0 ),
106 ( 0, inertia_yy, 0 ),
107 ( 0, 0, inertia_zz ) )
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
125 """Return mass, center of gravity, and inertia matrix for a polyhedron.
126
127 >>> from pyffi.utils.quickhull import qhull3d
128 >>> box = [(0,0,0),(1,0,0),(0,2,0),(0,0,3),(1,2,0),(0,2,3),(1,0,3),(1,2,3)]
129 >>> vertices, triangles = qhull3d(box)
130 >>> mass, center, inertia = get_mass_center_inertia_polyhedron(
131 ... vertices, triangles, density = 4)
132 >>> mass
133 24.0
134 >>> center
135 (0.5, 1.0, 1.5)
136 >>> inertia
137 ((26.0, 0.0, 0.0), (0.0, 20.0, 0.0), (0.0, 0.0, 10.0))
138 >>> poly = [(3,0,0),(0,3,0),(-3,0,0),(0,-3,0),(0,0,3),(0,0,-3)] # very rough approximation of a sphere of radius 2
139 >>> vertices, triangles = qhull3d(poly)
140 >>> mass, center, inertia = get_mass_center_inertia_polyhedron(
141 ... vertices, triangles, density = 3)
142 >>> mass
143 108.0
144 >>> center
145 (0.0, 0.0, 0.0)
146 >>> abs(inertia[0][0] - 194.4) < 0.0001
147 True
148 >>> abs(inertia[1][1] - 194.4) < 0.0001
149 True
150 >>> abs(inertia[2][2] - 194.4) < 0.0001
151 True
152 >>> abs(inertia[0][1]) < 0.0001
153 True
154 >>> abs(inertia[0][2]) < 0.0001
155 True
156 >>> abs(inertia[1][2]) < 0.0001
157 True
158 >>> sphere = []
159 >>> N = 10
160 >>> for j in range(-N+1, N):
161 ... theta = j * 0.5 * math.pi / N
162 ... st, ct = math.sin(theta), math.cos(theta)
163 ... M = max(3, int(ct * 2 * N + 0.5))
164 ... for i in range(0, M):
165 ... phi = i * 2 * math.pi / M
166 ... s, c = math.sin(phi), math.cos(phi)
167 ... sphere.append((2*s*ct, 2*c*ct, 2*st)) # construct sphere of radius 2
168 >>> sphere.append((0,0,2))
169 >>> sphere.append((0,0,-2))
170 >>> vertices, triangles = qhull3d(sphere)
171 >>> mass, center, inertia = get_mass_center_inertia_polyhedron(
172 ... vertices, triangles, density = 3, solid = True)
173 >>> abs(mass - 100.53) < 10 # 3*(4/3)*pi*2^3 = 100.53
174 True
175 >>> sum(abs(x) for x in center) < 0.01 # is center at origin?
176 True
177 >>> abs(inertia[0][0] - 160.84) < 10
178 True
179 >>> mass, center, inertia = get_mass_center_inertia_polyhedron(
180 ... vertices, triangles, density = 3, solid = False)
181 >>> abs(mass - 150.79) < 10 # 3*4*pi*2^2 = 150.79
182 True
183 >>> abs(inertia[0][0] - mass*0.666*4) < 20 # m*(2/3)*2^2
184 True
185 """
186
187
188
189
190
191 covariance_canonical = ( (2, 1, 1),
192 (1, 2, 1),
193 (1, 1, 2) )
194 covariance_correction = 1.0/120
195
196 covariances = []
197 masses = []
198 centers = []
199
200
201
202
203
204 for triangle in triangles:
205
206 vert0, vert1, vert2 = operator.itemgetter(*triangle)(vertices)
207
208
209
210 transform_transposed = ( vert0, vert1, vert2 )
211 transform = matTransposed(transform_transposed)
212
213
214 if solid:
215
216
217 determinant = matDeterminant(transform)
218
219 covariances.append(
220 matscalarMul(
221 matMul(matMul(transform,
222 covariance_canonical),
223 transform_transposed),
224 determinant))
225
226 masses.append(determinant / 6.0)
227
228 centers.append(tuple( 0.25 * sum(vert[i]
229 for vert in (vert0, vert1, vert2))
230 for i in xrange(3) ))
231 else:
232
233 centers.append(tuple( sum(vert[i]
234 for vert in (vert0, vert1, vert2)) / 3.0
235 for i in xrange(3) ))
236
237
238
239 masses.append(
240 vecNorm(vecCrossProduct(
241 vecSub(vert1, vert0), vecSub(vert2, vert0))) / 2.0)
242
243
244
245 covariances.append(
246 tuple(tuple( masses[-1]*x*y for x in centers[-1] )
247 for y in centers[-1]))
248
249
250 total_mass = sum(masses)
251 if total_mass == 0:
252
253
254 print("WARNING: mass is nearly zero (%f)" % total_mass)
255 return 0, (0,0,0), ((0,0,0),(0,0,0),(0,0,0))
256
257 total_center = (0, 0, 0)
258 for center, mass in izip(centers, masses):
259 total_center = vecAdd(total_center,
260 vecscalarMul(center, mass / total_mass))
261
262 total_covariance = ((0,0,0),(0,0,0),(0,0,0))
263 for covariance in covariances:
264 total_covariance = matAdd(total_covariance, covariance)
265 if solid:
266 total_covariance = matscalarMul(total_covariance, covariance_correction)
267
268
269
270
271 translate_correction = matscalarMul(tuple(tuple(x * y
272 for x in total_center)
273 for y in total_center),
274 total_mass)
275 total_covariance = matSub(total_covariance, translate_correction)
276
277
278 trace = sum(total_covariance[i][i] for i in xrange(3))
279 trace_matrix = tuple(tuple((trace if i == j else 0)
280 for i in xrange(3))
281 for j in xrange(3))
282 total_inertia = matSub(trace_matrix, total_covariance)
283
284
285 total_inertia = matscalarMul(total_inertia, density)
286 total_mass *= density
287
288
289 if total_mass < 0:
290 total_mass = -total_mass
291 total_inertia = tuple(tuple(-x for x in row)
292 for row in total_inertia)
293
294 return total_mass, total_center, total_inertia
295
296 if __name__ == "__main__":
297 import doctest
298 doctest.testmod()
299