Provide manual length, area, volume calc operators in qto UI. See #898.

This commit is contained in:
Dion Moult
2020-07-16 17:12:11 +10:00
parent c885652338
commit 2e1a32d540
5 changed files with 218 additions and 56 deletions
@@ -160,6 +160,13 @@ if bpy is not None:
operator.PropagateTextData,
operator.PushRepresentation,
operator.ConvertLocalToGlobal,
operator.GetObjectLength,
operator.GetObjectWidth,
operator.GetObjectHeight,
operator.GetObjectFootprintArea,
operator.GetObjectSideArea,
operator.GetObjectArea,
operator.GetObjectVolume,
prop.StrProperty,
prop.Variable,
prop.Role,
@@ -19,59 +19,6 @@ class ArrayModifier:
offset: Vector
class QtoCalculator():
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_length(self, o, vg_index):
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_edge_distance(self, obj, edge):
return (obj.data.vertices[edge.vertices[1]].co - obj.data.vertices[edge.vertices[0]].co).length
def get_area(self, o, vg_index):
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):
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
class IfcParser():
def __init__(self, ifc_export_settings):
self.data_dir = ifc_export_settings.data_dir
@@ -8,6 +8,7 @@ import ifcopenshell
import tempfile
from . import export_ifc
from . import import_ifc
from . import qto
from . import cut_ifc
from . import svgwriter
from . import sheeter
@@ -63,7 +64,7 @@ class ExportIFC(bpy.types.Operator):
output_file = bpy.path.ensure_ext(self.filepath, '.ifc')
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
ifc_parser = export_ifc.IfcParser(ifc_export_settings)
qto_calculator = export_ifc.QtoCalculator()
qto_calculator = qto.QtoCalculator()
ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser, qto_calculator)
ifc_export_settings.logger.info('Starting export')
ifc_exporter.export(context.selected_objects)
@@ -3026,7 +3027,7 @@ class PushRepresentation(bpy.types.Operator):
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
ifc_parser = export_ifc.IfcParser(ifc_export_settings)
ifc_parser.parse([bpy.context.active_object])
qto_calculator = export_ifc.QtoCalculator()
qto_calculator = qto.QtoCalculator()
self.ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser, qto_calculator)
self.ifc_exporter.file = ifcopenshell.file(schema=self.file.schema)
self.ifc_exporter.create_origin()
@@ -3145,3 +3146,87 @@ class ConvertLocalToGlobal(bpy.types.Operator):
bpy.context.scene.cursor.location = (eastings, northings, height)
return {'FINISHED'}
class GetObjectVolume(bpy.types.Operator):
bl_idname = 'bim.get_object_volume'
bl_label = 'Get Object Volume'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_volume(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectFootprintArea(bpy.types.Operator):
bl_idname = 'bim.get_object_footprint_area'
bl_label = 'Get Object Footprint Area'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_footprint_area(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectSideArea(bpy.types.Operator):
bl_idname = 'bim.get_object_side_area'
bl_label = 'Get Object Side Area'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_side_area(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectArea(bpy.types.Operator):
bl_idname = 'bim.get_object_area'
bl_label = 'Get Object Area'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_area(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectLength(bpy.types.Operator):
bl_idname = 'bim.get_object_length'
bl_label = 'Get Object Length'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_length(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectWidth(bpy.types.Operator):
bl_idname = 'bim.get_object_width'
bl_label = 'Get Object Width'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_width(bpy.context.active_object), 3))
return {'FINISHED'}
class GetObjectHeight(bpy.types.Operator):
bl_idname = 'bim.get_object_height'
bl_label = 'Get Object Height'
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
qto_calculator = qto.QtoCalculator()
bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index].properties[self.prop_index].string_value = str(round(qto_calculator.get_height(bpy.context.active_object), 3))
return {'FINISHED'}
@@ -0,0 +1,88 @@
from mathutils import Vector
class QtoCalculator():
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_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 x if x > y else 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 x if x < y else y
def get_height(self, o):
return (Vector(o.bound_box[1]) - Vector(o.bound_box[0])).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 o.data.polygons:
if round(polygon.center[2], 3) == 0.0 \
and round(o.data.vertices[polygon.vertices[0]].co[2], 3) == 0.0:
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
a1 = x * z
a2 = y * z
return a1 if a1 > a2 else a2
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
+36 -1
View File
@@ -99,10 +99,45 @@ class BIM_PT_object(Panel):
row = layout.row(align=True)
row.prop(qto, 'name', text='')
row.operator('bim.remove_qto', icon='X', text='').index = index
for prop in qto.properties:
for index2, prop in enumerate(qto.properties):
row = layout.row(align=True)
row.prop(prop, 'name', text='')
row.prop(prop, 'string_value', text='')
if 'length' in prop.name.lower():
op = row.operator('bim.get_object_length', icon='IPO_EASE_IN_OUT', text='')
op.qto_index = index
op.prop_index = index2
elif 'width' in prop.name.lower() \
and 'length' not in [p.name for p in qto.properties]:
op = row.operator('bim.get_object_length', icon='IPO_EASE_IN_OUT', text='')
op.qto_index = index
op.prop_index = index2
elif 'width' in prop.name.lower():
op = row.operator('bim.get_object_width', icon='IPO_EASE_IN_OUT', text='')
op.qto_index = index
op.prop_index = index2
elif 'height' in prop.name.lower():
op = row.operator('bim.get_object_height', icon='IPO_EASE_IN_OUT', text='')
op.qto_index = index
op.prop_index = index2
elif 'area' in prop.name.lower() \
and 'footprint' in prop.name.lower():
op = row.operator('bim.get_object_footprint_area', icon='MESH_CIRCLE', text='')
op.qto_index = index
op.prop_index = index2
elif 'area' in prop.name.lower() \
and 'side' in prop.name.lower():
op = row.operator('bim.get_object_side_area', icon='MESH_CIRCLE', text='')
op.qto_index = index
op.prop_index = index2
elif 'area' in prop.name.lower():
op = row.operator('bim.get_object_area', icon='MESH_CIRCLE', text='')
op.qto_index = index
op.prop_index = index2
elif 'volume' in prop.name.lower():
op = row.operator('bim.get_object_volume', icon='SPHERE', text='')
op.qto_index = index
op.prop_index = index2
row = layout.row()
row.prop(props, 'material_type')