From 50d71d5ea190279230746f107dd91d11251bc786 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 9 Feb 2023 20:13:14 +1100 Subject: [PATCH] Black code and make profile representation optional for windows. --- .../blenderbim/bim/module/model/window.py | 85 ++++++--------- .../ifcopenshell/util/shape_builder.py | 100 +++++++++--------- 2 files changed, 85 insertions(+), 100 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/window.py b/src/blenderbim/blenderbim/bim/module/model/window.py index 0652957bca..f0e4b3514c 100644 --- a/src/blenderbim/blenderbim/bim/module/model/window.py +++ b/src/blenderbim/blenderbim/bim/module/model/window.py @@ -18,27 +18,22 @@ import bpy +import json +import bmesh +import collections +import ifcopenshell +import blenderbim.tool as tool +import blenderbim.core.geometry as core +from ifcopenshell.api.geometry.add_window_representation import DEFAULT_PANEL_SCHEMAS from bpy.types import Operator from bpy.props import FloatProperty, IntProperty, BoolProperty from bpy_extras.object_utils import AddObjectHelper, object_data_add -import bmesh from bmesh.types import BMVert - -import ifcopenshell -from ifcopenshell.api.geometry.add_window_representation import DEFAULT_PANEL_SCHEMAS -import blenderbim -import blenderbim.tool as tool -import blenderbim.core.geometry as core from blenderbim.bim.helper import convert_property_group_from_si from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.model.helper import get_ifc_context_or_create, replace_ifc_representation_for_object - +from blenderbim.bim.module.model.helper import replace_ifc_representation_for_object from mathutils import Vector -from pprint import pprint - from os.path import basename, dirname -import json -import collections V = lambda *x: Vector([float(i) for i in x]) @@ -46,7 +41,7 @@ V = lambda *x: Vector([float(i) for i in x]) def update_window_modifier_representation(context): obj = context.active_object - ifc_element = tool.Ifc.get_entity(obj) + element = tool.Ifc.get_entity(obj) props = obj.BIMWindowProperties ifc_file = tool.Ifc.get() @@ -77,23 +72,22 @@ def update_window_modifier_representation(context): } representation_data["panel_properties"].append(panel_data) - # ELEVATION_VIEW representation - ifc_context = get_ifc_context_or_create(ifc_file, "Model", "Profile", "ELEVATION_VIEW") - representation_data["context"] = ifc_context - elevation_representation = ifcopenshell.api.run( - "geometry.add_window_representation", ifc_file, **representation_data - ) - replace_ifc_representation_for_object(ifc_file, ifc_context, obj, elevation_representation) + profile = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Profile", "ELEVATION_VIEW") + if profile: + representation_data["context"] = profile + elevation_representation = ifcopenshell.api.run( + "geometry.add_window_representation", ifc_file, **representation_data + ) + replace_ifc_representation_for_object(ifc_file, profile, obj, elevation_representation) - # MODEL_VIEW representation - ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") - representation_data["context"] = ifc_context + body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") + representation_data["context"] = body model_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data) - replace_ifc_representation_for_object(ifc_file, ifc_context, obj, model_representation) - ifc_element.PartitioningType = props.window_type + replace_ifc_representation_for_object(ifc_file, body, obj, model_representation) + element.PartitioningType = props.window_type -def create_bm_window_frame(bm, size: Vector, thickness: Vector, position: Vector = V(0,0,0).freeze()): +def create_bm_window_frame(bm, size: Vector, thickness: Vector, position: Vector = V(0, 0, 0).freeze()): """`thickness` of the profile is defined as list in the following order: `(LEFT, TOP, RIGHT, BOTTOM)` @@ -119,16 +113,16 @@ def create_bm_window_frame(bm, size: Vector, thickness: Vector, position: Vector ] edges = [ - (0, (0, 1)), - (1, (2, 3)), - (2, (4, 5)), - (3, (6, 7)), - (4, (7, 5)), - (5, (1, 3)), - (6, (4, 6)), - (7, (0, 2)), - (8, (2, 5)), - (9, (3, 7)), + (0, (0, 1)), + (1, (2, 3)), + (2, (4, 5)), + (3, (6, 7)), + (4, (7, 5)), + (5, (1, 3)), + (6, (4, 6)), + (7, (0, 2)), + (8, (2, 5)), + (9, (3, 7)), (10, (4, 0)), (11, (1, 6)), ] @@ -158,7 +152,7 @@ def create_bm_window_frame(bm, size: Vector, thickness: Vector, position: Vector return new_verts + translate_verts -def create_bm_box(bm, size: Vector = V(1,1,1).freeze(), position: Vector = V(0,0,0).freeze()): +def create_bm_box(bm, size: Vector = V(1, 1, 1).freeze(), position: Vector = V(0, 0, 0).freeze()): """create a box of `size`, position box first vertex at `position`""" box_verts = bmesh.ops.create_cube(bm, size=1)["verts"] bmesh.ops.translate(bm, vec=-box_verts[0].co, verts=box_verts) @@ -186,13 +180,9 @@ def create_bm_window( frame_verts = create_bm_window_frame(bm, frame_size, frame_thickness, frame_position) # window glass - glass_size = frame_size - V(frame_thickness*2, 0, frame_thickness*2) + glass_size = frame_size - V(frame_thickness * 2, 0, frame_thickness * 2) glass_size.y = glass_thickness - glass_position = frame_position + V( - frame_thickness, - frame_size.y / 2 - glass_thickness / 2, - frame_thickness - ) + glass_position = frame_position + V(frame_thickness, frame_size.y / 2 - glass_thickness / 2, frame_thickness) glass_verts = create_bm_box(bm, glass_size, glass_position) @@ -344,12 +334,7 @@ class BIM_OT_add_window(Operator): obj.location = spawn_location body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") element = blenderbim.core.root.assign_class( - tool.Ifc, - tool.Collector, - tool.Root, - obj=obj, - ifc_class="IfcWindow", - should_add_representation=False + tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class="IfcWindow", should_add_representation=False ) element.PredefinedType = "WINDOW" diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py index cad8a3437a..7e00a8b133 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py @@ -16,11 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from mathutils import Vector, Matrix +import collections import ifcopenshell import ifcopenshell.api from math import cos, sin, pi -import collections +from mathutils import Vector, Matrix V = lambda *x: Vector([float(i) for i in x]) sign = lambda x: x and (1, -1)[x < 0] @@ -33,30 +33,28 @@ sign = lambda x: x and (1, -1)[x < 0] class ShapeBuilder: def __init__(self, ifc_file): - self.ifc = ifc_file + self.file = ifc_file def polyline(self, points, closed=False, position_offset=None): # > points - list of points formatted like ( (x0, y0), (x1, y1) ) # < IfcIndexedPolyCurve - segments = [(i, i+1) for i in range(1, len(points))] + segments = [(i, i + 1) for i in range(1, len(points))] if closed: - segments.append( (len(points),1) ) + segments.append((len(points), 1)) if position_offset: points = [Vector(p) + position_offset for p in points] dimensions = len(points[0]) if dimensions == 2: - ifc_points = self.ifc.createIfcCartesianPointList2D(points) + ifc_points = self.file.createIfcCartesianPointList2D(points) elif dimensions == 3: - ifc_points = self.ifc.createIfcCartesianPointList3D(points) + ifc_points = self.file.createIfcCartesianPointList3D(points) - ifc_segments = [ self.ifc.createIfcLineIndex( segment ) for segment in segments] - ifc_curve = self.ifc.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments) + ifc_segments = [self.file.createIfcLineIndex(segment) for segment in segments] + ifc_curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments) return ifc_curve - def get_rectangle_coords(self, - size:Vector = Vector( (1., 1.) ).freeze(), - position:Vector = None): + def get_rectangle_coords(self, size: Vector = Vector((1.0, 1.0)).freeze(), position: Vector = None): dimensions = len(size) @@ -75,9 +73,7 @@ class ShapeBuilder: ] return points - def rectangle(self, - size:Vector = Vector( (1., 1.) ).freeze(), - position:Vector = None): + def rectangle(self, size: Vector = Vector((1.0, 1.0)).freeze(), position: Vector = None): """ function supports both 2d and 3d rectangle sizes @@ -88,12 +84,12 @@ class ShapeBuilder: # < IfcIndexedPolyCurve return self.polyline(self.get_rectangle_coords(size, position), closed=True) - def circle(self, center:Vector = Vector( (0., 0.) ).freeze(), radius = 1.): + def circle(self, center: Vector = Vector((0.0, 0.0)).freeze(), radius=1.0): # < returns IfcCircle - ifc_center = self.ifc.createIfcAxis2Placement2D(self.ifc.createIfcCartesianPoint(center)) - ifc_curve = self.ifc.createIfcCircle(ifc_center, radius) + ifc_center = self.file.createIfcAxis2Placement2D(self.file.createIfcCartesianPoint(center)) + ifc_curve = self.file.createIfcCircle(ifc_center, radius) - # self.ifc_file.createIfcAxis2Placement2D(tool.Ifc.get().createIfcCartesianPoint(center[0:2])) + # self.file_file.createIfcAxis2Placement2D(tool.Ifc.get().createIfcCartesianPoint(center[0:2])) return ifc_curve # TODO: explain points order for the curve_between_two_points @@ -114,9 +110,9 @@ class ShapeBuilder: diff = V(0.01, 0.01) * diff_sign middle_point = points[0] + diff points = [points[0], middle_point, points[1]] - seg = self.ifc.createIfcArcIndex((1, 2, 3)) - ifc_points = self.ifc.createIfcCartesianPointList2D(points) - curve = self.ifc.createIfcIndexedPolyCurve(Points=ifc_points, Segments=[seg]) + seg = self.file.createIfcArcIndex((1, 2, 3)) + ifc_points = self.file.createIfcCartesianPointList2D(points) + curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=[seg]) return curve def get_trim_points_from_mask(self, x_axis_radius, y_axis_radius, trim_points_mask, position_offset=None): @@ -157,24 +153,25 @@ class ShapeBuilder: Notion: trimmed ellipse also contains polyline between trim points, meaning IfcTrimmedCurve could be used for further extrusion. """ - direction = self.ifc.createIfcDirection(ref_x_direction) - ifc_position = self.ifc.createIfcAxis2Placement2D( - self.ifc.createIfcCartesianPoint(position), RefDirection=direction + direction = self.file.createIfcDirection(ref_x_direction) + ifc_position = self.file.createIfcAxis2Placement2D( + self.file.createIfcCartesianPoint(position), RefDirection=direction + ) + ifc_ellipse = self.file.createIfcEllipse( + Position=ifc_position, SemiAxis1=x_axis_radius, SemiAxis2=y_axis_radius ) - ifc_ellipse = self.ifc.createIfcEllipse(Position=ifc_position, SemiAxis1=x_axis_radius, SemiAxis2=y_axis_radius) if not trim_points: if not trim_points_mask: return ifc_ellipse trim_points = self.get_trim_points_from_mask( - x_axis_radius, y_axis_radius, - trim_points_mask, position_offset=position + x_axis_radius, y_axis_radius, trim_points_mask, position_offset=position ) - trim1 = [self.ifc.createIfcCartesianPoint(trim_points[0])] - trim2 = [self.ifc.createIfcCartesianPoint(trim_points[1])] + trim1 = [self.file.createIfcCartesianPoint(trim_points[0])] + trim2 = [self.file.createIfcCartesianPoint(trim_points[1])] - trim_ellipse = self.ifc.createIfcTrimmedCurve( + trim_ellipse = self.file.createIfcTrimmedCurve( BasisCurve=ifc_ellipse, Trim1=trim1, Trim2=trim2, SenseAgreement=True, MasterRepresentation="CARTESIAN" ) return trim_ellipse @@ -191,12 +188,11 @@ class ShapeBuilder: if not isinstance(inner_curves, collections.abc.Iterable): inner_curves = [inner_curves] - profile = self.ifc.createIfcArbitraryProfileDefWithVoids( - ProfileName=name, ProfileType=profile_type, - OuterCurve=outer_curve, InnerCurves=inner_curves + profile = self.file.createIfcArbitraryProfileDefWithVoids( + ProfileName=name, ProfileType=profile_type, OuterCurve=outer_curve, InnerCurves=inner_curves ) else: - profile = self.ifc.createIfcArbitraryClosedProfileDef( + profile = self.file.createIfcArbitraryClosedProfileDef( ProfileName=name, ProfileType=profile_type, OuterCurve=outer_curve ) return profile @@ -212,7 +208,7 @@ class ShapeBuilder: processed_objects = [] for c in curve_or_item: if create_copy: - c = ifcopenshell.util.element.copy_deep(self.ifc, c) + c = ifcopenshell.util.element.copy_deep(self.file, c) if c.is_a("IfcIndexedPolyCurve"): coords = [Vector(co) + translation for co in c.Points.CoordList] @@ -274,7 +270,7 @@ class ShapeBuilder: processed_objects = [] for c in curve_or_item: if create_copy: - c = ifcopenshell.util.element.copy_deep(self.ifc, c) + c = ifcopenshell.util.element.copy_deep(self.file, c) if c.is_a("IfcIndexedPolyCurve"): coords = [ @@ -313,7 +309,7 @@ class ShapeBuilder: ): """mirror_axes - along which axes mirror will be applied""" base = point_2d # prevent mutating the argument - mirror_axes = Vector( [-1 if i > 0 else 1 for i in mirror_axes] ) + mirror_axes = Vector([-1 if i > 0 else 1 for i in mirror_axes]) relative_point = base - mirror_point relative_point = relative_point * mirror_axes point_2d = relative_point + mirror_point @@ -366,7 +362,11 @@ class ShapeBuilder: processed_objects = [] for curve_or_item_el in curve_or_item: for mirror_axes in mirror_axes_data: - c = ifcopenshell.util.element.copy_deep(self.ifc, curve_or_item_el) if create_copy else curve_or_item_el + c = ( + ifcopenshell.util.element.copy_deep(self.file, curve_or_item_el) + if create_copy + else curve_or_item_el + ) if c.is_a("IfcIndexedPolyCurve"): inverted_placement_matrix = placement_matrix.inverted() if placement_matrix else None @@ -429,9 +429,9 @@ class ShapeBuilder: trim_coords = [c.Trim1[0].Coordinates, c.Trim2[0].Coordinates] trim_coords = [Vector(coords) for coords in trim_coords] trim_coords = [ - self.mirror_2d_point(base_position, mirror_axes, mirror_point) - for base_position in trim_coords] - + self.mirror_2d_point(base_position, mirror_axes, mirror_point) for base_position in trim_coords + ] + # if mirror only by 1 axis we need to preserve the counter-clockwise order # for the trim points if 0 in mirror_axes: @@ -477,13 +477,13 @@ class ShapeBuilder: if position_y_axis: position_z_axis = position_x_axis.cross(position_y_axis) - ifc_position = self.ifc.createIfcAxis2Placement3D( - self.ifc.createIfcCartesianPoint(position), # position - self.ifc.createIfcDirection(position_z_axis), # Z-axis / Axis - self.ifc.createIfcDirection(position_x_axis), # X-axis / RefDirection + ifc_position = self.file.createIfcAxis2Placement3D( + self.file.createIfcCartesianPoint(position), # position + self.file.createIfcDirection(position_z_axis), # Z-axis / Axis + self.file.createIfcDirection(position_x_axis), # X-axis / RefDirection ) - ifc_direction = self.ifc.createIfcDirection(extrusion_vector) - extruded_area = self.ifc.createIfcExtrudedAreaSolid( + ifc_direction = self.file.createIfcDirection(extrusion_vector) + extruded_area = self.file.createIfcExtrudedAreaSolid( SweptArea=profile_or_curve, Position=ifc_position, ExtrudedDirection=ifc_direction, Depth=magnitude ) return extruded_area @@ -493,7 +493,7 @@ class ShapeBuilder: # < IfcShapeRepresentation if not isinstance(items, collections.abc.Iterable): items = [items] - representation = self.ifc.createIfcShapeRepresentation( + representation = self.file.createIfcShapeRepresentation( ContextOfItems=context, RepresentationIdentifier=context.ContextIdentifier, RepresentationType="SweptSolid" if items[0].is_a("IfcExtrudedAreaSolid") else "Curve2D", @@ -502,4 +502,4 @@ class ShapeBuilder: return representation def deep_copy(self, element): - return ifcopenshell.util.element.copy_deep(self.ifc, element) + return ifcopenshell.util.element.copy_deep(self.file, element)