Merge branch 'v0.7.0' into pset_select_same_value

This commit is contained in:
Dion Moult
2023-07-08 20:05:13 +10:00
committed by GitHub
67 changed files with 5504 additions and 5007 deletions
@@ -106,6 +106,7 @@ classes = [
prop.ObjProperty,
prop.Attribute,
prop.ModuleVisibility,
prop.BIMAreaProperties,
prop.BIMProperties,
prop.IfcParameter,
prop.PsetQto,
@@ -119,6 +120,7 @@ classes = [
ui.BIM_UL_topics,
ui.BIM_ADDON_preferences,
# Scene panel groups
ui.BIM_PT_root,
ui.BIM_PT_project_info,
ui.BIM_PT_project_setup,
ui.BIM_PT_collaboration,
@@ -159,6 +161,7 @@ def register():
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_post.append(handler.ensureIfcExported)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties)
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
@@ -389,7 +389,7 @@
"scene.display.shading.studiolight_background_blur": 0.0,
"scene.display.shading.studiolight_intensity": 0.0,
"scene.display.shading.studiolight_rotate_z": 0.0,
"scene.display.shading.type": "SOLID",
"scene.display.shading.type": "RENDERED",
"scene.display.shading.use_compositor": "DISABLED",
"scene.display.shading.use_dof": false,
"scene.display.shading.use_scene_lights": false,
@@ -640,7 +640,7 @@
"scene.display.shading.studiolight_background_blur": 0.0,
"scene.display.shading.studiolight_intensity": 0.0,
"scene.display.shading.studiolight_rotate_z": 0.0,
"scene.display.shading.type": "SOLID",
"scene.display.shading.type": "RENDERED",
"scene.display.shading.use_compositor": "DISABLED",
"scene.display.shading.use_dof": false,
"scene.display.shading.use_scene_lights": false,
@@ -752,4 +752,4 @@
"space.overlay.xray_alpha_bone": 0.0
}
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1,2 +0,0 @@
Name,foo
TransomThickness,500
1 Name foo
2 TransomThickness 500
Binary file not shown.
+35
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import json
import addon_utils
@@ -30,6 +31,7 @@ from mathutils import Vector
from math import cos, degrees
cwd = os.path.dirname(os.path.realpath(__file__))
global_subscription_owner = object()
@@ -72,6 +74,9 @@ def name_callback(obj, data):
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
return
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
if element.is_a("IfcGridAxis"):
element.AxisTag = obj.name.split("/")[1]
refresh_ui_data()
if not element.is_a("IfcRoot"):
return
if obj.BIMObjectProperties.collection:
@@ -293,3 +298,33 @@ def setDefaultProperties(scene):
ifcopenshell.api.owner.settings.get_user = lambda ifc: core_owner.get_user(tool.Owner)
ifcopenshell.api.owner.settings.get_application = get_application
AuthoringData.type_thumbnails = {}
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
if "BIM" in bpy.data.workspaces:
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
else:
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
for obj in [bpy.data.objects.get("Camera"), bpy.data.objects.get("Light")]:
if obj:
bpy.data.objects.remove(obj)
for panel in [
"SCENE_PT_scene",
"SCENE_PT_unit",
"SCENE_PT_physics",
"SCENE_PT_rigid_body_world",
"SCENE_PT_audio",
"SCENE_PT_keying_sets",
"SCENE_PT_custom_props",
]:
try:
bpy.utils.unregister_class(getattr(bpy.types, panel))
except:
pass
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
for screen in bpy.data.screens:
screen.BIMAreaProperties.clear()
for i in range(20): # 20 is an arbitrary value of split areas
screen.BIMAreaProperties.add()
@@ -36,6 +36,7 @@ classes = (
operator.UndoBrick,
operator.RedoBrick,
operator.SerializeBrick,
operator.AddBrickNamespace,
prop.Brick,
prop.BIMBrickProperties,
ui.BIM_PT_brickschema,
@@ -109,9 +109,10 @@ class BrickschemaData:
if BrickStore.graph is None:
return []
results = []
filter = ["brick", "owl", "w3", "xml"]
for alias, uri in BrickStore.graph.namespaces():
# results.append((uri, f"{alias}: {uri}", ""))
results.append((uri, f"{alias}", ""))
if alias not in filter:
results.append((uri, f"{alias}: {uri}", ""))
return results
@classmethod
@@ -22,7 +22,7 @@ import blenderbim.tool as tool
import blenderbim.core.brick as core
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore
from blenderbim.tool.brick import BrickStore
class Operator:
def execute(self, context):
@@ -126,6 +126,7 @@ class AddBrick(bpy.types.Operator, Operator):
namespace=props.namespace,
brick_class=props.brick_equipment_class,
library=library,
label=props.new_brick_label
)
@@ -207,6 +208,36 @@ class RedoBrick(bpy.types.Operator, Operator):
class SerializeBrick(bpy.types.Operator, Operator):
bl_idname = "bim.serialize_brick"
bl_label = "Serialize Brick"
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
def invoke(self, context, event):
if self.should_save_as or not BrickStore.path:
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"}
else:
return self.execute(context)
def _execute(self, context):
core.serialize_brick(tool.Brick)
if self.should_save_as or not BrickStore.path:
BrickStore.path = self.filepath
core.serialize_brick(tool.Brick)
return {"FINISHED"}
@classmethod
def description(cls, context, properties):
if properties.should_save_as:
return "Save Brick project to a selected file"
return "Save the Brick project"
class AddBrickNamespace(bpy.types.Operator, Operator):
bl_idname = "bim.add_brick_namespace"
bl_label = "Add Brick Namespace"
def _execute(self, context):
props = context.scene.BIMBrickProperties
alias = props.new_brick_namespace_alias
uri = props.new_brick_namespace_uri
core.add_namespace(tool.Brick, alias=alias, uri=uri)
@@ -69,3 +69,7 @@ class BIMBrickProperties(PropertyGroup):
libraries: EnumProperty(name="Libraries", items=get_libraries)
namespace: EnumProperty(name="Namespace", items=get_namespaces)
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
brick_settings_toggled: BoolProperty(name="Brick Settings Toggled", default=False)
new_brick_label: StringProperty(name="New Brick Label")
new_brick_namespace_alias: StringProperty(name="New Brick Namespace Alias")
new_brick_namespace_uri: StringProperty(name="New Brick Namespace URI")
@@ -20,7 +20,7 @@ import blenderbim.tool as tool
from bpy.types import Panel, UIList
from blenderbim.bim.helper import prop_with_search
from blenderbim.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
from blenderbim.tool.brick import BrickStore
class BIM_PT_brickschema(Panel):
bl_label = "Brickschema Project"
@@ -30,6 +30,11 @@ class BIM_PT_brickschema(Panel):
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "OTHER"
def draw(self, context):
if not BrickschemaData.is_loaded:
BrickschemaData.load()
@@ -41,15 +46,39 @@ class BIM_PT_brickschema(Panel):
row.operator("bim.load_brick_project", text="Load Project")
return
if BrickStore.path:
row = self.layout.row(align=True)
row.label(text=BrickStore.path, icon='FILEBROWSER')
row = self.layout.row(align=True)
if len(self.props.brick_breadcrumbs):
row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
row.label(text=self.props.active_brick_class)
row.prop(data=self.props, property="brick_settings_toggled", text="", icon="PREFERENCES")
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
row.operator("bim.close_brick_project", text="", icon="CANCEL")
if self.props.brick_settings_toggled:
box = self.layout.box()
row = box.row(align=True)
row.label(text="Active Namespace:")
row = box.row(align=True)
prop_with_search(row, self.props, "namespace", text="")
row = box.row(align=True)
row.label(text="Bind New Namespace:")
row = box.row(align=True)
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
col = row.column()
col.alignment = "CENTER"
col.scale_x = 1.1
col.label(text=":")
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
row.operator("bim.add_brick_namespace", text="", icon="ADD")
row = self.layout.row(align=True)
prop_with_search(row, self.props, "namespace", text="")
row.label(text="Create Entity:")
row = self.layout.row(align=True)
row.prop(data=self.props, property="new_brick_label", text="")
prop_with_search(row, self.props, "brick_equipment_class", text="")
row.operator("bim.add_brick", text="", icon="ADD")
@@ -63,7 +92,10 @@ class BIM_PT_brickschema(Panel):
row.operator("bim.redo_brick", icon="LOOP_FORWARDS")
row = self.layout.row(align=True)
row.operator("bim.serialize_brick")
op = row.operator("bim.serialize_brick", icon="EXPORT", text="Save")
op.should_save_as = False
op = row.operator("bim.serialize_brick", icon="FILE_TICK", text="Save As")
op.should_save_as = True
self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
@@ -190,6 +190,8 @@ class CreateAllShapes(bpy.types.Operator):
total = len(elements)
settings = ifcopenshell.geom.settings()
settings_2d = ifcopenshell.geom.settings()
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
failures = []
excludes = () # For the developer to debug with
for i, element in enumerate(elements):
@@ -197,8 +199,16 @@ class CreateAllShapes(bpy.types.Operator):
continue
print(f"{i}/{total}:", element)
start = time.time()
shape = None
try:
shape = ifcopenshell.geom.create_shape(settings, element)
except:
try:
shape = ifcopenshell.geom.create_shape(settings_2d, element)
except:
failures.append(element)
print("***** FAILURE *****")
if shape:
print(
"Success",
time.time() - start,
@@ -206,9 +216,6 @@ class CreateAllShapes(bpy.types.Operator):
len(shape.geometry.edges),
len(shape.geometry.faces),
)
except:
failures.append(element)
print("***** FAILURE *****")
print(f"Failures: {len(failures)}")
for failure in failures:
print(failure)
@@ -89,7 +89,6 @@ classes = (
prop.BIMAnnotationProperties,
ui.BIM_PT_camera,
ui.BIM_PT_drawing_underlay,
ui.BIM_PT_annotation_utilities,
ui.BIM_PT_sheets,
ui.BIM_PT_drawings,
ui.BIM_PT_schedules,
@@ -300,9 +300,14 @@ class DecoratorData:
return None
dimension_style = "arrow"
fill_bg = False
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
if classes and "oblique" in classes.lower().split():
dimension_style = "oblique"
if classes:
classes_split = classes.lower().split()
if "oblique" in classes_split:
dimension_style = "oblique"
elif "fill-bg" in classes_split:
fill_bg = True
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Dimension") or {}
show_description_only = pset_data.get("ShowDescriptionOnly", False)
@@ -316,6 +321,7 @@ class DecoratorData:
"suppress_zero_inches": suppress_zero_inches,
"text_prefix": text_prefix,
"text_suffix": text_suffix,
"fill_bg": fill_bg,
}
cls.data[obj.name] = dimension_data
return dimension_data
@@ -1633,10 +1633,17 @@ class ActivateDrawingStyle(bpy.types.Operator, Operator):
space = self.get_view_3d(context) # Do not remove. It is used in exec later
style = json.loads(self.drawing_style.raster_style)
for path, value in style.items():
if isinstance(value, str):
exec(f"{path} = '{value}'")
else:
exec(f"{path} = {value}")
try:
if isinstance(value, str):
exec(f"{path} = '{value}'")
else:
exec(f"{path} = {value}")
except:
# Differences in Blender versions mean result in failures here
print("Failed to set shading style {path} to {value}")
shading_type = style.get("scene.display.shading.type", None)
if shading_type:
space.shading.type = shading_type
def set_query(self, context):
self.include_global_ids = []
@@ -828,45 +828,20 @@ class SvgWriter:
line_number = 0
for text_literal in text_literals:
# after pretty indentation some redundant spaces can occur in svg tags
# this is why we apply "font-size: 0;" to the text tag to remove those spaces
# and add clases to the tspan tags
# ref: https://github.com/IfcOpenShell/IfcOpenShell/issues/2833#issuecomment-1471584960
text = tool.Drawing.replace_text_literal_variables(text_literal.Literal, product)
attribs = {
"transform": text_transform,
"style": "font-size: 0;",
}
def add_text_tag(add_fill_bg):
nonlocal line_number
text_tag = self.svg.text(
"",
**(attribs | {"filter": "url(#fill-background)"}) if add_fill_bg else attribs,
**SvgWriter.get_box_alignment_parameters(text_literal.BoxAlignment),
)
self.svg.add(text_tag)
text_lines = text.replace("\\n", "\n").split("\n")
for text_line in text_lines:
# position has to be inserted at tspan to avoid x offset between tspans
# note that tspan doesn't support using `transform` attribute
# so we use (0,0) position because tspan is already offseted by text transform
tspan = self.svg.tspan(text_line, class_=classes_str, insert=(0, 0))
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
tspan.update({"dy": f"{line_number}em"})
text_tag.add(tspan)
line_number += 1
if add_fill_bg:
# return line_number back to the original value
line_number -= len(text_lines)
if "fill-bg" in classes:
add_text_tag(True)
add_text_tag(False)
text_tags = self.create_text_tag(
text,
text_position_svg,
angle,
text_literal.BoxAlignment,
classes_str,
multiline=True,
fill_bg="fill-bg" in classes,
line_number_start=line_number,
)
for tag in text_tags:
self.svg.add(tag)
line_number += len(tag.elements)
def draw_break_annotations(self, obj):
x_offset = self.raw_width / 2
@@ -1231,6 +1206,7 @@ class SvgWriter:
suppress_zero_inches=dimension_data["suppress_zero_inches"],
text_prefix=dimension_data["text_prefix"],
text_suffix=dimension_data["text_suffix"],
fill_bg=dimension_data["fill_bg"],
)
def draw_measureit_arch_dimension_annotations(self):
@@ -1256,6 +1232,7 @@ class SvgWriter:
suppress_zero_inches=False,
text_prefix="",
text_suffix="",
fill_bg=False,
):
offset = Vector([self.raw_width, self.raw_height]) / 2
v0 = self.project_point_onto_camera(v0_global)
@@ -1281,44 +1258,44 @@ class SvgWriter:
line = self.svg.line(start=start, end=end, class_=" ".join(classes))
self.svg.add(line)
text_tags = []
text_tag_kwargs = {
"angle": angle,
"class_str": "DIMENSION",
"text_format": text_format,
"multiline": True,
"fill_bg": fill_bg,
}
if not show_description_only:
text = f"{text_prefix}{str(dimension)}{text_suffix}"
text_tag = self.create_text_tag(
text_tags += self.create_text_tag(
text,
text_position + perpendicular,
angle,
"bottom-middle",
"DIMENSION",
text_format=text_format,
multiline=True,
box_alignment="bottom-middle",
multiline_to_bottom=False,
**text_tag_kwargs,
)
self.svg.add(text_tag)
if dimension_text:
text_tag = self.create_text_tag(
text_tags += self.create_text_tag(
dimension_text,
text_position - perpendicular,
angle,
"top-middle",
"DIMENSION",
text_format=text_format,
multiline=True,
box_alignment="top-middle",
multiline_to_bottom=True,
**text_tag_kwargs,
)
self.svg.add(text_tag)
elif show_description_only and dimension_text:
text_tag = self.create_text_tag(
text_tags.extend += self.create_text_tag(
dimension_text,
text_position + perpendicular,
angle,
"bottom-middle",
"DIMENSION",
text_format=text_format,
multiline=True,
box_alignment="bottom-middle",
multiline_to_bottom=False,
**text_tag_kwargs,
)
self.svg.add(text_tag)
for tag in text_tags:
self.svg.add(tag)
def create_text_tag(
self,
@@ -1329,33 +1306,58 @@ class SvgWriter:
class_str,
text_format=lambda x: x,
multiline=False,
multiline_to_bottom=False,
multiline_to_bottom=True,
fill_bg=False,
line_number_start=0,
_draw_fill_bg=False,
):
"""returns list of created text tags"""
text_tags = []
if fill_bg:
method_kwargs = locals() | {"_draw_fill_bg": True, "fill_bg": False}
del method_kwargs["self"]
del method_kwargs["text_tags"]
text_tags += self.create_text_tag(**method_kwargs)
base_text_attrs = SvgWriter.get_box_alignment_parameters(box_alignment)
base_text_attrs = base_text_attrs | ({"filter": "url(#fill-background)"} if _draw_fill_bg else {})
if not multiline:
text_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
return self.svg.text(
transform_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
text_tag = self.svg.text(
text_format(text),
insert=text_position,
class_=class_str,
**(text_kwargs | SvgWriter.get_box_alignment_parameters(box_alignment)),
**(transform_kwargs | base_text_attrs),
)
text_tags.append(text_tag)
return text_tags
text_position_svg_str = ", ".join(map(str, text_position))
text_transform = f"translate({text_position_svg_str}) rotate({angle})"
# after pretty indentation some redundant spaces can occur in svg tags
# this is why we apply "font-size: 0;" to the text tag to remove those spaces
# and add clases to the tspan tags
# ref: https://github.com/IfcOpenShell/IfcOpenShell/issues/2833#issuecomment-1471584960
text_kwargs = {
"transform": text_transform,
"style": "font-size: 0;",
}
text_tag = self.svg.text("", **text_kwargs, **SvgWriter.get_box_alignment_parameters(box_alignment))
text_tag = self.svg.text("", **text_kwargs, **base_text_attrs)
text_tags.append(text_tag)
text_lines = text.replace("\\n", "\n").split("\n")
text_lines = text_lines if multiline_to_bottom else text_lines[::-1]
for line_number, text_line in enumerate(text_lines):
for line_number, text_line in enumerate(text_lines, line_number_start):
# position has to be inserted at tspan to avoid x offset between tspans
# note that tspan doesn't support using `transform` attribute
# so we use (0,0) position because tspan is already offseted by text transform
tspan = self.svg.tspan(text_format(text_line), class_=class_str, insert=(0, 0))
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
tspan.update({"dy": f"{line_number if multiline_to_bottom else -line_number}em"})
text_tag.add(tspan)
return text_tag
return text_tags
def project_point_onto_camera(self, point):
# TODO is this needlessly complex?
@@ -489,87 +489,6 @@ class BIM_PT_text(Panel):
row.label(text=literal_data[attribute])
class BIM_PT_annotation_utilities(Panel):
bl_idname = "BIM_PT_annotation_utilities"
bl_label = "Annotation"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "BIM Documentation"
def draw(self, context):
layout = self.layout
self.props = context.scene.DocProperties
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Dimension", icon="FIXED_SIZE")
op.object_type = "DIMENSION"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Angle", icon="DRIVER_ROTATIONAL_DIFFERENCE")
op.object_type = "ANGLE"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Radius", icon="FORWARD")
op.object_type = "RADIUS"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Diameter", icon="ARROW_LEFTRIGHT")
op.object_type = "DIAMETER"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Text", icon="SMALL_CAPS")
op.object_type = "TEXT"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Leader", icon="TRACKING_BACKWARDS")
op.object_type = "TEXT_LEADER"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Stair Arrow", icon="SCREEN_BACK")
op.object_type = "STAIR_ARROW"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Hidden", icon="CON_TRACKTO")
op.object_type = "HIDDEN_LINE"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Level (Plan)", icon="SORTBYEXT")
op.object_type = "PLAN_LEVEL"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Level (Section)", icon="TRIA_DOWN")
op.object_type = "SECTION_LEVEL"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Breakline", icon="FCURVE")
op.object_type = "BREAKLINE"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Line", icon="MESH_MONKEY")
op.object_type = "LINEWORK"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Batting", icon="FORCE_FORCE")
op.object_type = "BATTING"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op.description = "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set"
op = row.operator("bim.add_annotation", text="Fill Area", icon="NODE_TEXTURE")
op.object_type = "FILL_AREA"
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Fall", icon="SORT_ASC")
op.object_type = "FALL"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
op = row.operator("bim.add_annotation", text="Revision", icon="VOLUME_DATA")
op.object_type = "REVISION_CLOUD"
op.data_type = tool.Drawing.get_annotation_data_type(op.object_type)
row = layout.row(align=True)
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
row.enabled = context.scene.camera is not None
class BIM_UL_drawinglist(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
@@ -187,6 +187,9 @@ class AnnotationToolUI:
selected_icon = "CHECKBOX_HLT" if cls.props.create_representation_for_type else "CHECKBOX_DEHLT"
row.prop(cls.props, "create_representation_for_type", text="", icon=selected_icon)
row = cls.layout.row(align=True)
row.prop(bpy.context.scene.DocProperties, "should_draw_decorations", text="Viewport Annotations")
@classmethod
def draw_edit_object_interface(cls, context):
if DecoratorData.get_ifc_text_data(bpy.context.object):
@@ -415,7 +415,11 @@ class OverrideDelete(bpy.types.Operator):
if self.is_batch:
ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get())
for obj in context.selected_objects:
if tool.Ifc.get_entity(obj):
element = tool.Ifc.get_entity(obj)
if element:
if ifcopenshell.util.element.get_pset(element, "BBIM_Array"):
self.report({"INFO"}, "Elements that are part of an array cannot be deleted.")
return {"FINISHED"}
tool.Geometry.delete_ifc_object(obj)
else:
bpy.data.objects.remove(obj)
@@ -33,6 +33,7 @@ classes = (
operator.ConvertGlobalToLocal,
operator.GetCursorLocation,
operator.SetCursorLocation,
operator.ConvertAngleToCoordinates,
prop.BIMGeoreferenceProperties,
ui.BIM_PT_gis,
ui.BIM_PT_gis_utilities,
@@ -171,3 +171,19 @@ class ConvertGlobalToLocal(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
core.convert_global_to_local(tool.Georeference)
class ConvertAngleToCoordinates(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.convert_angle_to_coord"
bl_label = "Convert Angle To Y Axis"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Convert angle to Y axis"
type: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
file = tool.Ifc.get()
props = context.scene.BIMGeoreferenceProperties
return file and (props.angle_degree_input_x or props.angle_degree_input_y)
def _execute(self, context):
core.convert_angle_to_coord(tool.Georeference, type=self.type)
@@ -37,6 +37,12 @@ class BIMGeoreferenceProperties(PropertyGroup):
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
coordinate_input: StringProperty(name="Coordinate Input", description='Formatted "x,y,z" (without quotes)')
coordinate_output: StringProperty(name="Coordinate Output", description='Formatted "x,y,z" (without quotes)')
angle_degree_input_x: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to Easting")
angle_degree_input_y: FloatProperty(name="Angle Degree Input", description="Angle (in degrees) rel to +Y")
x_axis_abscissa_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
x_axis_ordinate_output: StringProperty(name="X Axis Abscissa Ordinate Output", description="X axis abscissa and ordinate", )
y_axis_abscissa_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
y_axis_ordinate_output: StringProperty(name="Y Axis Abscissa Ordinate Output", description="Y axis abscissa and ordinate", )
has_blender_offset: BoolProperty(name="Has Blender Offset")
blender_eastings: StringProperty(name="Blender Eastings", default="0")
blender_northings: StringProperty(name="Blender Northings", default="0")
@@ -167,3 +167,21 @@ class BIM_PT_gis_utilities(Panel):
row = self.layout.row(align=True)
row.operator("bim.convert_local_to_global", text="Local to Global")
row.operator("bim.convert_global_to_local", text="Global to Local")
row = self.layout.row(align=True)
row.label(text="Orientation Calculator", icon="TRACKING_REFINE_FORWARDS")
row = self.layout.row(align=True)
row.prop(props, "angle_degree_input_x", text="Angle (°) rel to Easting")
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_x"
row = self.layout.row(align=True)
row.prop(props, "x_axis_abscissa_output", text="XAxis Abscissa")
row = self.layout.row(align=True)
row.prop(props, "x_axis_ordinate_output", text="XAxis Ordinate")
row = self.layout.row(align=True)
row.prop(props, "angle_degree_input_y", text="Angle (°) rel to to +Y")
row.operator("bim.convert_angle_to_coord", text="", icon="FILE_REFRESH").type = "rel_y"
row = self.layout.row(align=True)
row.prop(props, "y_axis_abscissa_output", text="North Abscissa")
row = self.layout.row(align=True)
row.prop(props, "y_axis_ordinate_output", text="North Ordinate")
@@ -54,6 +54,8 @@ classes = (
product.AddEmptyType,
product.AlignProduct,
product.ChangeTypePage,
product.DisableAddType,
product.EnableAddType,
product.LoadTypeThumbnails,
product.MirrorElements,
workspace.Hotkey,
@@ -37,6 +37,24 @@ from . import prop
import json
class EnableAddType(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_add_type"
bl_label = "Enable Add Type"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
bpy.context.scene.BIMModelProperties.is_adding_type = True
class DisableAddType(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_add_type"
bl_label = "Disable Add Type"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
bpy.context.scene.BIMModelProperties.is_adding_type = False
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
bl_idname = "bim.add_empty_type"
bl_label = "Add Empty Type"
@@ -154,10 +172,11 @@ class AddConstrTypeInstance(bpy.types.Operator):
)
else:
parent = ifcopenshell.util.element.get_container(building_element)
parent_obj = tool.Ifc.get_object(parent)
blenderbim.core.spatial.assign_container(
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
)
if parent:
parent_obj = tool.Ifc.get_object(parent)
blenderbim.core.spatial.assign_container(
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj
)
# set occurences properties for the types defined with modifiers
if instance_class in ["IfcWindow", "IfcDoor"]:
@@ -92,6 +92,7 @@ class BIMModelProperties(PropertyGroup):
)
icon_id: bpy.props.IntProperty()
updating: bpy.props.BoolProperty(default=False)
is_adding_type: bpy.props.BoolProperty(default=False)
occurrence_name_style: bpy.props.EnumProperty(
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
name="Occurrence Name Style",
@@ -132,7 +133,7 @@ class BIMModelProperties(PropertyGroup):
z: bpy.props.FloatProperty(name="Z", default=0.5)
rl1: bpy.props.FloatProperty(name="RL", default=1) # Used for things like walls, doors, flooring, skirting, etc
rl2: bpy.props.FloatProperty(name="RL", default=1) # Used for things like windows, other hosted furniture
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE")
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE", min=0, max=pi / 180 * 89)
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
type_template: bpy.props.EnumProperty(
items=(
@@ -70,10 +70,7 @@ class LaunchTypeManager(bpy.types.Operator):
row = columns.row(align=True)
row.alignment = "CENTER"
row.prop(props, "type_predefined_type", text="")
row.prop(props, "type_template", text="")
row.prop(props, "type_name", text="")
row.operator("bim.add_type", icon="ADD", text="")
# In case you want something here in the future
row = columns.row(align=True)
row.alignment = "RIGHT"
@@ -86,6 +83,22 @@ class LaunchTypeManager(bpy.types.Operator):
op = row.operator("bim.change_type_page", icon="TRIA_RIGHT", text="")
op.page = AuthoringData.data["next_page"]
if props.is_adding_type:
row = self.layout.row()
box = row.box()
row = box.row()
row.prop(props, "type_predefined_type")
row = box.row()
row.prop(props, "type_template")
row = box.row()
row.prop(props, "type_name")
row = box.row(align=True)
row.operator("bim.add_type", icon="CHECKMARK", text="Save New Type")
row.operator("bim.disable_add_type", icon="CANCEL", text="")
else:
row = self.layout.row()
row.operator("bim.enable_add_type", icon="ADD", text="Create New Type")
flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
for relating_type in AuthoringData.data["paginated_relating_types"]:
@@ -77,14 +77,15 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"}
if self.join_type in ("L", "V"):
if len(selected_objs) == 2:
another_selected_object = next(o for o in selected_objs if o != context.active_object)
if self.join_type == "L":
joiner.join_L(another_selected_object, context.active_object)
elif self.join_type == "V":
joiner.join_V(another_selected_object, context.active_object)
self.report({"ERROR"}, f"It requires 2 selected objects to do join of type {self.join_type}")
return {"CANCELLED"}
if len(selected_objs) != 2:
self.report({"ERROR"}, f"It requires 2 selected objects to do join of type {self.join_type}")
return {"CANCELLED"}
another_selected_object = next(o for o in selected_objs if o != context.active_object)
if self.join_type == "L":
joiner.join_L(another_selected_object, context.active_object)
elif self.join_type == "V":
joiner.join_V(another_selected_object, context.active_object)
return {"FINISHED"}
if self.join_type == "T":
elements = [tool.Ifc.get_entity(o) for o in context.selected_objects]
@@ -22,6 +22,7 @@ from . import ui, prop, operator
classes = (
operator.AppendLibraryElement,
operator.AssignLibraryDeclaration,
operator.AppendEntireLibrary,
operator.ChangeLibraryElement,
operator.CreateProject,
operator.DisableEditingHeader,
@@ -33,10 +34,10 @@ classes = (
operator.LoadLink,
operator.LoadProject,
operator.LoadProjectElements,
operator.NewProject,
operator.RefreshLibrary,
operator.RewindLibrary,
operator.SaveLibraryFile,
operator.AppendEntireLibrary,
operator.SelectLibraryFile,
operator.ToggleFilterCategories,
operator.ToggleLinkVisibility,
@@ -48,6 +49,7 @@ classes = (
prop.FilterCategory,
prop.Link,
prop.BIMProjectProperties,
ui.BIM_MT_project,
ui.BIM_PT_project,
ui.BIM_PT_project_library,
ui.BIM_PT_links,
@@ -57,22 +59,11 @@ classes = (
)
def menu_func_export(self, context):
op = self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
op.should_save_as = True
def menu_func_import(self, context):
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def register():
bpy.types.Scene.BIMProjectProperties = bpy.props.PointerProperty(type=prop.BIMProjectProperties)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
def unregister():
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
bpy.types.TOPBAR_MT_file.remove(ui.file_menu)
del bpy.types.Scene.BIMProjectProperties
@@ -38,6 +38,53 @@ from blenderbim.bim import export_ifc
from pathlib import Path
class NewProject(bpy.types.Operator):
bl_idname = "bim.new_project"
bl_label = "New Project"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Start a new IFC project in a fresh session"
preset: bpy.props.StringProperty()
def execute(self, context):
bpy.ops.wm.read_homefile()
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
if self.preset == "metric_m":
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
bpy.context.scene.unit_settings.system = "METRIC"
bpy.context.scene.unit_settings.length_unit = "METERS"
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
bpy.context.scene.BIMProjectProperties.template_file = "0"
elif self.preset == "metric_mm":
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
bpy.context.scene.unit_settings.system = "METRIC"
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
bpy.context.scene.BIMProjectProperties.template_file = "0"
elif self.preset == "imperial_ft":
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
bpy.context.scene.unit_settings.system = "IMPERIAL"
bpy.context.scene.unit_settings.length_unit = "FEET"
bpy.context.scene.BIMProperties.area_unit = "square foot"
bpy.context.scene.BIMProperties.volume_unit = "cubic foot"
bpy.context.scene.BIMProjectProperties.template_file = "0"
elif self.preset == "demo":
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
bpy.context.scene.unit_settings.system = "METRIC"
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Library.ifc"
if self.preset != "wizard":
bpy.ops.bim.create_project()
return {"FINISHED"}
class CreateProject(bpy.types.Operator):
bl_idname = "bim.create_project"
bl_label = "Create Project"
@@ -528,6 +575,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
should_start_fresh_session: bpy.props.BoolProperty(name="Should Start Fresh Session", default=True)
def execute(self, context):
if not self.is_existing_ifc_file():
@@ -540,6 +588,12 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
return {"FINISHED"}
def invoke(self, context, event):
if self.should_start_fresh_session:
bpy.ops.wm.read_homefile()
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
@@ -828,6 +882,10 @@ class ExportIFC(bpy.types.Operator):
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
save_as_invoked: bpy.props.BoolProperty(name="Save As Dialog Was Invoked", default=False, options={"HIDDEN"})
@classmethod
def poll(cls, context):
return tool.Ifc.get()
def draw(self, context):
layout = self.layout
layout.prop(self, "json_version")
@@ -18,11 +18,36 @@
import os
from blenderbim.bim.helper import prop_with_search
from bpy.types import Panel, UIList
from bpy.types import Panel, Menu, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.project.data import ProjectData
class BIM_MT_project(Menu):
bl_idname = "BIM_MT_project"
bl_label = "New IFC Project"
def draw(self, context):
layout = self.layout
layout.operator("bim.new_project", text="New Metric (m) Project").preset = "metric_m"
layout.operator("bim.new_project", text="New Metric (mm) Project").preset = "metric_mm"
layout.operator("bim.new_project", text="New Imperial (ft) Project").preset = "imperial_ft"
layout.operator("bim.new_project", text="New Demo Project").preset = "demo"
layout.operator("bim.new_project", text="New Project Wizard").preset = "wizard"
def file_menu(self, context):
self.layout.menu("BIM_MT_project", icon="COLLECTION_NEW")
op = self.layout.operator("bim.load_project", text="Open IFC Project", icon="FILEBROWSER")
op.should_start_fresh_session = True
self.layout.separator()
op = self.layout.operator("export_ifc.bim", icon="FILE_TICK", text="Save IFC Project")
op.should_save_as = False
op = self.layout.operator("export_ifc.bim", text="Save IFC Project As...")
op.should_save_as = True
self.layout.separator()
class BIM_PT_project(Panel):
bl_label = "IFC Project"
bl_idname = "BIM_PT_project"
@@ -160,10 +185,8 @@ class BIM_PT_project(Panel):
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
row = self.layout.row(align=True)
op = row.operator("export_ifc.bim", icon="EXPORT", text="Save Project")
op = row.operator("export_ifc.bim", icon="FILE_TICK", text="Save Project")
op.should_save_as = False
op = row.operator("export_ifc.bim", icon="FILE_TICK", text="Save As")
op.should_save_as = True
row.operator("bim.unload_project", text="", icon="CANCEL")
def draw_create_project_ui(self, context):
@@ -182,7 +205,7 @@ class BIM_PT_project(Panel):
row = self.layout.row(align=True)
row.operator("bim.create_project")
row.operator("bim.load_project")
row.operator("bim.load_project").should_start_fresh_session = False
class BIM_PT_project_library(Panel):
@@ -320,7 +320,6 @@ class EditPset(bpy.types.Operator, Operator):
tool.Blender.update_viewport()
class SelectSimilarPsetValue(bpy.types.Operator):
"""
Selects objects with the same property value.
@@ -364,9 +363,9 @@ class SelectSimilarPsetValue(bpy.types.Operator):
obj.select_set(True)
except:
continue
return {"FINISHED"}
class RemovePset(bpy.types.Operator, Operator):
bl_idname = "bim.remove_pset"
bl_label = "Remove Pset"
@@ -159,7 +159,7 @@ class IfcClassData:
@classmethod
def name(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
if not element:
return
name = element.is_a()
@@ -170,13 +170,13 @@ class IfcClassData:
@classmethod
def ifc_class(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
if element:
return element.is_a()
@classmethod
def can_reassign_class(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
element = tool.Ifc.get_entity(bpy.context.view_layer.objects.active)
if element:
if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"):
return False
@@ -383,7 +383,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
elif template == "RAILING":
mesh = bpy.data.meshes.new("IfcRailing")
obj = bpy.data.objects.new("TYPEX", mesh)
obj = bpy.data.objects.new(name, mesh)
element = blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
@@ -402,7 +402,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
elif template == "ROOF":
mesh = bpy.data.meshes.new("IfcRoof")
obj = bpy.data.objects.new("TYPEX", mesh)
obj = bpy.data.objects.new(name, mesh)
element = blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
+18
View File
@@ -335,6 +335,24 @@ class ModuleVisibility(PropertyGroup):
is_visible: BoolProperty(name="Value", default=True, update=update_is_visible)
class BIMAreaProperties(PropertyGroup):
tab: EnumProperty(
default="PROJECT",
items=[
("PROJECT", "Project Overview", "", "VIEW3D", 1),
("OBJECT", "Object Information", "", "FILE_3D", 2),
("MATERIALS", "Materials and Styles", "", "MATERIAL", 3),
("DRAWINGS", "Drawings and Documents", "", "DOCUMENTS", 4),
("SERVICES", "Services and Systems", "", "NETWORK_DRIVE", 5),
("STRUCTURE", "Structural Analysis", "", "EDITMODE_HLT", 6),
("SCHEDULING", "Construction Scheduling", "", "NLA", 7),
("FM", "Facility Management", "", "PACKAGE", 8),
("OTHER", "Other Properties", "", "COLLAPSEMENU", 9),
],
name="Tab",
)
class BIMProperties(PropertyGroup):
ui_preset: EnumProperty(
name="UI Preset",
+52 -6
View File
@@ -133,6 +133,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
spreadsheet_command: StringProperty(name="Spreadsheet Command", description="E.g. [['libreoffice', path]]")
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True)
should_play_chaching_sound: BoolProperty(
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
)
@@ -209,6 +210,8 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
row = layout.row()
row.prop(self, "should_hide_empty_props")
row = layout.row()
row.prop(self, "should_setup_workspace")
row = layout.row()
row.prop(self, "should_play_chaching_sound")
row = layout.row()
row.prop(self, "lock_grids_on_import")
@@ -278,12 +281,30 @@ def ifc_units(self, context):
# Scene panel groups
class BIM_PT_root(Panel):
bl_label = "BlenderBIM Add-on"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_order = 0
bl_options = {"HIDE_HEADER"}
def draw(self, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
self.layout.prop(aprops, "tab", text="")
class BIM_PT_project_info(Panel):
bl_label = "IFC Project Info"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "PROJECT"
def draw(self, context):
pass
@@ -295,6 +316,11 @@ class BIM_PT_project_setup(Panel):
bl_context = "scene"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "PROJECT"
def draw(self, context):
pass
@@ -306,6 +332,11 @@ class BIM_PT_collaboration(Panel):
bl_context = "scene"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "OTHER"
def draw(self, context):
pass
@@ -319,7 +350,8 @@ class BIM_PT_selection(Panel):
@classmethod
def poll(cls, context):
return tool.Ifc.get()
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "PROJECT" and tool.Ifc.get()
def draw(self, context):
pass
@@ -334,7 +366,8 @@ class BIM_PT_geometry(Panel):
@classmethod
def poll(cls, context):
return tool.Ifc.get()
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "PROJECT" and tool.Ifc.get()
def draw(self, context):
pass
@@ -349,7 +382,8 @@ class BIM_PT_4D5D(Panel):
@classmethod
def poll(cls, context):
return tool.Ifc.get()
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "SCHEDULING" and tool.Ifc.get()
def draw(self, context):
pass
@@ -364,7 +398,8 @@ class BIM_PT_structural(Panel):
@classmethod
def poll(cls, context):
return tool.Ifc.get()
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "STRUCTURE" and tool.Ifc.get()
def draw(self, context):
pass
@@ -379,7 +414,8 @@ class BIM_PT_services(Panel):
@classmethod
def poll(cls, context):
return tool.Ifc.get()
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "SERVICES" and tool.Ifc.get()
def draw(self, context):
pass
@@ -392,6 +428,11 @@ class BIM_PT_quality_control(Panel):
bl_context = "scene"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "OTHER"
def draw(self, context):
pass
@@ -403,6 +444,11 @@ class BIM_PT_integrations(Panel):
bl_context = "scene"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return aprops.tab == "OTHER"
def draw(self, context):
pass
@@ -493,7 +539,7 @@ class UIData:
@classmethod
def load(cls):
cls.data = { "version": cls.version() }
cls.data = {"version": cls.version()}
cls.is_loaded = True
@classmethod
+11 -4
View File
@@ -47,6 +47,7 @@ def rewind_brick_class(brick):
def close_brick_project(brick):
brick.clear_project()
brick.clear_brick_browser()
def convert_brick_project(ifc, brick):
@@ -67,13 +68,13 @@ def assign_brick_reference(ifc, brick, element=None, library=None, brick_uri=Non
brick.add_brickifc_reference(brick_uri, element, project)
def add_brick(ifc, brick, element=None, namespace=None, brick_class=None, library=None):
def add_brick(ifc, brick, element=None, namespace=None, brick_class=None, library=None, label="Unnamed"):
if element:
brick_uri = brick.add_brick_from_element(element, namespace, brick_class)
if library:
brick.run_assign_brick_reference(element=element, library=library, brick_uri=brick_uri)
else:
brick_uri = brick.add_brick(namespace, brick_class)
brick_uri = brick.add_brick(namespace, brick_class, label)
brick.run_refresh_brick_viewer()
@@ -110,13 +111,19 @@ def remove_brick(ifc, brick, library=None, brick_uri=None):
brick.remove_brick(brick_uri)
brick.run_refresh_brick_viewer()
def undo_brick(brick):
brick.undo_brick()
brick.run_refresh_brick_viewer()
def redo_brick(brick):
brick.redo_brick()
brick.run_refresh_brick_viewer()
def serialize_brick(brick, file_name="BlenderBIMSerializeTest.ttl"):
brick.serialize_brick(file_name)
def serialize_brick(brick):
brick.serialize_brick()
def add_namespace(brick, alias=None, uri=None):
brick.add_namespace(alias, uri)
@@ -80,3 +80,7 @@ def convert_global_to_local(georeference):
coordinates = georeference.enh2xyz(georeference.get_coordinates("input"), georeference.get_map_conversion())
georeference.set_coordinates("output", coordinates)
georeference.set_cursor_location(coordinates)
def convert_angle_to_coord(georeference, type):
vector_coordinates = georeference.angle2coords(georeference.get_angle(type), type)
georeference.set_vector_coordinates(vector_coordinates,type)
+4 -1
View File
@@ -370,13 +370,15 @@ class Geometry:
@interface
class Georeference:
def angle2coords(cls, angle, type): pass
def disable_editing(cls): pass
def enable_editing(cls): pass
def enh2xyz(cls, map_conversion, coordinates): pass
def get_angle(cls, type): pass
def get_coordinates(cls, io): pass
def get_cursor_location(cls): pass
def get_map_conversion(cls): pass
def get_map_conversion_attributes(cls): pass
def get_map_conversion(cls): pass
def get_projected_crs_attributes(cls): pass
def get_true_north_attributes(cls): pass
def import_map_conversion(cls): pass
@@ -388,6 +390,7 @@ class Georeference:
def set_cursor_location(cls, coordinates): pass
def set_ifc_grid_north(cls): pass
def set_ifc_true_north(cls): pass
def set_vector_coordinates(cls, vector_coordinates, type): pass
def xyz2enh(cls, map_conversion, coordinates): pass
+10 -10
View File
@@ -41,12 +41,12 @@ logger.setLevel(logging.ERROR)
class Brick(blenderbim.core.tool.Brick):
@classmethod
def add_brick(cls, namespace, brick_class):
def add_brick(cls, namespace, brick_class, label):
ns = Namespace(namespace)
brick = ns[ifcopenshell.guid.expand(ifcopenshell.guid.new())]
with BrickStore.graph.new_changeset("PROJECT") as cs:
cs.add((brick, RDF.type, URIRef(brick_class)))
cs.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Unnamed")))
cs.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal(label)))
return str(brick)
@classmethod
@@ -275,8 +275,6 @@ class Brick(blenderbim.core.tool.Brick):
with BrickStore.graph.new_changeset("SCHEMA") as cs:
cs.load_file(BrickStore.schema)
BrickStore.graph.bind("digitaltwin", Namespace("https://example.org/digitaltwin#"))
BrickStore.graph.bind("brick", Namespace("https://brickschema.org/schema/Brick#"))
BrickStore.graph.bind("rdfs", Namespace("http://www.w3.org/2000/01/rdf-schema#"))
@classmethod
def pop_brick_breadcrumb(cls):
@@ -332,12 +330,14 @@ class Brick(blenderbim.core.tool.Brick):
BrickStore.graph.redo()
@classmethod
def serialize_brick(cls, file_name):
#temporary file path, could either be user selected for "save as" or use the BrickStore.path for simply "save"
cwd = os.path.dirname(os.path.realpath(__file__))
dest = os.path.join(cwd, "..", "bim", "schema", file_name)
BrickStore.get_project().serialize(destination=dest, format="turtle")
def serialize_brick(cls):
BrickStore.get_project().serialize(destination=BrickStore.path, format="turtle")
@classmethod
def add_namespace(cls, alias, uri):
BrickStore.graph.bind(alias, Namespace(uri))
# need some way to reload namespace enum view
class BrickStore:
schema = None # this is now a os path
path = None # file path if the project was loaded in
+4 -1
View File
@@ -1499,7 +1499,10 @@ class Drawing(blenderbim.core.tool.Drawing):
filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing)
for visible_obj in bpy.context.visible_objects:
hide = tool.Ifc.get_entity(visible_obj) not in filtered_elements
element = tool.Ifc.get_entity(visible_obj)
if not element:
continue
hide = element not in filtered_elements
if bpy.context.view_layer.objects.get(visible_obj.name):
visible_obj.hide_set(hide)
visible_obj.hide_render = hide
@@ -272,3 +272,27 @@ class Georeference(blenderbim.core.tool.Georeference):
float(props.map_conversion.get("XAxisOrdinate").string_value),
)
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
@classmethod
def angle2coords(cls, angle, type):
if type == "rel_x":
return ifcopenshell.util.geolocation.angle2xaxis(angle)
elif type == "rel_y":
return ifcopenshell.util.geolocation.angle2yaxis(angle)
@classmethod
def get_angle(cls, type):
if type == "rel_x":
return bpy.context.scene.BIMGeoreferenceProperties.angle_degree_input_x
elif type == "rel_y":
return bpy.context.scene.BIMGeoreferenceProperties.angle_degree_input_y
@classmethod
def set_vector_coordinates(cls, vector_coordinates, type):
x, y = vector_coordinates
if type == "rel_x":
bpy.context.scene.BIMGeoreferenceProperties.x_axis_abscissa_output = str(x)
bpy.context.scene.BIMGeoreferenceProperties.x_axis_ordinate_output = str(y)
elif type == "rel_y":
bpy.context.scene.BIMGeoreferenceProperties.y_axis_abscissa_output = str(x)
bpy.context.scene.BIMGeoreferenceProperties.y_axis_ordinate_output = str(y)
+2 -2
View File
@@ -103,7 +103,7 @@ class Ifc(blenderbim.core.tool.Ifc):
ifc_path = cls.get_path()
if os.path.isfile(ifc_path):
ifc_path = os.path.dirname(ifc_path)
return uri if not uri or os.path.isabs(uri) else os.path.join(ifc_path, uri)
return (uri if not uri or os.path.isabs(uri) else os.path.join(ifc_path, uri)).replace("\\", "/")
@classmethod
def get_relative_uri(cls, uri):
@@ -112,7 +112,7 @@ class Ifc(blenderbim.core.tool.Ifc):
ifc_path = cls.get_path()
if os.path.isfile(ifc_path):
ifc_path = os.path.dirname(ifc_path)
return os.path.relpath(uri, ifc_path)
return os.path.relpath(uri, ifc_path).replace("\\", "/")
@classmethod
def unlink(cls, element=None, obj=None):
@@ -31,6 +31,8 @@ class Project(blenderbim.core.tool.Project):
# TODO refactor
filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", template)
bpy.ops.bim.select_library_file(filepath=filepath)
if IfcStore.library_file.schema != tool.Ifc.get().schema:
return
for element in IfcStore.library_file.by_type("IfcTypeProduct"):
bpy.ops.bim.append_library_element(definition=element.id())
@@ -135,6 +135,7 @@ def create_z_profile_lips_curve(ifc_file, FirstFlangeWidth, SecondFlangeWidth, D
return ifc_curve
class LibraryGenerator:
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
ifcopenshell.api.pre_listeners = {}
@@ -171,10 +172,13 @@ class LibraryGenerator:
"profile_z_lips": ("IfcArbitraryClosedProfileDef", {"t": "WallThickness", "c1": "FirstFlangeWidth", "c2": "SecondFlangeWidth", "h": "Depth", "r": "FilletRadius", "ll": "Girth"}),
}
US_profiles = ("ibeam_w_imp", )
if parse_profiles_type == "AU":
bolt_class_filter = lambda x: "bluescope" in x.id
elif parse_profiles_type == "EU":
bolt_class_filter = lambda x: "bluescope" not in x.id
bolt_class_filter = lambda x: "bluescope" not in x.id and x.id not in US_profiles
elif parse_profiles_type == "US":
bolt_class_filter = lambda x: x.id in US_profiles
else:
bolt_class_filter = lambda x: True
@@ -195,12 +199,27 @@ class LibraryGenerator:
# like hollow_generic_square
continue
bolts_cols = bolt_class.parameters.tables[0].columns
bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols]
bolts_cols_original = bolt_class.parameters.tables[0].columns
bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols_original]
inch_to_mm = lambda x: x * 0.0254 * 1000
def assure_data_units_is_mm(data, units):
data = data.copy()
for i in range(len(units)):
unit = units[i]
assert unit in ("Length (in)", "Length (mm)")
if unit != "Length (in)":
continue
for profile in data:
data[profile][i] = inch_to_mm(data[profile][i])
return data
bolts_data = bolt_class.parameters.tables[0].data
data_units = [bolt_class.parameters.types[col] for col in bolts_cols_original]
bolts_data = assure_data_units_is_mm(bolts_data, data_units)
for prof_name in bolts_data.keys():
ifc_params = dict(zip(bolts_cols, bolts_data[prof_name]))
ifc_params = dict(zip(bolts_cols, bolts_data[prof_name], strict=True))
if "unused" in ifc_params:
del ifc_params["unused"]
@@ -240,3 +259,4 @@ if __name__ == "__main__":
path = Path(__file__).parents[1] / "blenderbim/bim/data/libraries"
LibraryGenerator().generate(parse_profiles_type="EU", output_filename=str(path / "IFC4 EU Steel.ifc"))
LibraryGenerator().generate(parse_profiles_type="AU", output_filename=str(path / "IFC4 AU Steel.ifc"))
LibraryGenerator().generate(parse_profiles_type="US", output_filename=str(path / "IFC4 US Steel.ifc"))