Files
IfcOpenShell/src/ifcblenderexport/blenderbim/bim/qto.py
T

156 lines
5.9 KiB
Python

from mathutils import Vector
class QtoCalculator:
def guess_quantity(self, prop_name, alternative_prop_names, obj):
prop_name = prop_name.lower()
alternative_prop_names = [p.lower() for p in alternative_prop_names]
if "length" in prop_name and "width" not in alternative_prop_names and "height" not in alternative_prop_names:
return self.get_linear_length(obj)
elif "length" in prop_name:
return self.get_length(obj)
elif "width" in prop_name and "length" not in alternative_prop_names:
return self.get_length(obj)
elif "width" in prop_name:
return self.get_width(obj)
elif "height" in prop_name or "depth" in prop_name:
return self.get_height(obj)
elif "perimeter" in prop_name:
return self.get_perimeter(obj)
elif "area" in prop_name and ("footprint" in prop_name or "section" in prop_name or "floor" in prop_name):
return self.get_footprint_area(obj)
elif "area" in prop_name and "side" in prop_name:
return self.get_side_area(obj)
elif "area" in prop_name:
return self.get_area(obj)
elif "volume" in prop_name:
return self.get_volume(obj)
def get_units(self, o, vg_index):
return len([v for v in o.data.vertices if vg_index in [g.group for g in v.groups]])
def get_linear_length(self, o):
x = (Vector(o.bound_box[4]) - Vector(o.bound_box[0])).length
y = (Vector(o.bound_box[3]) - Vector(o.bound_box[0])).length
z = (Vector(o.bound_box[1]) - Vector(o.bound_box[0])).length
return max(x, y, z)
def get_length(self, o, vg_index=None):
if vg_index is None:
x = (Vector(o.bound_box[4]) - Vector(o.bound_box[0])).length
y = (Vector(o.bound_box[3]) - Vector(o.bound_box[0])).length
return max(x, y)
length = 0
edges = [
e
for e in o.data.edges
if (
vg_index in [g.group for g in o.data.vertices[e.vertices[0]].groups]
and vg_index in [g.group for g in o.data.vertices[e.vertices[1]].groups]
)
]
for e in edges:
length += self.get_edge_distance(o, e)
return length
def get_width(self, o):
x = (Vector(o.bound_box[4]) - Vector(o.bound_box[0])).length
y = (Vector(o.bound_box[3]) - Vector(o.bound_box[0])).length
return min(x, y)
def get_height(self, o):
return (Vector(o.bound_box[1]) - Vector(o.bound_box[0])).length
def get_perimeter(self, o):
parsed_edges = []
shared_edges = []
perimeter = 0
for polygon in self.get_lowest_polygons(o):
for edge_key in polygon.edge_keys:
if edge_key in parsed_edges:
shared_edges.append(edge_key)
else:
parsed_edges.append(edge_key)
perimeter += self.get_edge_key_distance(o, edge_key)
for edge_key in shared_edges:
perimeter -= self.get_edge_key_distance(o, edge_key)
return perimeter
def get_lowest_polygons(self, o):
lowest_polygons = []
lowest_z = None
for polygon in o.data.polygons:
z = round(polygon.center[2], 3)
if lowest_z is None:
lowest_z = z
if z > lowest_z:
continue
elif z == lowest_z:
lowest_polygons.append(polygon)
elif z < lowest_z:
lowest_polygons = [polygon]
lowest_z = z
return lowest_polygons
def get_edge_key_distance(self, obj, edge_key):
return (obj.data.vertices[edge_key[1]].co - obj.data.vertices[edge_key[0]].co).length
def get_edge_distance(self, obj, edge):
return (obj.data.vertices[edge.vertices[1]].co - obj.data.vertices[edge.vertices[0]].co).length
def get_footprint_area(self, o):
area = 0
for polygon in self.get_lowest_polygons(o):
area += polygon.area
return area
def get_side_area(self, o):
# There are a few dumb options for this, but this seems the dumbest
# until I get more practical experience on what works best.
x = (Vector(o.bound_box[4]) - Vector(o.bound_box[0])).length
y = (Vector(o.bound_box[3]) - Vector(o.bound_box[0])).length
z = (Vector(o.bound_box[1]) - Vector(o.bound_box[0])).length
return max(x * z, y * z)
def get_area(self, o, vg_index=None):
if vg_index is None:
area = 0
for polygon in o.data.polygons:
area += polygon.area
return area
area = 0
vertices_in_vg = [v.index for v in o.data.vertices if vg_index in [g.group for g in v.groups]]
for polygon in o.data.polygons:
if self.is_polygon_in_vg(polygon, vertices_in_vg):
area += polygon.area
return area
def is_polygon_in_vg(self, polygon, vertices_in_vg):
for v in polygon.vertices:
if v not in vertices_in_vg:
return False
return True
def get_volume(self, o, vg_index=None):
volume = 0
ob_mat = o.matrix_world
me = o.data
me.calc_loop_triangles()
for tf in me.loop_triangles:
tfv = tf.vertices
if len(tf.vertices) == 3:
tf_tris = ((me.vertices[tfv[0]], me.vertices[tfv[1]], me.vertices[tfv[2]]),)
else:
tf_tris = (
(me.vertices[tfv[0]], me.vertices[tfv[1]], me.vertices[tfv[2]]),
(me.vertices[tfv[2]], me.vertices[tfv[3]], me.vertices[tfv[0]],),
)
for tf_iter in tf_tris:
v1 = ob_mat @ tf_iter[0].co
v2 = ob_mat @ tf_iter[1].co
v3 = ob_mat @ tf_iter[2].co
volume += v1.dot(v2.cross(v3)) / 6.0
return volume