First pass at connecting to new ifcopenshell.draw. See #1153.

This commit is contained in:
Dion Moult
2021-10-27 19:42:36 +11:00
parent 52e8ac386e
commit 2ad2a102cb
3 changed files with 94 additions and 90 deletions
@@ -31,6 +31,7 @@ import ifcopenshell.geom
import ifcopenshell.util.selector import ifcopenshell.util.selector
import ifcopenshell.util.representation import ifcopenshell.util.representation
import blenderbim.bim.schema import blenderbim.bim.schema
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.svgwriter as svgwriter import blenderbim.bim.module.drawing.svgwriter as svgwriter
import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.sheeter as sheeter 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.profile_code("Start drawing generation process")
self.props = context.scene.DocProperties self.props = context.scene.DocProperties
self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name
self.get_scale()
underlay_svg = self.generate_underlay(context) underlay_svg = self.generate_underlay(context)
self.profile_code("Generate underlay") self.profile_code("Generate underlay")
linework_svg = self.generate_linework(context) linework_svg = self.generate_linework(context)
@@ -190,7 +192,7 @@ class CreateDrawing(bpy.types.Operator):
elif "<defs>" in line: elif "<defs>" in line:
should_skip = True should_skip = True
continue continue
elif "</style>" in line: elif "</defs>" in line:
should_skip = False should_skip = False
continue continue
elif should_skip: 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") 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: if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
return svg_path return svg_path
# This is a work in progress. See #1153 and #1564. # This is a work in progress. See #1153 and #1564.
# Switch from old to new if you are testing v0.7.0 import ifcopenshell.draw
self.generate_linework_old(context, svg_path)
# self.generate_linework_new(svg_path)
return svg_path
def generate_linework_new(self, svg_path): files = [tool.Ifc.get()]
settings = ifcopenshell.geom.settings( draw_settings = ifcopenshell.draw.draw_settings()
APPLY_DEFAULT_MATERIALS=True, draw_settings.drawing_guid = tool.Ifc.get_entity(self.camera).GlobalId
DISABLE_TRIANGULATION=True, draw_settings.css = False
INCLUDE_CURVES=True, draw_settings.auto_floorplan = False
EXCLUDE_SOLIDS_AND_SURFACES=False, draw_settings.scale = self.scale
)
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())
def generate_linework_old(self, context, svg_path): iterators = []
ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert") for f in files:
subprocess.run( geom_settings = ifcopenshell.geom.settings(APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True)
[ body_contexts = [
ifcconvert_path, c.id()
context.scene.BIMProperties.ifc_file, for c in self.file.by_type("IfcGeometricRepresentationSubContext")
"-yv", if c.ContextIdentifier in ["Body", "Facetation"]
svg_path,
"--plan",
"--model",
"--elevation-ref=DRAWING",
"--svg-xmlns",
"--svg-project",
"--svg-poly",
"--svg-without-storeys",
"--exclude",
"entities",
"IfcSpace",
"IfcOpeningElement",
"IfcDoor",
"IfcWindow",
] ]
) 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 return svg_path
def generate_annotation(self, context): def generate_annotation(self, context):
@@ -347,17 +323,7 @@ class CreateDrawing(bpy.types.Operator):
camera = self.camera camera = self.camera
svg_writer = svgwriter.SvgWriter() svg_writer = svgwriter.SvgWriter()
if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": svg_writer.human_scale = self.human_scale
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
drawing_style = context.scene.DocProperties.drawing_styles[ drawing_style = context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index camera.data.BIMCameraProperties.active_drawing_style_index
@@ -371,7 +337,7 @@ class CreateDrawing(bpy.types.Operator):
height = camera.data.ortho_scale height = camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x 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.output = svg_path
svg_writer.data_dir = context.scene.BIMProperties.data_dir svg_writer.data_dir = context.scene.BIMProperties.data_dir
svg_writer.vector_style = drawing_style.vector_style svg_writer.vector_style = drawing_style.vector_style
@@ -418,6 +384,19 @@ class CreateDrawing(bpy.types.Operator):
svg_writer.write("annotation") svg_writer.write("annotation")
return svg_writer.output 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): def is_landscape(self, render):
return render.resolution_x > render.resolution_y 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) level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve", context)
width_in_mm = width * 1000 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 real_world_width_in_mm = width_in_mm * scale
offset_in_mm = 20 offset_in_mm = 20
offset_percentage = offset_in_mm / real_world_width_in_mm offset_percentage = offset_in_mm / real_world_width_in_mm
@@ -18,6 +18,8 @@
import os import os
import bpy import bpy
import ifcopenshell
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.annotation as annotation
import blenderbim.bim.module.drawing.decoration as decoration import blenderbim.bim.module.drawing.decoration as decoration
import enum import enum
@@ -53,6 +55,22 @@ def purge():
vector_styles_enum = [] 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): def get_diagram_scales(self, context):
global diagram_scales_enum global diagram_scales_enum
if ( if (
@@ -294,8 +312,8 @@ class BIMCameraProperties(PropertyGroup):
name="Target View", name="Target View",
default="PLAN_VIEW", default="PLAN_VIEW",
) )
diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale") diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale)
custom_diagram_scale: StringProperty(name="Custom Scale") custom_diagram_scale: StringProperty(name="Custom Scale", update=update_diagram_scale)
raster_x: IntProperty(name="Raster X", default=1000) raster_x: IntProperty(name="Raster X", default=1000)
raster_y: IntProperty(name="Raster Y", default=1000) raster_y: IntProperty(name="Raster Y", default=1000)
is_nts: BoolProperty(name="Is NTS") is_nts: BoolProperty(name="Is NTS")
+31 -18
View File
@@ -57,23 +57,25 @@ class draw_settings:
storey_heights: str = "none" storey_heights: str = "none"
include_entities: str = "" include_entities: str = ""
exclude_entities: str = "IfcOpeningElement" exclude_entities: str = "IfcOpeningElement"
drawing_guid: str = ""
def main(settings, files, progress_function=DO_NOTHING): def main(settings, files, iterators=None, merge_projection=True, progress_function=DO_NOTHING):
iterator_kwargs = {} if not iterators:
if settings.include_entities: iterator_kwargs = {}
iterator_kwargs["include"] = settings.include_entities.split(",") if settings.include_entities:
elif settings.exclude_entities: iterator_kwargs["include"] = settings.include_entities.split(",")
iterator_kwargs["exclude"] = settings.exclude_entities.split(",") elif settings.exclude_entities:
iterator_kwargs["exclude"] = settings.exclude_entities.split(",")
# We have to keep the iterator in memory because otherwise # We have to keep the iterator in memory because otherwise
# the styles are cleared up. # the styles are cleared up.
iterators = list( iterators = list(
map( map(
functools.partial(ifcopenshell.geom.iterator, SETTINGS, **iterator_kwargs), functools.partial(ifcopenshell.geom.iterator, SETTINGS, **iterator_kwargs),
files, files,
)
) )
)
def yield_from_iterator(it): def yield_from_iterator(it):
if it.initialize(): if it.initialize():
@@ -90,6 +92,12 @@ def main(settings, files, progress_function=DO_NOTHING):
if settings.auto_floorplan: if settings.auto_floorplan:
sr.setSectionHeightsFromStoreys() 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 # required for svgfill
sr.setPolygonal(True) sr.setPolygonal(True)
sr.setUseNamespace(True) sr.setUseNamespace(True)
@@ -105,19 +113,21 @@ def main(settings, files, progress_function=DO_NOTHING):
sr.setPrintSpaceAreas(settings.space_areas) sr.setPrintSpaceAreas(settings.space_areas)
sr.setDrawDoorArcs(settings.door_arcs) sr.setDrawDoorArcs(settings.door_arcs)
sr.setNoCSS(not settings.css) sr.setNoCSS(not settings.css)
try: try:
sh = ['none', 'full', 'left'].index(settings.storey_heights) sh = ['none', 'full', 'left'].index(settings.storey_heights)
sr.setDrawStoreyHeights(sh) sr.setDrawStoreyHeights(sh)
except: except:
raise ValueError("storey_heights should be one of {'none', 'full', 'left'}") raise ValueError("storey_heights should be one of {'none', 'full', 'left'}")
""" """
# It is also possible to add drawing planes manually # It is also possible to add drawing planes manually
import bpy
obj = bpy.context.active_object
sr.addDrawing( sr.addDrawing(
obj.matrix_world.transposed()[3][0:3], obj.matrix_world.transposed()[3][0:3], # location
-obj.matrix_world.transposed()[2][0:3], obj.matrix_world.transposed()[2][0:3], # z axis (view direction)
-obj.matrix_world.transposed()[0][0:3], obj.matrix_world.transposed()[0][0:3], # x axis
"Test", # drawing name "Test", # drawing name
True # include projection True # include projection
) )
@@ -143,6 +153,9 @@ def main(settings, files, progress_function=DO_NOTHING):
# Obtain SVG output from serializer buffer # Obtain SVG output from serializer buffer
svg_data_1 = buffer.get_value() svg_data_1 = buffer.get_value()
if not merge_projection:
return svg_data_1
# Parse SVG into vector of line segments # Parse SVG into vector of line segments
# #
# The second argument 'projection' tells the parser to only include <g> groups # The second argument 'projection' tells the parser to only include <g> groups