mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
156 lines
5.9 KiB
Python
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
|