From 2ad2a102cb05044f4f8b2e9a8bbb3dcbbed1824a Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 27 Oct 2021 19:42:36 +1100 Subject: [PATCH] First pass at connecting to new ifcopenshell.draw. See #1153. --- .../blenderbim/bim/module/drawing/operator.py | 113 +++++++----------- .../blenderbim/bim/module/drawing/prop.py | 22 +++- src/ifcopenshell-python/ifcopenshell/draw.py | 49 +++++--- 3 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index ee0eec8ac1..a8c2e6e200 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -31,6 +31,7 @@ import ifcopenshell.geom import ifcopenshell.util.selector import ifcopenshell.util.representation import blenderbim.bim.schema +import blenderbim.tool as tool import blenderbim.bim.module.drawing.svgwriter as svgwriter import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.sheeter as sheeter @@ -146,6 +147,7 @@ class CreateDrawing(bpy.types.Operator): self.profile_code("Start drawing generation process") self.props = context.scene.DocProperties self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name + self.get_scale() underlay_svg = self.generate_underlay(context) self.profile_code("Generate underlay") linework_svg = self.generate_linework(context) @@ -190,7 +192,7 @@ class CreateDrawing(bpy.types.Operator): elif "" in line: should_skip = True continue - elif "" in line: + elif "" in line: should_skip = False continue elif should_skip: @@ -280,61 +282,35 @@ class CreateDrawing(bpy.types.Operator): svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-linework.svg") if os.path.isfile(svg_path) and self.props.should_use_linework_cache: return svg_path + # This is a work in progress. See #1153 and #1564. - # Switch from old to new if you are testing v0.7.0 - self.generate_linework_old(context, svg_path) - # self.generate_linework_new(svg_path) - return svg_path + import ifcopenshell.draw - def generate_linework_new(self, svg_path): - settings = ifcopenshell.geom.settings( - APPLY_DEFAULT_MATERIALS=True, - DISABLE_TRIANGULATION=True, - INCLUDE_CURVES=True, - EXCLUDE_SOLIDS_AND_SURFACES=False, - ) - buffer = ifcopenshell.geom.serializers.buffer() - serialiser = ifcopenshell.geom.serializers.svg(buffer, settings) - serialiser.setFile(self.file) - serialiser.setElevationRef("DRAWING") - serialiser.setUseNamespace(True) - serialiser.setAlwaysProject(True) - serialiser.setUseHlrPoly(True) - serialiser.setWithoutStoreys(True) - excluded_elements = [] - for ifc_class in ["IfcSpace", "IfcOpeningElement", "IfcDoor", "IfcWindow"]: - excluded_elements += self.file.by_type(ifc_class) - for element in ifcopenshell.geom.iterate( - settings, self.file, multiprocessing.cpu_count(), exclude=excluded_elements - ): - serialiser.write(element) - serialiser.finalize() - with open(svg_path, "w") as svg: - svg.write(buffer.get_value()) + files = [tool.Ifc.get()] + draw_settings = ifcopenshell.draw.draw_settings() + draw_settings.drawing_guid = tool.Ifc.get_entity(self.camera).GlobalId + draw_settings.css = False + draw_settings.auto_floorplan = False + draw_settings.scale = self.scale - def generate_linework_old(self, context, svg_path): - ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert") - subprocess.run( - [ - ifcconvert_path, - context.scene.BIMProperties.ifc_file, - "-yv", - svg_path, - "--plan", - "--model", - "--elevation-ref=DRAWING", - "--svg-xmlns", - "--svg-project", - "--svg-poly", - "--svg-without-storeys", - "--exclude", - "entities", - "IfcSpace", - "IfcOpeningElement", - "IfcDoor", - "IfcWindow", + iterators = [] + for f in files: + geom_settings = ifcopenshell.geom.settings(APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True) + body_contexts = [ + c.id() + for c in self.file.by_type("IfcGeometricRepresentationSubContext") + if c.ContextIdentifier in ["Body", "Facetation"] ] - ) + if body_contexts: + geom_settings.set_context_ids(body_contexts) + iterators.append( + ifcopenshell.geom.iterator(geom_settings, f, multiprocessing.cpu_count(), exclude=["IfcOpeningElement", "IfcWindow", "IfcDoor"]) + ) + + with open(svg_path, "w") as svg: + results = ifcopenshell.draw.main(draw_settings, files, iterators=iterators, merge_projection=False) + svg.write(str(results)) + return svg_path def generate_annotation(self, context): @@ -347,17 +323,7 @@ class CreateDrawing(bpy.types.Operator): camera = self.camera svg_writer = svgwriter.SvgWriter() - if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": - human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|") - else: - human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|") - - numerator, denominator = fraction.split("/") - - if camera.data.BIMCameraProperties.is_nts: - svg_writer.human_scale = "NTS" - else: - svg_writer.human_scale = human_scale + svg_writer.human_scale = self.human_scale drawing_style = context.scene.DocProperties.drawing_styles[ camera.data.BIMCameraProperties.active_drawing_style_index @@ -371,7 +337,7 @@ class CreateDrawing(bpy.types.Operator): height = camera.data.ortho_scale width = height / render.resolution_y * render.resolution_x - svg_writer.scale = float(numerator) / float(denominator) + svg_writer.scale = self.scale svg_writer.output = svg_path svg_writer.data_dir = context.scene.BIMProperties.data_dir svg_writer.vector_style = drawing_style.vector_style @@ -418,6 +384,19 @@ class CreateDrawing(bpy.types.Operator): svg_writer.write("annotation") return svg_writer.output + def get_scale(self): + if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": + self.human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|") + else: + self.human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|") + + if self.camera.data.BIMCameraProperties.is_nts: + self.human_scale = "NTS" + + numerator, denominator = fraction.split("/") + self.scale = float(numerator) / float(denominator) + + def is_landscape(self, render): return render.resolution_x > render.resolution_y @@ -787,12 +766,6 @@ class GenerateReferences(bpy.types.Operator): level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve", context) width_in_mm = width * 1000 - if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": - human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|") - else: - human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|") - numerator, denominator = fraction.split("/") - scale = float(numerator) / float(denominator) real_world_width_in_mm = width_in_mm * scale offset_in_mm = 20 offset_percentage = offset_in_mm / real_world_width_in_mm diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py index f87e4e6ee1..93ba539224 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py @@ -18,6 +18,8 @@ import os import bpy +import ifcopenshell +import blenderbim.tool as tool import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.decoration as decoration import enum @@ -53,6 +55,22 @@ def purge(): vector_styles_enum = [] +def update_diagram_scale(self, context): + scale = self.diagram_scale + if scale == "CUSTOM": + scale = self.custom_diagram_scale + scale = scale.split("|")[1] + element = tool.Ifc.get_entity(context.scene.camera) + if not element: + return + pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing") + if pset: + pset = tool.Ifc.get().by_id(pset["id"]) + else: + pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing") + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Scale": scale}) + + def get_diagram_scales(self, context): global diagram_scales_enum if ( @@ -294,8 +312,8 @@ class BIMCameraProperties(PropertyGroup): name="Target View", default="PLAN_VIEW", ) - diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale") - custom_diagram_scale: StringProperty(name="Custom Scale") + diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale) + custom_diagram_scale: StringProperty(name="Custom Scale", update=update_diagram_scale) raster_x: IntProperty(name="Raster X", default=1000) raster_y: IntProperty(name="Raster Y", default=1000) is_nts: BoolProperty(name="Is NTS") diff --git a/src/ifcopenshell-python/ifcopenshell/draw.py b/src/ifcopenshell-python/ifcopenshell/draw.py index b6f6a4f80c..e9528efd4a 100644 --- a/src/ifcopenshell-python/ifcopenshell/draw.py +++ b/src/ifcopenshell-python/ifcopenshell/draw.py @@ -57,23 +57,25 @@ class draw_settings: storey_heights: str = "none" include_entities: str = "" exclude_entities: str = "IfcOpeningElement" + drawing_guid: str = "" -def main(settings, files, progress_function=DO_NOTHING): - iterator_kwargs = {} - if settings.include_entities: - iterator_kwargs["include"] = settings.include_entities.split(",") - elif settings.exclude_entities: - iterator_kwargs["exclude"] = settings.exclude_entities.split(",") +def main(settings, files, iterators=None, merge_projection=True, progress_function=DO_NOTHING): + if not iterators: + iterator_kwargs = {} + if settings.include_entities: + iterator_kwargs["include"] = settings.include_entities.split(",") + elif settings.exclude_entities: + iterator_kwargs["exclude"] = settings.exclude_entities.split(",") - # We have to keep the iterator in memory because otherwise - # the styles are cleared up. - iterators = list( - map( - functools.partial(ifcopenshell.geom.iterator, SETTINGS, **iterator_kwargs), - files, + # We have to keep the iterator in memory because otherwise + # the styles are cleared up. + iterators = list( + map( + functools.partial(ifcopenshell.geom.iterator, SETTINGS, **iterator_kwargs), + files, + ) ) - ) def yield_from_iterator(it): if it.initialize(): @@ -90,6 +92,12 @@ def main(settings, files, progress_function=DO_NOTHING): if settings.auto_floorplan: sr.setSectionHeightsFromStoreys() + if settings.drawing_guid: + sr.setElevationRefGuid(settings.drawing_guid) + sr.setWithoutStoreys(True) + # If you want to filter by IfcAnnotation ObjectType named "DRAWING" + #sr.setElevationRef("DRAWING") + # required for svgfill sr.setPolygonal(True) sr.setUseNamespace(True) @@ -105,19 +113,21 @@ def main(settings, files, progress_function=DO_NOTHING): sr.setPrintSpaceAreas(settings.space_areas) sr.setDrawDoorArcs(settings.door_arcs) sr.setNoCSS(not settings.css) - + try: sh = ['none', 'full', 'left'].index(settings.storey_heights) sr.setDrawStoreyHeights(sh) except: raise ValueError("storey_heights should be one of {'none', 'full', 'left'}") - + """ # It is also possible to add drawing planes manually + import bpy + obj = bpy.context.active_object sr.addDrawing( - obj.matrix_world.transposed()[3][0:3], - -obj.matrix_world.transposed()[2][0:3], - -obj.matrix_world.transposed()[0][0:3], + obj.matrix_world.transposed()[3][0:3], # location + obj.matrix_world.transposed()[2][0:3], # z axis (view direction) + obj.matrix_world.transposed()[0][0:3], # x axis "Test", # drawing name True # include projection ) @@ -143,6 +153,9 @@ def main(settings, files, progress_function=DO_NOTHING): # Obtain SVG output from serializer buffer svg_data_1 = buffer.get_value() + if not merge_projection: + return svg_data_1 + # Parse SVG into vector of line segments # # The second argument 'projection' tells the parser to only include groups