diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index d89b6d3734..2717eae142 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -202,7 +202,7 @@ class CreateDrawing(bpy.types.Operator): @classmethod def poll(cls, context): - return bool(tool.Ifc.get() and tool.Drawing.is_camera_orthographic() and tool.Drawing.is_drawing_active()) + return bool(tool.Ifc.get() and tool.Drawing.is_drawing_active()) def invoke(self, context, event): # printing all drawings on shift+click @@ -251,14 +251,20 @@ class CreateDrawing(bpy.types.Operator): self.svg_writer.setup_drawing_resource_paths(self.camera_element) + underlay_svg = None + linework_svg = None + annotation_svg = None + with profile("Generate underlay"): underlay_svg = self.generate_underlay(context) with profile("Generate linework"): - linework_svg = self.generate_linework(context) + if tool.Drawing.is_camera_orthographic(): + linework_svg = self.generate_linework(context) with profile("Generate annotation"): - annotation_svg = self.generate_annotation(context) + if tool.Drawing.is_camera_orthographic(): + annotation_svg = self.generate_annotation(context) with profile("Combine SVG layers"): svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg) diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index b9606797a1..ac8ac370dc 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -589,6 +589,12 @@ class Drawing(blenderbim.core.tool.Drawing): def import_drawing(cls, drawing): settings = ifcopenshell.geom.settings() settings.set(settings.STRICT_TOLERANCE, True) + + camera_type = "ORTHO" + body = ifcopenshell.util.representation.get_representation(drawing, "Model", "Body", "MODEL_VIEW") + if "IfcRectangularPyramid" in {e.is_a() for e in tool.Ifc.get().traverse(body)}: + camera_type = "PERSP" + shape = ifcopenshell.geom.create_shape(settings, drawing) geometry = shape.geometry @@ -601,17 +607,37 @@ class Drawing(blenderbim.core.tool.Drawing): depth = max(z) - min(z) camera = bpy.data.cameras.new(tool.Loader.get_mesh_name(geometry)) - camera.type = "ORTHO" - camera.ortho_scale = width if width > height else height - camera.clip_start = 0.002 - camera.clip_end = depth + camera.type = camera_type + camera.show_limits = True - if width > height: - camera.BIMCameraProperties.raster_x = 1000 - camera.BIMCameraProperties.raster_y = round(1000 * (height / width)) - else: - camera.BIMCameraProperties.raster_x = round(1000 * (width / height)) - camera.BIMCameraProperties.raster_y = 1000 + if camera_type == "ORTHO": + camera.ortho_scale = width if width > height else height + camera.clip_start = 0.002 # Technically 0, but Blender doesn't allow this, so 2mm it is! + camera.clip_end = depth + + if width > height: + camera.BIMCameraProperties.raster_x = 1000 + camera.BIMCameraProperties.raster_y = round(1000 * (height / width)) + else: + camera.BIMCameraProperties.raster_x = round(1000 * (width / height)) + camera.BIMCameraProperties.raster_y = 1000 + elif camera_type == "PERSP": + abs_min_z = abs(min(z)) + abs_max_z = abs(max(z)) + camera.clip_start = abs_max_z + camera.clip_end = abs_min_z + max_res = 1000 + + if width > height: + fov = 2 * math.atan(width / (2 * abs_min_z)) + camera.BIMCameraProperties.raster_x = max_res + camera.BIMCameraProperties.raster_y = int(max_res / (width / height)) + else: + fov = 2 * math.atan(height / (2 * abs_min_z)) + camera.BIMCameraProperties.raster_y = max_res + camera.BIMCameraProperties.raster_x = int(max_res * (width / height)) + + camera.angle = fov psets = ifcopenshell.util.element.get_psets(drawing) pset = psets.get("EPset_Drawing") diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 0bebedba6f..8b458ef777 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . import bpy +import math import bmesh import ifcopenshell.util.unit from mathutils import Vector, Matrix @@ -239,7 +240,10 @@ class Usecase: elif isinstance(self.settings["geometry"], bpy.types.Curve): return self.create_curve3d_representation() elif isinstance(self.settings["geometry"], bpy.types.Camera): - return self.create_camera_block_representation() + if self.settings["geometry"].type == "ORTHO": + return self.create_camera_block_representation() + elif self.settings["geometry"].type == "PERSP": + return self.create_camera_pyramid_representation() elif not len(self.settings["geometry"].edges): return self.create_point_cloud_representation() elif not len(self.settings["geometry"].polygons): @@ -269,14 +273,12 @@ class Usecase: block = self.file.create_entity( "IfcBlock", - **{ - "Position": self.file.createIfcAxis2Placement3D( - self.create_cartesian_point(-width / 2, -height / 2, -self.settings["geometry"].clip_end) - ), - "XLength": self.convert_si_to_unit(width), - "YLength": self.convert_si_to_unit(height), - "ZLength": self.convert_si_to_unit(self.settings["geometry"].clip_end), - }, + Position=self.file.createIfcAxis2Placement3D( + self.create_cartesian_point(-width / 2, -height / 2, -self.settings["geometry"].clip_end) + ), + XLength=self.convert_si_to_unit(width), + YLength=self.convert_si_to_unit(height), + ZLength=self.convert_si_to_unit(self.settings["geometry"].clip_end), ) return self.file.createIfcShapeRepresentation( @@ -286,6 +288,48 @@ class Usecase: [self.file.createIfcCsgSolid(block)], ) + def create_camera_pyramid_representation(self): + raster_x = self.settings["geometry"].BIMCameraProperties.raster_x + raster_y = self.settings["geometry"].BIMCameraProperties.raster_y + fov = self.settings["geometry"].angle + + clip_end = self.settings["geometry"].clip_end + clip_start = self.settings["geometry"].clip_start + + if self.is_camera_landscape(): + half_width = math.tan(fov / 2) * clip_end + half_height = half_width * raster_y / raster_x + else: + half_height = math.tan(fov / 2) * clip_end + half_width = half_height * raster_x / raster_y + + x_length = 2 * half_width + y_length = 2 * half_height + + pyramid = self.file.create_entity( + "IfcRectangularPyramid", + Position=self.file.createIfcAxis2Placement3D( + self.create_cartesian_point(-x_length / 2, -y_length / 2, -clip_end) + ), + XLength=self.convert_si_to_unit(x_length), + YLength=self.convert_si_to_unit(y_length), + Height=self.convert_si_to_unit(clip_end), + ) + + surface = self.file.createIfcPlane( + self.file.createIfcAxis2Placement3D(self.create_cartesian_point(0, 0, -clip_start)) + ) + half_space = self.file.createIfcHalfSpaceSolid(surface, False) + + clipping_result = self.file.create_entity("IfcBooleanClippingResult", "DIFFERENCE", pyramid, half_space) + + return self.file.createIfcShapeRepresentation( + self.settings["context"], + self.settings["context"].ContextIdentifier, + "CSG", + [self.file.createIfcCsgSolid(clipping_result)], + ) + def is_camera_landscape(self): return ( self.settings["geometry"].BIMCameraProperties.raster_x @@ -372,6 +416,7 @@ class Usecase: def is_mesh_curve_consequtive(self, geom_data): import blenderbim.tool as tool + bm = tool.Blender.get_bmesh_for_mesh(geom_data) bm.verts.ensure_lookup_table() start_vert = bm.verts[0] @@ -387,6 +432,7 @@ class Usecase: processed_verts = set() processed_verts.add(start_vert) + def validate_edge(edge, start_vert, processed_verts): cur_vert = edge.other_vert(start_vert) while True: @@ -403,13 +449,13 @@ class Usecase: edge = next(e for e in edges if e != edge) cur_vert = edge.other_vert(cur_vert) return True - + if not validate_edge(edge0, start_vert, processed_verts): return - + if edge1 and not validate_edge(edge1, start_vert, processed_verts): return - + if len(processed_verts) != n_verts: return False return True @@ -479,6 +525,7 @@ class Usecase: def remove_doubles_from_mesh(self, mesh): import blenderbim.tool as tool + bm = tool.Blender.get_bmesh_for_mesh(mesh) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) tool.Blender.apply_bmesh(mesh, bm) @@ -488,8 +535,7 @@ class Usecase: self.remove_doubles_from_mesh(geom_data) curves = [] points = [ - self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None) - for v in geom_data.vertices + self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None) for v in geom_data.vertices ] coord_list = [p.Coordinates for p in points] edge_loops = []