merge master v6.0

This commit is contained in:
admin
2021-01-11 11:39:36 +08:00
parent 699742880e
commit 555a2ce79e
40 changed files with 1189 additions and 25605 deletions
+55 -51
View File
@@ -5,15 +5,25 @@ bpy = sys.modules.get("bpy")
if bpy is not None:
import bpy
import blenderbim.bim.module.covetool as module_covetool
import blenderbim.bim.module.model as module_model
import blenderbim.bim.module.root as module_root
import blenderbim.bim.module.aggregate as module_aggregate
import blenderbim.bim.module.attribute as module_attribute
import blenderbim.bim.module.bcf as module_bcf
import blenderbim.bim.module.context as module_context
import blenderbim.bim.module.covetool as module_covetool
import blenderbim.bim.module.debug as module_debug
import blenderbim.bim.module.geometry as module_geometry
import blenderbim.bim.module.model as module_model
import blenderbim.bim.module.owner as module_owner
import blenderbim.bim.module.project as module_project
import blenderbim.bim.module.pset as module_pset
import blenderbim.bim.module.spatial as module_spatial
import blenderbim.bim.module.style as module_style
import blenderbim.bim.module.type as module_type
import blenderbim.bim.module.unit as module_unit
from . import ui, prop, operator
classes = [
operator.ReassignClass,
operator.AssignClass,
operator.UnassignClass,
operator.SelectClass,
operator.SelectType,
operator.OpenUri,
@@ -27,7 +37,6 @@ if bpy is not None:
operator.ValidateIfcFile,
operator.ExportIFC,
operator.ImportIFC,
operator.ProfileImportIFC,
operator.ColourByClass,
operator.ColourByAttribute,
operator.ColourByPset,
@@ -45,8 +54,6 @@ if bpy is not None:
operator.SelectSweptSolidInnerCurves,
operator.AssignSweptSolidExtrusion,
operator.SelectSweptSolidExtrusion,
operator.AddPset,
operator.RemovePset,
operator.AddQto,
operator.RemoveQto,
operator.AddMaterialPset,
@@ -65,18 +72,6 @@ if bpy is not None:
operator.AssignConstraint,
operator.UnassignConstraint,
operator.RemoveObjectConstraint,
operator.AddPerson,
operator.RemovePerson,
operator.AddPersonRole,
operator.RemovePersonRole,
operator.AddPersonAddress,
operator.RemovePersonAddress,
operator.AddOrganisation,
operator.RemoveOrganisation,
operator.AddOrganisationRole,
operator.RemoveOrganisationRole,
operator.AddOrganisationAddress,
operator.RemoveOrganisationAddress,
operator.AddDocumentInformation,
operator.RemoveDocumentInformation,
operator.AssignDocumentInformation,
@@ -86,11 +81,8 @@ if bpy is not None:
operator.UnassignDocumentReference,
operator.RemoveObjectDocumentReference,
operator.GenerateGlobalId,
operator.AddAttribute,
operator.RemoveAttribute,
operator.AddMaterialAttribute,
operator.RemoveMaterialAttribute,
operator.QuickProjectSetup,
operator.SelectGlobalId,
operator.SelectAttribute,
operator.SelectPset,
@@ -107,8 +99,6 @@ if bpy is not None:
operator.FetchLibraryInformation,
operator.FetchExternalMaterial,
operator.FetchObjectPassport,
operator.AddSubcontext,
operator.RemoveSubcontext,
operator.CutSection,
operator.AddSheet,
operator.OpenSheet,
@@ -133,8 +123,6 @@ if bpy is not None:
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
operator.SwitchContext,
operator.RemoveContext,
operator.OpenUpstream,
operator.BIM_OT_ChangeClassificationLevel,
operator.AddPropertySetTemplate,
@@ -151,7 +139,6 @@ if bpy is not None:
operator.ImportIfcCsv,
operator.EyedropIfcCsv,
operator.ReloadIfcFile,
operator.SelectSimilarType,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SelectDocIfcFile,
@@ -161,7 +148,6 @@ if bpy is not None:
operator.AddVariable,
operator.RemoveVariable,
operator.PropagateTextData,
operator.PushRepresentation,
operator.ConvertLocalToGlobal,
operator.ConvertGlobalToLocal,
operator.GuessQuantity,
@@ -203,15 +189,7 @@ if bpy is not None:
operator.RemoveDrawingStyleAttribute,
operator.CopyPropertyToSelection,
operator.CopyAttributeToSelection,
operator.CreateShapeFromStepId,
operator.SelectHighPolygonMeshes,
operator.InspectFromStepId,
operator.InspectFromObject,
operator.RewindInspector,
operator.RefreshDrawingList,
operator.GetRepresentationIfcParameters,
operator.BakeParametricGeometry,
operator.UpdateIfcRepresentation,
operator.SetBlenderClashSetA,
operator.SetBlenderClashSetB,
operator.ExecuteBlenderClash,
@@ -245,11 +223,8 @@ if bpy is not None:
prop.Schedule,
prop.DrawingStyle,
prop.Sheet,
prop.Subcontext,
prop.Representation,
prop.PresentationLayer,
prop.BIMProperties,
prop.BIMDebugProperties,
prop.DocProperties,
prop.BIMLibrary,
prop.MapConversion,
@@ -278,10 +253,6 @@ if bpy is not None:
ui.BIM_PT_search,
ui.BIM_PT_ifccsv,
ui.BIM_PT_ifcclash,
ui.BIM_PT_owner,
ui.BIM_PT_people,
ui.BIM_PT_organisations,
ui.BIM_PT_context,
ui.BIM_PT_qa,
ui.BIM_PT_library,
ui.BIM_PT_gis,
@@ -290,15 +261,10 @@ if bpy is not None:
ui.BIM_PT_cobie,
ui.BIM_PT_patch,
ui.BIM_PT_mvd,
ui.BIM_PT_debug,
ui.BIM_PT_material,
ui.BIM_PT_mesh,
ui.BIM_PT_presentation_layer_data,
ui.BIM_PT_object,
ui.BIM_PT_object_material,
ui.BIM_PT_object_psets,
ui.BIM_PT_object_qto,
ui.BIM_PT_representations,
ui.BIM_PT_classification_references,
ui.BIM_PT_documents,
ui.BIM_PT_constraint_relations,
@@ -322,9 +288,22 @@ if bpy is not None:
ui.BIM_ADDON_preferences,
]
classes.extend(module_root.classes)
classes.extend(module_aggregate.classes)
classes.extend(module_attribute.classes)
classes.extend(module_bcf.classes)
classes.extend(module_context.classes)
classes.extend(module_covetool.classes)
classes.extend(module_debug.classes)
classes.extend(module_geometry.classes)
classes.extend(module_model.classes)
classes.extend(module_owner.classes)
classes.extend(module_project.classes)
classes.extend(module_pset.classes)
classes.extend(module_spatial.classes)
classes.extend(module_style.classes)
classes.extend(module_type.classes)
classes.extend(module_unit.classes)
def menu_func_export(self, context):
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
@@ -344,7 +323,6 @@ if bpy is not None:
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Scene.BIMDebugProperties = bpy.props.PointerProperty(type=prop.BIMDebugProperties)
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
@@ -356,10 +334,24 @@ if bpy is not None:
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
module_root.register()
module_aggregate.register()
module_attribute.register()
module_bcf.register()
module_context.register()
module_covetool.register()
module_debug.register()
module_geometry.register()
module_model.register()
module_owner.register()
module_project.register()
module_pset.register()
module_spatial.register()
module_style.register()
module_type.register()
module_unit.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
def unregister():
for cls in reversed(classes):
@@ -368,7 +360,6 @@ if bpy is not None:
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
del bpy.types.Scene.BIMDebugProperties
del bpy.types.Scene.DocProperties
del bpy.types.Scene.MapConversion
del bpy.types.Scene.TargetCRS
@@ -379,7 +370,20 @@ if bpy is not None:
del bpy.types.Camera.BIMCameraProperties
del bpy.types.TextCurve.BIMTextProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
module_unit.unregister()
module_type.unregister()
module_style.unregister()
module_spatial.unregister()
module_pset.unregister()
module_project.unregister()
module_owner.unregister()
module_model.unregister()
module_geometry.unregister()
module_debug.unregister()
module_covetool.unregister()
module_context.unregister()
module_bcf.unregister()
module_attribute.register()
module_aggregate.register()
module_root.unregister()
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
+128 -126
View File
@@ -21,7 +21,6 @@ class BaseDecorator():
DEF_GLSL = """
#define PI 3.141592653589793
#define COLOR vec4({color[0]}, {color[1]}, {color[2]}, {color[3]})
#define MAX_POINTS 64
#define CIRCLE_SEGS 24
"""
@@ -99,63 +98,28 @@ class BaseDecorator():
"""
FRAG_GLSL = """
uniform vec4 color;
out vec4 fragColor;
void main() {
fragColor = COLOR;
fragColor = color;
}
"""
# class var for single handler
installed = None
@classmethod
def install(cls, *args, **kwargs):
if cls.installed:
cls.uninstall()
handler = cls(*args, **kwargs)
cls.installed = SpaceView3D.draw_handler_add(handler, (), 'WINDOW', 'POST_PIXEL')
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, 'WINDOW')
except ValueError:
pass
cls.installed = None
def __init__(self, props, context):
self.context = context
self.props = props
self.shader = self.create_shader()
self.font_id = 0
self.font_size = 16
self.dpi = context.preferences.system.dpi
def create_shader(self):
defines = self.DEF_GLSL.format(color=self.props.decorations_colour)
def __init__(self):
# NB: libcode param doesn't work
return GPUShader(vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
geocode=self.LIB_GLSL + self.GEOM_GLSL,
defines=defines)
self.shader = GPUShader(vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
geocode=self.LIB_GLSL + self.GEOM_GLSL,
defines=self.DEF_GLSL)
def __call__(self):
if self.props.active_drawing_index is None or len(self.props.drawings) == 0:
return
for obj in self.get_objects():
self.decorate(obj)
def get_objects(self):
def get_objects(self, collection):
"""find relevant objects
using class.basename
returns: iterable of blender objects
"""
drawing = self.props.drawings[self.props.active_drawing_index]
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
return filter(lambda o: self.basename in o.name, collection.objects)
return filter(lambda o: self.basename in o.name, collection.all_objects)
def get_path_geom(self, obj, topo=True):
"""Parses path geometry into line segments
@@ -223,13 +187,14 @@ class BaseDecorator():
return vertices, indices
def decorate(self, object):
def decorate(self, context, object):
"""perform actuall drawing stuff"""
raise NotImplementedError()
def draw_lines(self, obj, vertices, indices, topology=None):
region = self.context.region
region3d = self.context.region_data
def draw_lines(self, context, obj, vertices, indices, topology=None):
region = context.region
region3d = context.region_data
color = context.scene.DocProperties.decorations_colour
fmt = GPUVertFormat()
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
@@ -245,13 +210,20 @@ class BaseDecorator():
batch = GPUBatch(type='LINES', buf=vbo, elem=ibo)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
self.shader.bind()
self.shader.uniform_float
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
self.shader.uniform_float("winsize", (region.width, region.height))
self.shader.uniform_float("color", color)
batch.draw(self.shader)
def draw_label(self, text, pos, dir, gap=4, center=True, vcenter=False):
def draw_label(self, context, text, pos, dir, gap=4, center=True, vcenter=False):
"""Draw text label
Args:
@@ -259,15 +231,21 @@ class BaseDecorator():
aligned and centered at segment middle
"""
font_id = 0
font_size = 16
dpi = context.preferences.system.dpi
color = context.scene.DocProperties.decorations_colour
ang = -Vector((1, 0)).angle_signed(dir)
cos = math.cos(ang)
sin = math.sin(ang)
blf.size(self.font_id, self.font_size, self.dpi)
blf.size(font_id, font_size, dpi)
w, h = 0, 0
if center or vcenter:
w, h = blf.dimensions(self.font_id, text)
w, h = blf.dimensions(font_id, text)
if center:
# horizontal centering
@@ -281,18 +259,18 @@ class BaseDecorator():
# side-shifting
pos += Vector((-sin, cos)) * gap
blf.enable(self.font_id, blf.ROTATION)
blf.position(self.font_id, pos.x, pos.y, 0)
blf.enable(font_id, blf.ROTATION)
blf.position(font_id, pos.x, pos.y, 0)
blf.rotation(self.font_id, ang)
blf.color(self.font_id, *self.props.decorations_colour)
blf.draw(self.font_id, text)
blf.disable(self.font_id, blf.ROTATION)
blf.rotation(font_id, ang)
blf.color(font_id, *color)
blf.draw(font_id, text)
blf.disable(font_id, blf.ROTATION)
def format_value(self, value):
unit_system = bpy.context.scene.unit_settings.system
def format_value(self, context, value):
unit_system = context.scene.unit_settings.system
if unit_system == 'IMPERIAL':
precision = bpy.context.scene.BIMProperties.imperial_precision
precision = context.scene.BIMProperties.imperial_precision
if precision == "NONE":
precision = 256
elif precision == "1":
@@ -313,7 +291,6 @@ class DimensionDecorator(BaseDecorator):
- puts metric text next to each segment
"""
basename = "IfcAnnotation/Dimension"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
@@ -377,14 +354,14 @@ class DimensionDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, _ = self.get_path_geom(obj, topo=False)
self.draw_lines(obj, verts, idxs)
self.draw_labels(obj, verts, idxs)
self.draw_lines(context, obj, verts, idxs)
self.draw_labels(context, obj, verts, idxs)
def draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices, indices):
region = context.region
region3d = context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
@@ -394,8 +371,8 @@ class DimensionDecorator(BaseDecorator):
if dir.length < 1:
continue
length = (v1 - v0).length
text = self.format_value(length)
self.draw_label(text, p0 + (dir) * .5, dir)
text = self.format_value(context, length)
self.draw_label(context, text, p0 + (dir) * .5, dir)
class EqualityDecorator(DimensionDecorator):
@@ -405,11 +382,10 @@ class EqualityDecorator(DimensionDecorator):
- puts 'EQ' label
"""
basename = "IfcAnnotation/Equal"
installed = None
def draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices, indices):
region = context.region
region3d = context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
@@ -418,7 +394,7 @@ class EqualityDecorator(DimensionDecorator):
dir = p1 - p0
if dir.length < 1:
continue
self.draw_label("EQ", p0 + (dir) * .5, dir)
self.draw_label(context, "EQ", p0 + (dir) * .5, dir)
class LeaderDecorator(BaseDecorator):
@@ -427,7 +403,6 @@ class LeaderDecorator(BaseDecorator):
- middle points w/out decorations
"""
basename = "IfcAnnotation/Leader"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
@@ -484,9 +459,9 @@ class LeaderDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
class StairDecorator(BaseDecorator):
@@ -496,7 +471,6 @@ class StairDecorator(BaseDecorator):
- middle points w/out decorations
"""
basename = "IfcAnnotation/Stair"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
@@ -570,14 +544,13 @@ class StairDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
class HiddenDecorator(BaseDecorator):
basename = "IfcAnnotation/Hidden"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define DASH_SIZE 16.0
@@ -619,6 +592,7 @@ class HiddenDecorator(BaseDecorator):
"""
FRAG_GLSL = """
uniform vec4 color;
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
@@ -626,21 +600,20 @@ class HiddenDecorator(BaseDecorator):
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
fragColor = color;
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
if obj.data.is_editmode:
verts, idxs = self.get_editmesh_geom(obj)
else:
verts, idxs = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, idxs)
self.draw_lines(context, obj, verts, idxs)
class MiscDecorator(HiddenDecorator):
basename = "IfcAnnotation/Misc"
installed = None
FRAG_GLSL = BaseDecorator.FRAG_GLSL
@@ -661,16 +634,15 @@ class LevelDecorator(BaseDecorator):
continue
yield [obj.matrix_world @ p.co for p in spline_points]
def decorate(self, obj):
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
self.draw_lines(context, obj, verts, idxs, topo)
splines = self.get_splines(obj)
self.draw_labels(obj, splines)
self.draw_labels(context, obj, splines)
class PlanDecorator(LevelDecorator):
basename = "IfcAnnotation/Plan Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
@@ -736,9 +708,9 @@ class PlanDecorator(LevelDecorator):
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, splines):
region = context.region
region3d = context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
@@ -747,13 +719,12 @@ class PlanDecorator(LevelDecorator):
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0, dir, gap=8, center=False)
text = "RL " + self.format_value(context, verts[-1].z)
self.draw_label(context, text, p0, dir, gap=8, center=False)
class SectionDecorator(LevelDecorator):
basename = "IfcAnnotation/Section Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CALLOUT_GAP 8.0
@@ -816,9 +787,9 @@ class SectionDecorator(LevelDecorator):
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, splines):
region = context.region
region3d = context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
@@ -827,8 +798,8 @@ class SectionDecorator(LevelDecorator):
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
text = "RL " + self.format_value(context, verts[-1].z)
self.draw_label(context, text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
class BreakDecorator(BaseDecorator):
@@ -838,7 +809,6 @@ class BreakDecorator(BaseDecorator):
Uses first two vertices in verts list.
"""
basename = "IfcAnnotation/Break"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define BREAK_LENGTH 32.0
@@ -893,12 +863,12 @@ class BreakDecorator(BaseDecorator):
}
"""
def decorate(self, obj):
def decorate(self, context, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
self.draw_lines(context, obj, verts, [(0, 1)])
def get_mesh_geom(self, obj):
# first vertices only
@@ -914,7 +884,6 @@ class BreakDecorator(BaseDecorator):
class GridDecorator(BaseDecorator):
basename = "IfcGridAxis/"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 16.0
@@ -983,6 +952,7 @@ class GridDecorator(BaseDecorator):
"""
FRAG_GLSL = """
uniform vec4 color;
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
@@ -990,7 +960,7 @@ class GridDecorator(BaseDecorator):
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
fragColor = color;
}
"""
@@ -1005,36 +975,68 @@ class GridDecorator(BaseDecorator):
vertices = [obj.matrix_world @ v.co for v in mesh.edges[0].verts]
return vertices
def decorate(self, obj):
def decorate(self, context, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
self.draw_labels(obj, verts)
self.draw_lines(context, obj, verts, [(0, 1)])
self.draw_labels(context, obj, verts)
def draw_labels(self, obj, vertices):
region = self.context.region
region3d = self.context.region_data
def draw_labels(self, context, obj, vertices):
region = context.region
region3d = context.region_data
v0 = Vector(vertices[0])
v1 = Vector(vertices[1])
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = Vector((1, 0))
text = obj.BIMObjectProperties.attributes['AxisTag'].string_value
self.draw_label(text, p0, dir, vcenter=True, gap=0)
self.draw_label(text, p1, dir, vcenter=True, gap=0)
self.draw_label(context, text, p0, dir, vcenter=True, gap=0)
self.draw_label(context, text, p1, dir, vcenter=True, gap=0)
all_decorators = [
DimensionDecorator,
EqualityDecorator,
GridDecorator,
HiddenDecorator,
LeaderDecorator,
MiscDecorator,
PlanDecorator,
SectionDecorator,
StairDecorator,
BreakDecorator
]
class DecorationsHandler():
decorators_classes = [
DimensionDecorator,
EqualityDecorator,
GridDecorator,
HiddenDecorator,
LeaderDecorator,
MiscDecorator,
PlanDecorator,
SectionDecorator,
StairDecorator,
BreakDecorator
]
installed = None
@classmethod
def install(cls, context):
if cls.installed:
cls.uninstall()
handler = cls()
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), 'WINDOW', 'POST_PIXEL')
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, 'WINDOW')
except ValueError:
pass
cls.installed = None
def __init__(self):
self.decorators = [cls() for cls in self.decorators_classes]
def __call__(self, context):
props = context.scene.DocProperties
if props.active_drawing_index is None or len(props.drawings) == 0:
return
drawing = props.drawings[props.active_drawing_index]
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
for decorator in self.decorators:
for obj in decorator.get_objects(collection):
decorator.decorate(context, obj)
+70 -100
View File
@@ -8,7 +8,6 @@ import os
import zipfile
import tempfile
import ifcopenshell
import ifcopenshell.util.pset
import ifcopenshell.util.schema
from pathlib import Path
from mathutils import Vector, Matrix
@@ -60,7 +59,6 @@ class IfcParser:
self.rel_contained_in_spatial_structure = {}
self.rel_nests = {}
self.rel_space_boundaries = {}
self.rel_defines_by_type = {}
self.rel_defines_by_qto = {}
self.rel_defines_by_pset = {}
self.rel_associates_document_object = {}
@@ -372,9 +370,6 @@ class IfcParser:
product.update(metadata_override)
type_product = obj.BIMObjectProperties.relating_type
if type_product and self.is_a_type(self.get_ifc_class(type_product.name)):
reference = self.get_type_product_reference(type_product.name)
self.rel_defines_by_type.setdefault(reference, []).append(self.product_index)
if product["has_boundary_condition"]:
product["boundary_condition_class"] = obj.BIMObjectProperties.boundary_condition.name
@@ -472,39 +467,28 @@ class IfcParser:
continue
results[item_key] = {"ifc": None, "raw": raw, "material": material, "attributes": {"Name": item.name}}
def add_automatic_qtos(self, ifc_class, obj):
def add_automatic_qtos(self, ifc_class: str, obj):
if not obj.data:
return
qto_names = self.get_applicable_qtos(ifc_class)
for name in qto_names:
if name not in ifcopenshell.util.pset.qtos:
continue
applicable_qtos = schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
for applicable_qto in applicable_qtos:
has_automatic_value = False
props = ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys()
guessed_values = {}
for prop_name in props:
value = self.qto_calculator.guess_quantity(prop_name, props, obj)
prop_names = [p.Name for p in applicable_qto.HasPropertyTemplates]
for prop_name in prop_names:
value = self.qto_calculator.guess_quantity(prop_name, prop_names, obj)
if value:
guessed_values[prop_name] = value
has_automatic_value = True
if has_automatic_value:
qto = obj.BIMObjectProperties.qtos.add()
qto.name = name
for prop_name in props:
qto.name = applicable_qto.Name
for prop_name in prop_names:
prop = qto.properties.add()
prop.name = prop_name
if prop_name in guessed_values:
prop.string_value = str(guessed_values[prop_name])
def get_applicable_qtos(self, ifc_class):
results = []
empty = ifcopenshell.file(schema=self.ifc_export_settings.schema)
element = empty.create_entity(ifc_class)
for ifc_class, qto_names in schema.ifc.applicable_qtos.items():
if element.is_a(ifc_class):
results.extend(qto_names)
return results
def get_product_relating_structure(self, product, obj):
relating_structure = obj.BIMObjectProperties.relating_structure
if relating_structure:
@@ -1057,29 +1041,32 @@ class IfcParser:
for context in self.ifc_export_settings.context_tree:
for subcontext in context["subcontexts"]:
for target_view in subcontext["target_views"]:
rep_context = self.get_obj_representation_context(obj, context["name"], subcontext["name"], target_view)
if rep_context:
self.append_representation_in_context(obj, rep_context, name)
representation = self.get_shape_representation(obj, context["name"], subcontext["name"], target_view)
if representation:
self.append_representation_in_context(obj, representation, name)
def get_obj_representation_context(self, obj, context, subcontext, target_view):
for c in obj.BIMObjectProperties.representation_contexts:
if c.context == context and c.name == subcontext and c.target_view == target_view:
return c
if obj.BIMObjectProperties.representation_contexts:
def get_shape_representation(self, obj, context, subcontext, target_view):
for representation in obj.BIMObjectProperties.representations:
c = self.stored_file.by_id(representation.ifc_definition_id)
if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view:
return representation
if obj.BIMObjectProperties.representations:
return
if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
representation_context = obj.BIMObjectProperties.representation_contexts.add()
representation_context.context = "Model"
representation_context.name = "Body"
representation_context.target_view = "MODEL_VIEW"
return representation_context
# TODO: reimplement - see bug #1222
#if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW":
# representation_context = obj.BIMObjectProperties.representation_contexts.add()
# representation_context.context = "Model"
# representation_context.name = "Body"
# representation_context.target_view = "MODEL_VIEW"
# return representation_context
def append_representation_in_context(self, obj, rep_context, name):
context = rep_context.context
subcontext = rep_context.name
target_view = rep_context.target_view
def append_representation_in_context(self, obj, shape_representation, name):
context_of_items = self.stored_file.by_id(shape_representation.ifc_definition_id).ContextOfItems
context = context_of_items.ContextType
subcontext = context_of_items.ContextIdentifier
target_view = context_of_items.TargetView
if self.ifc_export_settings.should_roundtrip_native and rep_context.ifc_definition_id:
if self.ifc_export_settings.should_roundtrip_native and shape_representation.ifc_definition_id:
self.representations[
"{}/{}/{}/{}".format(context, subcontext, target_view, name)
] = self.get_representation(obj.data, obj, context, subcontext, target_view)
@@ -1088,7 +1075,6 @@ class IfcParser:
context_prefix = "/".join([context, subcontext, target_view])
mesh_name = "/".join([context_prefix, name])
mesh = self.search_for_mesh_or_curve_data(mesh_name)
# TODO: if the search result is empty, we should check for ifc_definition_id
if mesh:
self.representations[mesh_name] = self.get_representation(mesh, obj, context, subcontext, target_view)
if "Model/Box/MODEL_VIEW" in self.generated_subcontexts and context_prefix == "Model/Body/MODEL_VIEW":
@@ -1113,7 +1099,7 @@ class IfcParser:
return data
def get_representation(self, mesh, obj, context, subcontext, target_view):
rep_context = self.get_obj_representation_context(obj, context, subcontext, target_view)
representation = self.get_shape_representation(obj, context, subcontext, target_view)
return {
"ifc": None,
"raw": mesh,
@@ -1121,9 +1107,9 @@ class IfcParser:
"context": context,
"subcontext": subcontext,
"target_view": target_view,
"has_ifc_definition": rep_context and rep_context.ifc_definition_id,
"has_ifc_definition": representation and representation.ifc_definition_id,
"ifc_definition": mesh.BIMMeshProperties.ifc_definition if hasattr(mesh, "BIMMeshProperties") else None,
"ifc_definition_id": rep_context.ifc_definition_id if rep_context else 0
"ifc_definition_id": representation.ifc_definition_id if representation else 0
if hasattr(mesh, "BIMMeshProperties")
else None,
"is_parametric": mesh.BIMMeshProperties.is_parametric if hasattr(mesh, "BIMMeshProperties") else False,
@@ -1345,8 +1331,9 @@ class IfcExporter:
self.roundtrip_id_new_to_old = {}
def export(self, selected_objects):
self.file = ifc.IfcStore.get_file()
if self.file and self.ifc_export_settings.should_export_from_memory:
self.stored_file = ifc.IfcStore.get_file() # See bug #1222
if self.stored_file and self.ifc_export_settings.should_export_from_memory:
self.file = self.stored_file
return self.write_ifc_file()
self.schema_version = self.ifc_export_settings.schema
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema_version)
@@ -1383,7 +1370,6 @@ class IfcExporter:
self.relate_objects_to_objects()
self.relate_elements_to_spatial_structures()
self.relate_nested_elements_to_hosted_elements()
self.relate_objects_to_types()
self.relate_objects_to_qtos()
self.relate_objects_to_psets()
self.relate_objects_to_opening_elements()
@@ -1423,15 +1409,16 @@ class IfcExporter:
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
self.get_application_name(), self.get_application_version()
)
if self.owner_history:
if self.schema_version == "IFC2X3":
self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
else:
self.file.wrapped_data.header.file_name.authorization = (
self.owner_history.OwningUser.ThePerson.Identification
)
else:
self.file.wrapped_data.header.file_name.authorization = "Nobody"
# TODO: reimplement. See #1222.
#if self.owner_history:
# if self.schema_version == "IFC2X3":
# self.file.wrapped_data.header.file_name.authorization = self.owner_history.OwningUser.ThePerson.Id
# else:
# self.file.wrapped_data.header.file_name.authorization = (
# self.owner_history.OwningUser.ThePerson.Identification
# )
#else:
# self.file.wrapped_data.header.file_name.authorization = "Nobody"
def get_application_name(self):
return "BlenderBIM"
@@ -1699,13 +1686,15 @@ class IfcExporter:
pset["ifc"] = self.file.create_entity("IfcMaterialProperties", **pset["attributes"])
def create_qto_properties(self, qto):
if qto["attributes"]["Name"] in ifcopenshell.util.pset.qtos:
return self.create_templated_qto_properties(qto)
qto_template = schema.ifc.psetqto.get_by_name(qto["attributes"]["Name"])
if qto_template:
return self.create_templated_qto_properties(qto, qto_template)
return self.create_custom_qto_properties(qto)
def create_pset_properties(self, pset):
if pset["attributes"]["Name"] in ifcopenshell.util.pset.psets:
return self.create_templated_pset_properties(pset)
pset_template = schema.ifc.psetqto.get_by_name(pset["attributes"]["Name"])
if pset_template:
return self.create_templated_pset_properties(pset, pset_template)
return self.create_custom_pset_properties(pset)
def create_custom_pset_properties(self, pset):
@@ -1735,15 +1724,15 @@ class IfcExporter:
)
return properties
def create_templated_pset_properties(self, pset):
def create_templated_pset_properties(self, pset, pset_template):
properties = []
templates = ifcopenshell.util.pset.psets[pset["attributes"]["Name"]]["HasPropertyTemplates"]
for name, data in templates.items():
for prop in pset_template.HasPropertyTemplates:
name = prop.Name
if name not in pset["raw"]:
continue
if data.TemplateType == "P_SINGLEVALUE" or data.TemplateType == "P_ENUMERATEDVALUE":
if data.PrimaryMeasureType:
value_type = data.PrimaryMeasureType
if prop.TemplateType == "P_SINGLEVALUE" or prop.TemplateType == "P_ENUMERATEDVALUE":
if prop.PrimaryMeasureType:
value_type = prop.PrimaryMeasureType
else:
# The IFC spec is missing some, so we provide a fallback
value_type = "IfcLabel"
@@ -1753,7 +1742,8 @@ class IfcExporter:
properties.append(
self.file.create_entity("IfcPropertySingleValue", **{"Name": name, "NominalValue": nominal_value})
)
invalid_pset_keys = [k for k in pset["raw"].keys() if k not in templates.keys()]
templates_names = [prop.Name for prop in qto_template.HasPropertyTemplates]
invalid_pset_keys = [k for k in pset["raw"].keys() if k not in templates_names]
if invalid_pset_keys:
self.ifc_export_settings.logger.error(
"One or more properties were invalid in the pset {}: {}".format(
@@ -1762,20 +1752,21 @@ class IfcExporter:
)
return properties
def create_templated_qto_properties(self, qto):
def create_templated_qto_properties(self, qto, qto_template):
properties = []
templates = ifcopenshell.util.pset.qtos[qto["attributes"]["Name"]]["HasPropertyTemplates"]
for name, data in templates.items():
for prop in qto_template.HasPropertyTemplates:
name = prop.Name
if name not in qto["raw"]:
continue
if data.TemplateType[0:2] == "Q_":
value_basename = data.TemplateType[2:].title()
if prop.TemplateType[0:2] == "Q_":
value_basename = prop.TemplateType[2:].title()
value_name = f"{value_basename}Value"
class_name = f"IfcQuantity{value_basename}"
properties.append(
self.file.create_entity(class_name, **{"Name": name, value_name: float(qto["raw"][name])})
)
invalid_qto_keys = [k for k in qto["raw"].keys() if k not in templates.keys()]
templates_names = [prop.Name for prop in qto_template.HasPropertyTemplates]
invalid_qto_keys = [k for k in qto["raw"].keys() if k not in templates_names]
if invalid_qto_keys:
self.ifc_export_settings.logger.error(
"One or more properties were invalid in the qto {}/{}: {}".format(
@@ -2039,8 +2030,8 @@ class IfcExporter:
# At the moment, we assume that styled items only apply to the body context.
if representation.RepresentationIdentifier != "Body":
continue
rep_context = self.ifc_parser.get_obj_representation_context(product["raw"], "Model", "Body", "MODEL_VIEW")
if self.ifc_export_settings.should_roundtrip_native and rep_context and rep_context.ifc_definition_id:
rep = self.ifc_parser.get_shape_representation(product["raw"], "Model", "Body", "MODEL_VIEW")
if self.ifc_export_settings.should_roundtrip_native and rep and rep.ifc_definition_id:
# For native roundtripping, each slot could be a one to many relationship to items
for item in self.get_geometric_representation_items(representation):
original_id = self.roundtrip_id_new_to_old[item.id()]
@@ -3181,17 +3172,6 @@ class IfcExporter:
[o["ifc"] for o in related_objects],
)
def relate_objects_to_types(self):
for relating_type, related_objects in self.ifc_parser.rel_defines_by_type.items():
self.file.createIfcRelDefinesByType(
ifcopenshell.guid.new(),
self.owner_history,
None,
None,
[self.ifc_parser.products[o]["ifc"] for o in related_objects],
self.ifc_parser.type_products[relating_type]["ifc"],
)
def relate_objects_to_qtos(self):
for relating_property_key, related_objects in self.ifc_parser.rel_defines_by_qto.items():
self.file.createIfcRelDefinesByProperties(
@@ -3468,6 +3448,7 @@ class IfcExporter:
return co * self.ifc_parser.unit_scale
def write_ifc_file(self):
self.set_header()
extension = self.ifc_export_settings.output_file.split(".")[-1]
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
@@ -3551,15 +3532,4 @@ class IfcExportSettings:
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
settings.should_export_from_memory = scene_bim.export_should_export_from_memory
settings.context_tree = []
for ifc_context in ["model", "plan"]:
if getattr(scene_bim, "has_{}_context".format(ifc_context)):
subcontexts = {}
for subcontext in getattr(scene_bim, "{}_subcontexts".format(ifc_context)):
subcontexts.setdefault(subcontext.name, []).append(subcontext.target_view)
settings.context_tree.append(
{
"name": ifc_context.title(),
"subcontexts": [{"name": key, "target_views": value} for key, value in subcontexts.items()],
}
)
return settings
@@ -1,4 +1,6 @@
import math
from mathutils import geometry
from mathutils import Vector
import bpy
@@ -285,3 +287,84 @@ def format_distance(value, isArea=False, hide_units=True):
tx_dist = fmt % value
return tx_dist
def parse_diagram_scale(camera):
"""Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
else:
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
return float(numerator) / float(denominator)
def ortho_view_frame(camera, margin=0.015):
"""Calculates 2d bounding box of camera view area.
Similar to `bpy.types.Camera.view_frame`
:arg camera: camera of drawing
:type camera: bpy.types.Camera + BIMCameraProperties
:arg margin: margins, in scene units
:type margin: float
:return: (xmin, xmax, ymin, ymax) in local camera coordinates
"""
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale
hwidth = size * .5
hheight = size * .5 * aspect
scale = parse_diagram_scale(camera)
xmarg = margin * scale
ymarg = margin * scale * aspect
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg)
def clip_segment(bounds, segm):
"""Clipping line segment to bounds
:arg bounds: (xmin, xmax, ymin, ymax)
:arg segm: 2 vertices of the segment
:return: 2 new vertices of segment or None if segment outside the bounding box
"""
# LiangBarsky algorithm
xmin, xmax, ymin, ymax = bounds
p1, p2 = segm
def clip_side(p, q):
if abs(p) < 1e-10: # ~= 0, parallel to the side
if q < 0:
return None # outside
else:
return 0, 1 # inside
t = q / p # the intersection point
if p < 0: # entering
return t, 1
else: # leaving
return 0, t
dlt = p2 - p1
tt = (
clip_side(-dlt.x, p1.x - xmin), # left
clip_side(+dlt.x, xmax - p1.x), # right
clip_side(-dlt.y, p1.y - ymin), # bottom
clip_side(+dlt.y, ymax - p1.y), # top
)
if None in tt:
return None
t1 = max(0, max(t[0] for t in tt))
t2 = min(1, min(t[1] for t in tt))
if t1 >= t2:
return None
p1c = p1 + dlt * t1
p2c = p1 + dlt * t2
return p1c, p2c
@@ -5,6 +5,7 @@ import ifcopenshell
class IfcStore:
path = ""
file = None
schema = None
pset_template_path = ""
pset_template_file = None
@@ -15,3 +16,11 @@ class IfcStore:
if IfcStore.path:
IfcStore.file = ifcopenshell.open(IfcStore.path)
return IfcStore.file
@staticmethod
def get_schema():
if IfcStore.file is None:
return
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
+35 -109
View File
@@ -3,7 +3,7 @@ import ifcopenshell.geom
import ifcopenshell.util.geolocation
import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.pset
import ifcopenshell.util.unit
import bpy
import bmesh
import os
@@ -20,8 +20,8 @@ import tempfile
from pathlib import Path
from itertools import cycle
from datetime import datetime
from . import helper
from . import ifc
from . import schema
class FileCopy(threading.Thread):
@@ -256,6 +256,7 @@ class MaterialCreator:
def create_new_single(self, material):
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
obj.BIMMaterialProperties.ifc_definition_id = int(material.id())
self.ifc_importer.add_element_attributes(material, obj.BIMMaterialProperties)
for pset in getattr(material, "HasProperties", ()):
self.ifc_importer.add_pset(pset, obj.BIMMaterialProperties)
@@ -286,6 +287,7 @@ class MaterialCreator:
for style in styles:
if not style.is_a("IfcSurfaceStyle"):
continue
material.BIMMaterialProperties.ifc_style_id = int(style.id())
external_style = None
for surface_style in style.Styles:
if surface_style.is_a("IfcSurfaceStyleShading"):
@@ -373,7 +375,6 @@ class IfcImporter:
self.native_data = {}
self.groups = {}
self.aggregates = {}
self.aggregate_collection = None
self.aggregate_collections = {}
self.material_creator = MaterialCreator(ifc_import_settings, self)
@@ -409,8 +410,6 @@ class IfcImporter:
self.profile_code("Patching ifc")
self.set_units()
self.profile_code("Set units")
self.create_geometric_representation_contexts()
self.profile_code("Create contexts")
self.create_project()
self.profile_code("Create project")
self.create_classifications()
@@ -773,6 +772,7 @@ class IfcImporter:
obj = self.existing_elements[element.GlobalId]
else:
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.add_element_attributes(element, obj.BIMObjectProperties)
group_collection.objects.link(obj)
self.groups[element.GlobalId] = {"ifc": element, "blender": obj}
@@ -809,6 +809,7 @@ class IfcImporter:
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
mesh = self.create_mesh(axis, shape)
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
obj.matrix_world = matrix_world
self.add_element_attributes(axis, obj.BIMObjectProperties)
grid.objects.link(obj)
@@ -843,12 +844,10 @@ class IfcImporter:
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.material_creator.create(element, obj, mesh)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_type_product_psets(element, obj)
self.add_product_representations(element, obj)
self.type_collection.objects.link(obj)
self.type_products[element.GlobalId] = obj
@@ -950,11 +949,16 @@ class IfcImporter:
mesh = self.create_native_mesh(element, shape)
if mesh is None:
mesh = self.create_mesh(element, shape)
if "-" in shape.geometry.id:
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id.split("-")[0])
else:
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id)
self.meshes[mesh_name] = mesh
else:
mesh = None
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
if shape:
m = shape.transformation.matrix.data
@@ -968,13 +972,10 @@ class IfcImporter:
obj.matrix_world = self.get_element_matrix(element)
self.add_element_representation_items(element, obj)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_opening_relation(element, obj)
self.add_product_definitions(element, obj)
self.add_product_representations(element, obj)
self.added_data[element.GlobalId] = obj
if element.is_a("IfcOpeningElement"):
@@ -1309,7 +1310,6 @@ class IfcImporter:
# Occurs when reloading a project
pass
project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name]
project_collection.children[self.aggregate_collection.name].hide_viewport = True
project_collection.children[self.opening_collection.name].hide_viewport = True
project_collection.children[self.type_collection.name].hide_viewport = True
@@ -1355,47 +1355,21 @@ class IfcImporter:
bpy.ops.mesh.normals_make_consistent(context_override)
bpy.ops.object.editmode_toggle(context_override)
def add_product_representations(self, element, obj):
if element.is_a("IfcProduct"):
if not element.Representation:
return
for r in element.Representation.Representations:
new = obj.BIMObjectProperties.representations.add()
new.name = r.RepresentationIdentifier
new.type = r.RepresentationType
new.ifc_definition_id = r.id()
elif element.is_a("IfcTypeProduct"):
if not element.RepresentationMaps:
return
for r in element.RepresentationMaps:
new = obj.BIMObjectProperties.representations.add()
new.name = r.MappedRepresentation.RepresentationIdentifier
new.type = r.MappedRepresentation.RepresentationType
new.ifc_definition_id = r.MappedRepresentation.id()
def add_product_definitions(self, element, obj):
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
return
for definition in element.IsDefinedBy:
if not definition.is_a("IfcRelDefinesByProperties"):
continue
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
self.add_pset(definition.RelatingPropertyDefinition, obj.BIMObjectProperties)
elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
self.add_qto(definition.RelatingPropertyDefinition, obj)
def add_type_product_psets(self, element, obj):
if not hasattr(element, "HasPropertySets") or not element.HasPropertySets:
return
for definition in element.HasPropertySets:
if definition.is_a("IfcPropertySet"):
self.add_pset(definition, obj.BIMObjectProperties)
def add_pset(self, pset, props):
new_pset = props.psets.add()
new_pset.name = pset.Name
if new_pset.name in ifcopenshell.util.pset.psets:
for prop_name in ifcopenshell.util.pset.psets[new_pset.name]["HasPropertyTemplates"].keys():
pset_template = schema.ifc.psetqto.get_by_name(new_pset.name)
if pset_template:
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
prop = new_pset.properties.add()
prop.name = prop_name
try:
@@ -1421,8 +1395,9 @@ class IfcImporter:
def add_qto(self, qto, obj):
new_qto = obj.BIMObjectProperties.qtos.add()
new_qto.name = str(qto.Name)
if new_qto.name in ifcopenshell.util.pset.qtos:
for prop_name in ifcopenshell.util.pset.qtos[new_qto.name]["HasPropertyTemplates"].keys():
qto_template = schema.ifc.psetqto.get_by_name(new_qto.name)
if qto_template:
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
prop = new_qto.properties.add()
prop.name = prop_name
for prop in qto.Quantities:
@@ -1438,11 +1413,6 @@ class IfcImporter:
new_prop.name = prop.Name
new_prop.string_value = str(value)
def add_defines_by_type_relation(self, element, obj):
related_type = ifcopenshell.util.element.get_type(element)
if related_type:
obj.BIMObjectProperties.relating_type = self.type_products[related_type.GlobalId]
def add_opening_relation(self, element, obj):
if not element.is_a("IfcOpeningElement"):
return
@@ -1499,7 +1469,7 @@ class IfcImporter:
self.unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
unit = unit.ConversionFactor.UnitComponent
if unit.is_a("IfcSIUnit"):
self.unit_scale *= helper.SIUnitHelper.get_prefix_multiplier(unit.Prefix)
self.unit_scale *= ifcopenshell.util.unit.get_prefix_multiplier(unit.Prefix)
def set_units(self):
units = self.file.by_type("IfcUnitAssignment")[0]
@@ -1530,28 +1500,6 @@ class IfcImporter:
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
)
def create_geometric_representation_contexts(self):
bpy.context.scene.BIMProperties.has_model_context = False
for context in self.file.by_type("IfcGeometricRepresentationContext"):
if context.is_a("IfcGeometricRepresentationSubContext"):
if not context.ContextIdentifier:
# Revit creates invalid contexts, so we just ignore them
continue
if context.ContextType == "Model":
subcontexts = bpy.context.scene.BIMProperties.model_subcontexts
elif context.ContextType == "Plan":
subcontexts = bpy.context.scene.BIMProperties.plan_subcontexts
if subcontexts.get(context.ContextIdentifier):
continue
subcontext = subcontexts.add()
subcontext.name = context.ContextIdentifier
subcontext.target_view = context.TargetView
subcontext.ifc_definition_id = context.id()
elif context.ContextType == "Model":
bpy.context.scene.BIMProperties.has_model_context = True
elif context.ContextType == "Plan":
bpy.context.scene.BIMProperties.has_plan_context = True
def create_project(self):
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
if self.project["ifc"].GlobalId in self.existing_elements:
@@ -1743,30 +1691,23 @@ class IfcImporter:
]
else:
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
for collection in self.project["blender"].children:
if collection.name == "Aggregates":
self.aggregate_collection = collection
break
if not self.aggregate_collection:
self.aggregate_collection = bpy.data.collections.new("Aggregates")
self.project["blender"].children.link(self.aggregate_collection)
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
def create_aggregate(self, rel_aggregate):
collection = bpy.data.collections.new(f"IfcRelAggregates/{rel_aggregate.id()}")
self.aggregate_collection.children.link(collection)
element = rel_aggregate.RelatingObject
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
obj.instance_type = "COLLECTION"
obj.instance_collection = collection
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.place_object_in_spatial_tree(element, obj)
self.add_element_attributes(element, obj.BIMObjectProperties)
collection = bpy.data.collections.new(obj.name)
obj.users_collection[0].children.link(collection)
obj.users_collection[0].objects.unlink(obj)
collection.objects.link(obj)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.aggregates[element.GlobalId] = obj
self.aggregate_collections[rel_aggregate.id()] = collection
@@ -1823,12 +1764,11 @@ class IfcImporter:
return
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.material_creator.create(element, obj, mesh)
obj.matrix_world = self.get_element_matrix(element, mesh_name)
self.add_element_attributes(element, obj.BIMObjectProperties)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.added_data[element.GlobalId] = obj
@@ -1869,8 +1809,6 @@ class IfcImporter:
):
container = element.ContainedInStructure[0].RelatingStructure
if container.is_a("IfcSpace"):
if self.ifc_import_settings.should_import_spaces and container.GlobalId in self.added_data:
obj.BIMObjectProperties.relating_structure = self.added_data[container.GlobalId]
return self.place_object_in_spatial_tree(container, obj)
elif element.is_a("IfcGrid"):
grid_collection = bpy.data.collections.get(obj.name)
@@ -2052,23 +1990,6 @@ class IfcImporter:
):
return representation.Items[0].MappingTarget
def get_geometry_type(self, element):
tree = []
if hasattr(element, "Representation"):
tree = self.file.traverse(element.Representation)
elif hasattr(element, "RepresentationMaps"):
for representation_map in element.RepresentationMaps:
tree.extend(self.file.traverse(representation_map))
representations = [
e
for e in tree
if e.is_a("IfcRepresentation")
and e.RepresentationIdentifier == "Body"
and e.RepresentationType != "MappedRepresentation"
]
for representation in representations:
return representation.Items[0].is_a()
def create_mesh(self, element, shape, is_curve=False):
try:
if hasattr(shape, "geometry"):
@@ -2079,7 +2000,13 @@ class IfcImporter:
if is_curve:
return self.create_curve(geometry)
mesh = bpy.data.meshes.new(geometry.id)
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
mesh = bpy.data.meshes.new("{}/{}".format(
self.file.by_id(representation_id).ContextOfItems.id(), geometry.id))
if geometry.faces:
num_vertices = len(geometry.verts) // 3
@@ -2120,7 +2047,6 @@ class IfcImporter:
ios_materials.append(mat.name)
mesh["ios_materials"] = ios_materials
mesh["ios_material_ids"] = geometry.material_ids
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
return mesh
except:
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
File diff suppressed because it is too large Load Diff
+118 -119
View File
@@ -1,7 +1,6 @@
import json
import os
import ifcopenshell
import ifcopenshell.util.pset
from pathlib import Path
from . import export_ifc
from . import schema
@@ -9,6 +8,7 @@ from . import ifc
from . import annotation
from . import decoration
import bpy
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
from bpy.app.handlers import persistent
from bpy.props import (
@@ -40,35 +40,19 @@ psettemplatefiles_enum = []
propertysettemplates_enum = []
classification_enum = []
attributes_enum = []
psetnames_enum = []
psetnames = {}
qtonames_enum = []
materialattributes_enum = []
materialtypes_enum = []
contexts_enum = []
subcontexts_enum = []
target_views_enum = []
persons_enum = []
organisations_enum = []
sheets_enum = []
vector_styles_enum = []
@persistent
def setDefaultProperties(scene):
if (
bpy.context.scene.BIMProperties.has_model_context
and len(bpy.context.scene.BIMProperties.model_subcontexts) == 0
):
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Body"
subcontext.target_view = "MODEL_VIEW"
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Box"
subcontext.target_view = "MODEL_VIEW"
if bpy.context.scene.BIMProperties.has_plan_context and len(bpy.context.scene.BIMProperties.plan_subcontexts) == 0:
subcontext = bpy.context.scene.BIMProperties.plan_subcontexts.add()
subcontext.name = "Annotation"
subcontext.target_view = "PLAN_VIEW"
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
@@ -140,14 +124,13 @@ def setDefaultProperties(scene):
def getIfcPredefinedTypes(self, context):
global types_enum
if len(types_enum) < 1:
for name, data in schema.ifc.elements.items():
if name != self.ifc_class.strip():
continue
for attribute in data["attributes"]:
if attribute["name"] != "PredefinedType":
continue
types_enum.extend([(e, e, "") for e in attribute["enum_values"]])
file = IfcStore.get_file()
if len(types_enum) < 1 and file:
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
for attribute in declaration.attributes():
if attribute.name() == "PredefinedType":
types_enum.extend([(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()])
break
return types_enum
@@ -263,6 +246,7 @@ def refreshActiveDrawingIndex(self, context):
def getIfcProducts(self, context):
global products_enum
file = IfcStore.get_file()
if len(products_enum) < 1:
products_enum.extend(
[
@@ -272,23 +256,38 @@ def getIfcProducts(self, context):
"IfcElementType",
"IfcSpatialElement",
"IfcGroup",
"IfcStructural",
"IfcPositioningElement",
"IfcStructuralItem",
"IfcContext",
"IfcAnnotation",
]
]
)
if file.schema == "IFC2X3":
products_enum[2] = ("IfcSpatialStructureElement", "IfcSpatialStructureElement", "")
return products_enum
def getIfcClasses(self, context):
global classes_enum
if len(classes_enum) < 1:
classes_enum.extend([(e, e, "") for e in getattr(schema.ifc, self.ifc_product)])
file = IfcStore.get_file()
if len(classes_enum) < 1 and file:
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_product)
def get_classes(declaration):
results = []
if not declaration.is_abstract():
results.append(declaration.name())
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
classes = get_classes(declaration)
classes_enum.extend([(c, c, "") for c in sorted(classes)])
return classes_enum
def getAttributeEnumValues(self, context):
return [(e, e, "") for e in json.loads(self.enum_items)]
def getProfileDef(self, context):
global profiledef_enum
if len(profiledef_enum) < 1:
@@ -297,17 +296,27 @@ def getProfileDef(self, context):
def getPersons(self, context):
global persons_enum
persons_enum.clear()
persons_enum.extend([(p.name, p.name, "") for p in bpy.context.scene.BIMProperties.people])
return persons_enum
from blenderbim.bim.module.owner.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, person in Data.people.items():
if "Id" in person:
identifier = person["Id"] or ""
else:
identifier = person["Identifier"] or ""
results.append((str(ifc_id), identifier, ""))
return results
def getOrganisations(self, context):
global organisations_enum
organisations_enum.clear()
organisations_enum.extend([(o.name, o.name, "") for o in bpy.context.scene.BIMProperties.organisations])
return organisations_enum
from blenderbim.bim.module.owner.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, organisation in Data.organisations.items():
results.append((str(ifc_id), organisation["Name"], ""))
return results
def getIfcPatchRecipes(self, context):
@@ -359,11 +368,18 @@ def refreshTitleblocks(self, context):
def toggleDecorations(self, context):
toggle = self.should_draw_decorations
if toggle:
for dec in decoration.all_decorators:
dec.install(self, context)
decoration.DecorationsHandler.install(context)
else:
for dec in decoration.all_decorators:
dec.uninstall()
decoration.DecorationsHandler.uninstall()
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
def getScenarios(self, context):
@@ -430,22 +446,20 @@ def refreshReferences(self, context):
def getPsetNames(self, context):
global psetnames_enum
psetnames_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_pset=True
)
psetnames_enum.extend([(p, p, "") for p in pset_names])
return psetnames_enum
global psetnames
if "/" in context.active_object.name:
ifc_class = context.active_object.name.split("/")[0]
if ifc_class not in psetnames:
psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
return psetnames[ifc_class]
return []
def getMaterialPsetNames(self, context):
global materialpsetnames_enum
materialpsetnames_enum.clear()
pset_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, "IfcMaterial", is_pset=True
)
pset_names = schema.ifc.psetqto.get_applicable_names("IfcMaterial", pset_only=True)
materialpsetnames_enum.extend([(p, p, "") for p in pset_names])
return materialpsetnames_enum
@@ -454,27 +468,11 @@ def getQtoNames(self, context):
global qtonames_enum
qtonames_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
qto_names = ifcopenshell.util.pset.get_applicable_psetqtos(
bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_qto=True
)
qto_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], qto_only=True)
qtonames_enum.extend([(q, q, "") for q in qto_names])
return qtonames_enum
def getApplicableAttributes(self, context):
global attributes_enum
attributes_enum.clear()
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
attributes_enum.extend(
[
(a["name"], a["name"], "")
for a in schema.ifc.elements[context.active_object.name.split("/")[0]]["attributes"]
if self.attributes.find(a["name"]) == -1
]
)
return attributes_enum
def getApplicableMaterialAttributes(self, context):
global materialattributes_enum
materialattributes_enum.clear()
@@ -508,6 +506,19 @@ def getMaterialTypes(self, context):
return materialtypes_enum
def getContexts(self, context):
from blenderbim.bim.module.context.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, context in Data.contexts.items():
results.append((str(ifc_id), context["ContextType"], ""))
for ifc_id2, subcontext in context["HasSubContexts"].items():
results.append((str(ifc_id2), "{}/{}/{}".format(
subcontext["ContextType"], subcontext["ContextIdentifier"], subcontext["TargetView"]), ""))
return results
def getSubcontexts(self, context):
global subcontexts_enum
subcontexts_enum.clear()
@@ -556,19 +567,9 @@ class Attribute(PropertyGroup):
bool_value: BoolProperty(name="Value")
int_value: IntProperty(name="Value")
float_value: FloatProperty(name="Value")
class Subcontext(PropertyGroup):
name: StringProperty(name="Name")
context: StringProperty(name="Context")
target_view: StringProperty(name="Target View")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class Representation(PropertyGroup):
name: StringProperty(name="Name")
type: StringProperty(name="Type")
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_null: BoolProperty(name="Is Null")
enum_items: StringProperty(name="Value")
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value")
class MaterialLayer(PropertyGroup):
@@ -685,8 +686,7 @@ class DocProperties(PropertyGroup):
drawing_styles: CollectionProperty(name="Drawing Styles", type=DrawingStyle)
should_draw_decorations: BoolProperty(name="Should Draw Decorations", update=toggleDecorations)
decorations_colour: FloatVectorProperty(name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1),
min=0.0, max=1.0, size=4,
update=toggleDecorations)
min=0.0, max=1.0, size=4)
class BIMCameraProperties(PropertyGroup):
@@ -1097,6 +1097,7 @@ class Address(PropertyGroup):
name: StringProperty(name="Name", default="IfcPostalAddress") # Stores IfcPostalAddress or IfcTelecomAddress
purpose: EnumProperty(
items=[
("None", "None", ""),
("OFFICE", "OFFICE", "An office address."),
("SITE", "SITE", "A site address."),
("HOME", "HOME", "A home address."),
@@ -1117,7 +1118,7 @@ class Address(PropertyGroup):
country: StringProperty(name="Country")
telephone_numbers: StringProperty(name="Telephone Numbers")
fascimile_numbers: StringProperty(name="Fascimile Numbers")
facsimile_numbers: StringProperty(name="Facsimile Numbers")
pager_number: StringProperty(name="Pager Number")
electronic_mail_addresses: StringProperty(name="Emails")
www_home_page_url: StringProperty(name="Websites")
@@ -1158,12 +1159,9 @@ class Role(PropertyGroup):
class Organisation(PropertyGroup):
identification: StringProperty(name="Identification")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
roles: CollectionProperty(name="Roles", type=Role)
active_role_index: bpy.props.IntProperty()
addresses: CollectionProperty(name="Addresses", type=Address)
active_address_index: bpy.props.IntProperty()
class Person(PropertyGroup):
@@ -1173,10 +1171,6 @@ class Person(PropertyGroup):
middle_names: StringProperty(name="Middle Names")
prefix_titles: StringProperty(name="Prefixes")
suffix_titles: StringProperty(name="Suffixes")
roles: CollectionProperty(name="Roles", type=Role)
active_role_index: bpy.props.IntProperty()
addresses: CollectionProperty(name="Addresses", type=Address)
active_address_index: bpy.props.IntProperty()
class Classification(PropertyGroup):
@@ -1269,7 +1263,7 @@ class BIMProperties(PropertyGroup):
)
export_should_force_faceted_brep: BoolProperty(name="Export with Faceted Breps", default=False)
export_should_force_triangulation: BoolProperty(name="Export with Triangulation", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=True)
import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False)
import_should_ignore_building_coordinates: BoolProperty(name="Import Ignoring Building Coordinates", default=False)
import_should_reset_absolute_coordinates: BoolProperty(name="Import Resetting Absolute Coordinates", default=False)
@@ -1281,7 +1275,7 @@ class BIMProperties(PropertyGroup):
import_should_auto_set_workarounds: BoolProperty(name="Automatically Set Vendor Workarounds", default=True)
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
import_should_import_native: BoolProperty(name="Import Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=True)
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
import_should_import_with_profiling: BoolProperty(name="Import with Profiling", default=True)
import_should_import_aggregates: BoolProperty(name="Import Aggregates", default=True)
@@ -1296,12 +1290,18 @@ class BIMProperties(PropertyGroup):
import_should_offset_model: BoolProperty(name="Import and Offset Model", default=False)
import_model_offset_coordinates: StringProperty(name="Model Offset Coordinates", default="0,0,0")
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
person: EnumProperty(items=getPersons, name="Person")
organisation: EnumProperty(items=getOrganisations, name="Organisation")
people: CollectionProperty(name="People", type=Person)
organisations: CollectionProperty(name="Organisations", type=Organisation)
active_person_index: IntProperty(name="Active Person Index")
active_organisation_index: IntProperty(name="Active Organisation Index")
person: PointerProperty(type=Person)
active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation)
active_organisation_id: IntProperty(name="Active Organisation Id")
role: PointerProperty(type=Role)
active_role_id: IntProperty(name="Active Role Id")
address: PointerProperty(type=Address)
active_address_id: IntProperty(name="Active Address Id")
user_person: EnumProperty(items=getPersons, name="Person")
user_organisation: EnumProperty(items=getOrganisations, name="Organisation")
has_georeferencing: BoolProperty(name="Has Georeferencing", default=False)
has_library: BoolProperty(name="Has Project Library", default=False)
search_regex: BoolProperty(name="Search With Regex", default=False)
@@ -1328,10 +1328,7 @@ class BIMProperties(PropertyGroup):
classification: EnumProperty(items=getClassifications, name="Classification", update=refreshReferences)
active_classification_name: StringProperty(name="Active Classification Name")
classifications: CollectionProperty(name="Classifications", type=Classification)
has_model_context: BoolProperty(name="Has Model Context", default=True)
has_plan_context: BoolProperty(name="Has Plan Context", default=True)
model_subcontexts: CollectionProperty(name="Model Subcontexts", type=Subcontext)
plan_subcontexts: CollectionProperty(name="Plan Subcontexts", type=Subcontext)
contexts: EnumProperty(items=getContexts, name="Contexts")
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")
available_subcontexts: EnumProperty(items=getSubcontexts, name="Available Subcontexts")
available_target_views: EnumProperty(items=getTargetViews, name="Available Target Views")
@@ -1487,14 +1484,23 @@ class BoundaryCondition(PropertyGroup):
class BIMObjectProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
is_editing_aggregate: BoolProperty(name="Is Editing Aggregate")
is_editing_container: BoolProperty(name="Is Editing Container")
relating_type: PointerProperty(name="Type", type=bpy.types.Object)
is_editing_type: BoolProperty(name="Is Editing Type")
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
active_pset_id: IntProperty(name="Active Pset ID")
active_pset_name: StringProperty(name="Pset Name")
properties: CollectionProperty(name="Properties", type=Attribute)
psets: CollectionProperty(name="Psets", type=PsetQto)
qtos: CollectionProperty(name="Qtos", type=PsetQto)
applicable_attributes: EnumProperty(items=getApplicableAttributes, name="Attribute Names")
document_references: CollectionProperty(name="Document References", type=DocumentReference)
active_document_reference_index: IntProperty(name="Active Document Reference Index")
constraints: CollectionProperty(name="Constraints", type=Constraint)
@@ -1508,20 +1514,10 @@ class BIMObjectProperties(PropertyGroup):
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
representations: CollectionProperty(name="Representations", type=Representation)
# Address applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress
address: PointerProperty(name="Address", type=Address)
class BIMDebugProperties(PropertyGroup):
step_id: IntProperty(name="STEP ID")
number_of_polygons: IntProperty(name="Number of Polygons")
active_step_id: IntProperty(name="STEP ID")
step_id_breadcrumb: CollectionProperty(name="STEP ID Breadcrumb", type=StrProperty)
attributes: CollectionProperty(name="Attributes", type=Attribute)
inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute)
class BIMMaterialProperties(PropertyGroup):
is_external: BoolProperty(name="Has External Definition")
location: StringProperty(name="Location")
@@ -1531,6 +1527,9 @@ class BIMMaterialProperties(PropertyGroup):
psets: CollectionProperty(name="Psets", type=PsetQto)
attributes: CollectionProperty(name="Attributes", type=Attribute)
applicable_attributes: EnumProperty(items=getApplicableMaterialAttributes, name="Attribute Names")
ifc_definition_id: IntProperty(name="IFC Definition ID")
# In Blender, a material object can map to an IFC material, IFC surface style, or both
ifc_style_id: IntProperty(name="IFC Style ID")
class SweptSolid(PropertyGroup):
@@ -1551,13 +1550,13 @@ class ItemSlotMap(PropertyGroup):
class BIMMeshProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_native: BoolProperty(name="Is Native", default=False)
is_swept_solid: BoolProperty(name="Is Swept Solid")
swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid)
is_parametric: BoolProperty(name="Is Parametric", default=False)
geometry_type: StringProperty(name="Geometry Type")
ifc_definition: StringProperty(name="IFC Definition")
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
active_representation_item_index: IntProperty(name="Active Representation Item Index")
presentation_layer_index: IntProperty(name="Presentation Layer Index", default=-1)
ifc_item_ids: CollectionProperty(name="IFC Definition ID", type=ItemSlotMap)
ifc_item_ids: CollectionProperty(name="IFC Item IDs", type=ItemSlotMap)
@@ -10,11 +10,10 @@ cwd = os.path.dirname(os.path.realpath(__file__))
class IfcSchema:
def __init__(self):
self.schema_dir = os.path.join(cwd, "schema") # TODO: make configurable
self.data_dir = os.path.join(cwd, "data") # TODO: make configurable
self.schema_dir = Path(cwd).joinpath("schema") # TODO: make configurable
self.data_dir = Path(cwd).joinpath("data") # TODO: make configurable
# TODO: Make it less troublesome
self.products = [
"IfcContext",
"IfcElement",
"IfcSpatialElement",
"IfcGroup",
@@ -29,10 +28,11 @@ class IfcSchema:
self.elements = {}
self.property_files = []
property_paths = Path(os.path.join(self.data_dir, "pset")).glob("*.ifc")
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
# TODO: add IFC2X3 PsetQto template support
self.psetqto = ifcopenshell.util.pset.PsetQto("IFC4")
for path in property_paths:
ifcopenshell.util.pset.load_property_set_template(path)
ifcopenshell.util.pset.load_property_set_template(os.path.join(self.schema_dir, "Pset_IFC4_ADD2.ifc"))
self.psetqto.templates.append(ifcopenshell.open(path))
self.classification_files = {}
self.classifications = {}
@@ -1,114 +0,0 @@
{
"IfcProject": {
"is_abstract": false,
"parent": "IfcContext",
"attributes": [
{
"name": "GlobalId",
"type": "IfcGloballyUniqueId",
"is_enum": false,
"enum_values": []
},
{
"name": "Name",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Description",
"type": "IfcText",
"is_enum": false,
"enum_values": []
},
{
"name": "ObjectType",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "LongName",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Phase",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
}
],
"complex_attributes": [
{
"name": "OwnerHistory",
"type": "IfcOwnerHistory",
"is_select": false,
"select_types": []
},
{
"name": "UnitsInContext",
"type": "IfcUnitAssignment",
"is_select": false,
"select_types": []
}
]
},
"IfcProjectLibrary": {
"is_abstract": false,
"parent": "IfcContext",
"attributes": [
{
"name": "GlobalId",
"type": "IfcGloballyUniqueId",
"is_enum": false,
"enum_values": []
},
{
"name": "Name",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Description",
"type": "IfcText",
"is_enum": false,
"enum_values": []
},
{
"name": "ObjectType",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "LongName",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
},
{
"name": "Phase",
"type": "IfcLabel",
"is_enum": false,
"enum_values": []
}
],
"complex_attributes": [
{
"name": "OwnerHistory",
"type": "IfcOwnerHistory",
"is_select": false,
"select_types": []
},
{
"name": "UnitsInContext",
"type": "IfcUnitAssignment",
"is_select": false,
"select_types": []
}
]
}
}
File diff suppressed because it is too large Load Diff
+1 -561
View File
@@ -5,106 +5,6 @@ from bpy.types import Panel
from bpy.props import StringProperty
class BIM_PT_object(Panel):
bl_label = "IFC Object"
bl_idname = "BIM_PT_object"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
bim_properties = context.scene.BIMProperties
if props.is_reassigning_class or "/" not in context.active_object.name:
row = layout.row()
row.prop(bim_properties, "ifc_product")
row = layout.row()
row.prop(bim_properties, "ifc_class")
if bim_properties.ifc_predefined_type:
row = layout.row()
row.prop(bim_properties, "ifc_predefined_type")
if bim_properties.ifc_predefined_type == "USERDEFINED":
row = layout.row()
row.prop(bim_properties, "ifc_userdefined_type")
row = layout.row(align=True)
if "Ifc" not in context.active_object.name:
op = row.operator("bim.assign_class")
else:
op = row.operator("bim.assign_class")
op.object_name = context.active_object.name
op = row.operator("bim.unassign_class", icon="X", text="")
op.object_name = context.active_object.name
else:
row = layout.row()
row.operator("bim.reassign_class", text="Reassign IFC Class")
if "Ifc" not in context.active_object.name:
return
layout.label(text="Attributes:")
row = layout.row(align=True)
row.prop(props, "applicable_attributes", text="")
row.operator("bim.add_attribute")
for index, attribute in enumerate(props.attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.name == "GlobalId":
row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="")
op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="")
op.attribute_name = attribute.name
op.attribute_value = attribute.string_value
row.operator("bim.remove_attribute", icon="X", text="").attribute_index = index
row = layout.row()
row.prop(props, "attributes")
if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
self.draw_addresses_ui()
row = layout.row(align=True)
row.prop(props, "relating_type")
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
row = layout.row()
row.prop(props, "relating_structure")
def draw_addresses_ui(self):
layout = self.layout
layout.label(text="Address:")
address = bpy.context.active_object.BIMObjectProperties.address
row = layout.row()
row.prop(address, "purpose")
if address.purpose == "USERDEFINED":
row = layout.row()
row.prop(address, "user_defined_purpose")
row = layout.row()
row.prop(address, "description")
row = layout.row()
row.prop(address, "internal_location")
row = layout.row()
row.prop(address, "address_lines")
row = layout.row()
row.prop(address, "postal_box")
row = layout.row()
row.prop(address, "town")
row = layout.row()
row.prop(address, "region")
row = layout.row()
row.prop(address, "postal_code")
row = layout.row()
row.prop(address, "country")
class BIM_PT_object_material(Panel):
bl_label = "IFC Object Material"
bl_idname = "BIM_PT_object_material"
@@ -243,73 +143,6 @@ class BIM_PT_object_material(Panel):
row.prop(attribute, "string_value", text="")
class BIM_PT_object_psets(Panel):
bl_label = "IFC Object Property Sets"
bl_idname = "BIM_PT_object_psets"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
row = layout.row(align=True)
row.prop(props, "pset_name", text="")
row.operator("bim.add_pset")
self.draw_psets_ui(props)
if props.relating_type and props.relating_type.BIMObjectProperties.psets:
layout.label(text="Inherited Psets:")
self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False)
def draw_psets_ui(self, props, enabled=True):
for index, pset in enumerate(props.psets):
box = self.layout.box()
row = box.row(align=True)
row.prop(
pset,
"is_expanded",
icon="TRIA_DOWN" if pset.is_expanded else "TRIA_RIGHT",
icon_only=True,
emboss=False,
)
row2 = row.row(align=True)
row2.enabled = enabled
row2.prop(pset, "name", text="", icon="COPY_ID", emboss=pset.is_editable)
if enabled:
row.prop(pset, "is_editable", icon="CHECKMARK" if pset.is_editable else "GREASEPENCIL", icon_only=True)
row.operator("bim.remove_pset", icon="X", text="").pset_index = index
if not pset.is_expanded:
continue
if pset.is_editable:
for prop in pset.properties:
row = box.row(align=True)
row.enabled = enabled
row.prop(prop, "name", text="")
row.prop(prop, "string_value", text="")
if not enabled:
continue
op = row.operator("bim.copy_property_to_selection", icon="COPYDOWN", text="")
op.pset_name = pset.name
op.prop_name = prop.name
op.prop_value = prop.string_value
else:
col = box.column(align=True)
for prop in pset.properties:
if not prop.string_value:
continue
col.enabled = enabled
col.prop(prop, "string_value", text=prop.name)
class BIM_PT_object_qto(Panel):
bl_label = "IFC Object Quantity Sets"
bl_idname = "BIM_PT_object_qto"
@@ -626,48 +459,6 @@ class BIM_PT_constraint_relations(Panel):
layout.label(text="Constraint is invalid")
class BIM_PT_representations(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_representations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
layout = self.layout
props = context.active_object.BIMObjectProperties
if not props.representations:
layout.label(text="No representations found")
row = layout.row(align=True)
row.prop(bpy.context.scene.BIMProperties, "available_contexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_subcontexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_target_views", text="")
# TODO: reimplement with incremental editing
# op = row.operator("bim.switch_context", icon="ADD", text="")
# op.has_target_context = False
self.file = ifc.IfcStore.get_file()
for index, representation in enumerate(props.representations):
row = layout.row(align=True)
row.prop(representation, "name", text="")
row.prop(representation, "type", text="")
if not representation.ifc_definition_id:
continue
subcontext = self.file.by_id(representation.ifc_definition_id).ContextOfItems
if subcontext.is_a() == "IfcGeometricRepresentationContext":
continue
# TODO: reimplement with incremental editing
# op = row.operator("bim.switch_context", icon="OUTLINER_DATA_MESH", text="")
# op.has_target_context = True
# op.context_name = subcontext.ContextType
# op.subcontext_name = subcontext.ContextIdentifier
# op.target_view_name = subcontext.TargetView
# row.operator("bim.remove_context", icon="X", text="").index = index
class BIM_PT_classification_references(Panel):
bl_label = "IFC Classification References"
bl_idname = "BIM_PT_classification_references"
@@ -794,70 +585,6 @@ class BIM_PT_classifications(Panel):
row.prop(props, "classifications")
class BIM_PT_mesh(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_mesh"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "data"
@classmethod
def poll(cls, context):
return (
context.active_object is not None
and context.active_object.type == "MESH"
and hasattr(context.active_object.data, "BIMMeshProperties")
)
def draw(self, context):
if not context.active_object.data:
return
layout = self.layout
props = context.active_object.data.BIMMeshProperties
row = layout.row(align=True)
row.operator("bim.push_representation")
row = layout.row()
row.prop(props, "geometry_type")
layout.label(text="IFC Parameters:")
row = layout.row()
row.operator("bim.get_representation_ifc_parameters")
row = layout.row()
row.operator("bim.bake_parametric_geometry")
for index, ifc_parameter in enumerate(props.ifc_parameters):
row = layout.row(align=True)
row.prop(ifc_parameter, "name", text="")
row.prop(ifc_parameter, "value", text="")
row.operator("bim.update_ifc_representation", icon="FILE_REFRESH", text="").index = index
row = layout.row()
row.prop(props, "is_parametric")
row = layout.row()
row.prop(props, "is_native")
row = layout.row()
row.prop(props, "is_swept_solid")
row = layout.row()
row.operator("bim.add_swept_solid")
for index, swept_solid in enumerate(props.swept_solids):
row = layout.row(align=True)
row.prop(swept_solid, "name", text="")
row.operator("bim.remove_swept_solid", icon="X", text="").index = index
row = layout.row()
sub = row.row(align=True)
sub.operator("bim.assign_swept_solid_outer_curve").index = index
sub.operator("bim.select_swept_solid_outer_curve", icon="RESTRICT_SELECT_OFF", text="").index = index
sub = row.row(align=True)
sub.operator("bim.add_swept_solid_inner_curve").index = index
sub.operator("bim.select_swept_solid_inner_curves", icon="RESTRICT_SELECT_OFF", text="").index = index
row = layout.row(align=True)
row.operator("bim.assign_swept_solid_extrusion").index = index
row.operator("bim.select_swept_solid_extrusion", icon="RESTRICT_SELECT_OFF", text="").index = index
row = layout.row()
row.prop(props, "swept_solids")
class BIM_PT_presentation_layer_data(Panel):
bl_label = "IFC Presentation Layers"
bl_idname = "BIM_PT_presentation"
@@ -1303,213 +1030,6 @@ class BIM_PT_text(Panel):
row.prop(variable, "prop_key")
class BIM_PT_owner(Panel):
bl_label = "IFC Owner History"
bl_idname = "BIM_PT_owner"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
props = scene.BIMProperties
if not props.person:
layout.label(text="No people found.")
else:
row = layout.row()
row.prop(props, "person")
if not props.organisation:
layout.label(text="No organisations found.")
else:
row = layout.row()
row.prop(props, "organisation")
class BIM_PT_people(Panel):
bl_label = "IFC People"
bl_idname = "BIM_PT_people"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
props = context.scene.BIMProperties
row = layout.row()
row.operator("bim.add_person")
if props.people:
layout.template_list("BIM_UL_generic", "", props, "people", props, "active_person_index")
if props.active_person_index < len(props.people):
person = props.people[props.active_person_index]
row = layout.row()
row.prop(person, "name")
row.operator("bim.remove_person", icon="X", text="").index = props.active_person_index
row = layout.row()
row.prop(person, "family_name")
row = layout.row()
row.prop(person, "given_name")
row = layout.row()
row.prop(person, "middle_names")
row = layout.row()
row.prop(person, "prefix_titles")
row = layout.row()
row.prop(person, "suffix_titles")
layout.label(text="Roles:")
draw_roles_ui(layout, person, "person")
layout.label(text="Addresses:")
draw_addresses_ui(layout, person, "person")
class BIM_PT_organisations(Panel):
bl_label = "IFC Organisations"
bl_idname = "BIM_PT_organisations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
props = context.scene.BIMProperties
row = layout.row()
row.operator("bim.add_organisation")
if props.organisations:
layout.template_list("BIM_UL_generic", "", props, "organisations", props, "active_organisation_index")
if props.active_organisation_index < len(props.organisations):
organisation = props.organisations[props.active_organisation_index]
row = layout.row()
row.prop(organisation, "name")
row.operator("bim.remove_organisation", icon="X", text="").index = props.active_organisation_index
row = layout.row()
row.prop(organisation, "description")
layout.label(text="Roles:")
draw_roles_ui(layout, organisation, "organisation")
layout.label(text="Addresses:")
draw_addresses_ui(layout, organisation, "organisation")
def draw_roles_ui(layout, parent, parent_type):
row = layout.row()
row.operator(f"bim.add_{parent_type}_role")
if parent.roles:
layout.template_list("BIM_UL_generic", "", parent, "roles", parent, "active_role_index")
if parent.active_role_index < len(parent.roles):
role = parent.roles[parent.active_role_index]
row = layout.row()
row.prop(role, "name")
row.operator(f"bim.remove_{parent_type}_role", icon="X", text="").index = parent.active_role_index
if role.name == "USERDEFINED":
row = layout.row()
row.prop(role, "user_defined_role")
row = layout.row()
row.prop(role, "description")
def draw_addresses_ui(layout, parent, parent_type):
row = layout.row()
row.operator(f"bim.add_{parent_type}_address")
if parent.addresses:
layout.template_list("BIM_UL_generic", "", parent, "addresses", parent, "active_address_index")
if parent.active_address_index < len(parent.addresses):
address = parent.addresses[parent.active_address_index]
row = layout.row()
row.prop(address, "name")
row.operator(f"bim.remove_{parent_type}_address", icon="X", text="").index = parent.active_address_index
row = layout.row()
row.prop(address, "purpose")
if address.purpose == "USERDEFINED":
row = layout.row()
row.prop(address, "user_defined_purpose")
row = layout.row()
row.prop(address, "description")
if "IfcPostalAddress" in address.name:
row = layout.row()
row.prop(address, "internal_location")
row = layout.row()
row.prop(address, "address_lines")
row = layout.row()
row.prop(address, "postal_box")
row = layout.row()
row.prop(address, "town")
row = layout.row()
row.prop(address, "region")
row = layout.row()
row.prop(address, "postal_code")
row = layout.row()
row.prop(address, "country")
elif "IfcTelecomAddress" in address.name:
row = layout.row()
row.prop(address, "telephone_numbers")
row = layout.row()
row.prop(address, "fascimile_numbers")
row = layout.row()
row.prop(address, "pager_number")
row = layout.row()
row.prop(address, "electronic_mail_addresses")
row = layout.row()
row.prop(address, "www_home_page_url")
row = layout.row()
row.prop(address, "messaging_ids")
class BIM_PT_context(Panel):
bl_label = "IFC Geometric Representation Contexts"
bl_idname = "BIM_PT_context"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.BIMProperties
for context in ["model", "plan"]:
row = layout.row(align=True)
row.prop(props, f"has_{context}_context")
if not getattr(props, f"has_{context}_context"):
continue
layout.label(text="Geometric Representation Subcontexts:")
row = layout.row(align=True)
row.prop(props, "available_subcontexts", text="")
row.prop(props, "available_target_views", text="")
row.operator("bim.add_subcontext", icon="ADD", text="").context = context
for subcontext_index, subcontext in enumerate(getattr(props, "{}_subcontexts".format(context))):
row = layout.row(align=True)
row.prop(subcontext, "name", text="")
row.prop(subcontext, "target_view", text="")
row.operator("bim.remove_subcontext", icon="X", text="").indexes = "{}-{}".format(
context, subcontext_index
)
class BIM_PT_bim(Panel):
bl_label = "Building Information Modeling"
bl_idname = "BIM_PT_bim"
@@ -1526,9 +1046,6 @@ class BIM_PT_bim(Panel):
layout.label(text="System Setup:")
row = layout.row()
row.operator("bim.quick_project_setup")
row = layout.row(align=True)
row.prop(bim_properties, "schema_dir")
row.operator("bim.select_schema_dir", icon="FILE_FOLDER", text="")
@@ -1548,22 +1065,6 @@ class BIM_PT_bim(Panel):
layout.label(text="IFC Categorisation:")
row = layout.row()
row.prop(bim_properties, "ifc_product")
row = layout.row()
row.prop(bim_properties, "ifc_class")
if bim_properties.ifc_predefined_type:
row = layout.row()
row.prop(bim_properties, "ifc_predefined_type")
if bim_properties.ifc_predefined_type == "USERDEFINED":
row = layout.row()
row.prop(bim_properties, "ifc_userdefined_type")
row = layout.row(align=True)
op = row.operator("bim.assign_class")
op.object_name = ""
op = row.operator("bim.unassign_class", icon="X", text="")
op.object_name = ""
row = layout.row(align=True)
row.operator("bim.select_class")
row.operator("bim.select_type")
@@ -1850,6 +1351,7 @@ class BIM_PT_patch(Panel):
class BIM_PT_mvd(Panel):
bl_label = "Model View Definitions (MVD)"
bl_idname = "BIM_PT_mvd"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@@ -2360,68 +1862,6 @@ class BIM_PT_misc_utilities(Panel):
row.operator("bim.snap_spaces_together")
class BIM_PT_debug(Panel):
bl_label = "IFC Debug"
bl_idname = "BIM_PT_debug"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.BIMDebugProperties
row = layout.row()
row.operator("bim.profile_import_ifc")
row = layout.row()
row.prop(props, "step_id", text="")
row = layout.row()
row.operator("bim.create_shape_from_step_id")
row = layout.row()
row.prop(props, "number_of_polygons", text="")
row = layout.row()
row.operator("bim.select_high_polygon_meshes")
layout.label(text="Inspector:")
row = layout.row(align=True)
if len(props.step_id_breadcrumb) >= 2:
row.operator("bim.rewind_inspector", icon="FRAME_PREV", text="")
row.prop(props, "active_step_id", text="")
row = layout.row(align=True)
row.operator("bim.inspect_from_step_id").step_id = bpy.context.scene.BIMDebugProperties.active_step_id
row.operator("bim.inspect_from_object")
if props.attributes:
layout.label(text="Direct attributes:")
for index, attribute in enumerate(props.attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.int_value:
row.operator(
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
).step_id = attribute.int_value
if props.inverse_attributes:
layout.label(text="Inverse attributes:")
for index, attribute in enumerate(props.inverse_attributes):
row = layout.row(align=True)
row.prop(attribute, "name", text="")
row.prop(attribute, "string_value", text="")
if attribute.int_value:
row.operator(
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
).step_id = attribute.int_value
def ifc_units(self, context):
scene = context.scene
props = context.scene.BIMProperties