diff --git a/src/blenderbim/blenderbim/bim/data/templates/titleblocks/A1.svg b/src/blenderbim/blenderbim/bim/data/templates/titleblocks/A1.svg
index de252c4203..ec48691774 100644
--- a/src/blenderbim/blenderbim/bim/data/templates/titleblocks/A1.svg
+++ b/src/blenderbim/blenderbim/bim/data/templates/titleblocks/A1.svg
@@ -173,7 +173,7 @@
style="stroke-width:0.999999px"
y="1036.1166"
x="3030.9653"
- id="tspan2527">{{revision}}
+ id="tspan2527">{{Revision}}
{{number}}
+ style="stroke-width:0.999999px">{{Identification}} - {{Name}}
{{revision}}
+ id="tspan2527">{{Revision}}
{{number}}
+ style="stroke-width:0.264583px">{{Identification}} - {{Name}}
{{revision}}
+ style="stroke-width:0.264583px">{{Revision}}
{{number}}
+ id="tspan2531">{{Identification}} - {{Name}}
{{no}}
+ id="tspan857">{{Identification}}
{{name}}
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325247px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:start;text-anchor:start;stroke-width:0.26458329px">{{Name}}
{{scale}}
+ id="tspan865">{{Scale}}
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
index 34b8cdfcd2..aeadfaff80 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py
@@ -635,14 +635,8 @@ class RemoveDrawingFromSheet(bpy.types.Operator):
def execute(self, context):
reference = tool.Ifc.get().by_id(self.reference)
- if tool.Ifc.get_schema() == "IFC2X3":
- sheet = reference.ReferenceToDocument[0]
- drawing = [r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference][
- 0
- ].RelatedObjects[0]
- else:
- sheet = reference.ReferencedDocument
- drawing = reference.DocumentRefForObjects[0].RelatedObjects[0]
+ sheet = tool.Drawing.get_reference_sheet(reference)
+ drawing = tool.Drawing.get_reference_element(reference)
tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
tool.Ifc.run("document.remove_reference", reference=reference)
@@ -667,10 +661,11 @@ class CreateSheets(bpy.types.Operator):
scene = context.scene
props = scene.DocProperties
active_sheet = props.sheets[props.active_sheet_index]
- name = active_sheet.name
+ sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
+ name = tool.Drawing.get_sheet_filename(sheet)
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = scene.BIMProperties.data_dir
- sheet_builder.build(name)
+ sheet_builder.build(sheet)
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
index 07e5b9b626..c56e6b9262 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py
@@ -72,8 +72,10 @@ 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 in scale:
+ return
+ human_scale, scale = scale.split("|")
+ element = tool.Ifc.get_entity(context.active_object)
if not element:
return
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
@@ -81,7 +83,9 @@ def update_diagram_scale(self, context):
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})
+ ifcopenshell.api.run(
+ "pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Scale": scale, "HumanScale": human_scale}
+ )
def get_diagram_scales(self, context):
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
index 317a76e83a..2a8229b660 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py
@@ -67,7 +67,7 @@ class SheetBuilder:
drawing_dir = os.path.join(self.data_dir, "diagrams")
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
drawing_path = os.path.join(drawing_dir, filename + ".svg")
- underlay_path = os.path.join(drawing_dir, filename + ".png")
+ underlay_path = os.path.join(drawing_dir, filename + "-underlay.png")
if not os.path.isfile(sheet_path):
raise FileNotFoundError
@@ -92,6 +92,7 @@ class SheetBuilder:
if os.path.isfile(underlay_path):
background = ET.SubElement(view, "image")
+ background.attrib["data-type"] = "background"
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
background.attrib["x"] = "30"
background.attrib["y"] = "30"
@@ -100,6 +101,7 @@ class SheetBuilder:
if os.path.isfile(drawing_path):
foreground = ET.SubElement(view, "image")
+ foreground.attrib["data-type"] = "foreground"
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
foreground.attrib["x"] = "30"
foreground.attrib["y"] = "30"
@@ -119,9 +121,7 @@ class SheetBuilder:
sheet_tree = ET.parse(sheet_path)
sheet_root = sheet_tree.getroot()
- print('removing drawing', sheet_root.findall("{http://www.w3.org/2000/svg}g"))
for g in sheet_root.findall("{http://www.w3.org/2000/svg}g"):
- print('checking g', g)
if g.attrib["data-type"] == "drawing" and g.attrib["data-guid"] == drawing.GlobalId:
sheet_root.remove(g)
break
@@ -160,13 +160,15 @@ class SheetBuilder:
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg"))
title_root = title_tree.getroot()
title = ET.SubElement(parent, "image")
+ title.attrib["data-type"] = "view-title"
title.attrib["xlink:href"] = "../templates/view-title.svg"
title.attrib["x"] = str(x)
title.attrib["y"] = str(y)
title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width")))
title.attrib["height"] = str(self.convert_to_mm(title_root.attrib.get("height")))
- def build(self, sheet_name):
+ def build(self, sheet):
+ sheet_name = tool.Drawing.get_sheet_filename(sheet)
os.makedirs(os.path.join(self.data_dir, "build", sheet_name), exist_ok=True)
sheet_path = os.path.join(self.data_dir, "sheets", f"{sheet_name}.svg")
@@ -177,48 +179,60 @@ class SheetBuilder:
tree = ET.parse(sheet_path)
root = tree.getroot()
- titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
- image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
- titleblock.append(self.parse_embedded_svg(image, {"number": sheet_name, "revision": "A"}))
- titleblock.remove(image)
-
- self.group_number = 1
- self.build_drawings(root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'), sheet_name)
+ self.build_titleblock(root, sheet)
+ self.build_drawings(root, sheet)
self.build_schedules(root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'))
with open(os.path.join(self.data_dir, "build", sheet_name, f"{sheet_name}.svg"), "wb") as output:
tree.write(output)
- def build_drawings(self, drawings, sheet_name):
- for view in drawings:
+ def build_titleblock(self, root, sheet):
+ titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
+ image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
+ titleblock.append(self.parse_embedded_svg(image, sheet.get_info()))
+ titleblock.remove(image)
+
+ def build_drawings(self, root, sheet):
+ sheet_name = tool.Drawing.get_sheet_filename(sheet)
+ references = tool.Drawing.get_document_references(sheet)
+ drawing_references = {tool.Drawing.get_reference_element(r): r for r in references}
+
+ for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
+ drawing = [d for d in drawing_references.keys() if d and d.GlobalId == view.attrib["data-guid"]][0]
+ reference = drawing_references[drawing]
+
images = view.findall("{http://www.w3.org/2000/svg}image")
- background = images[0]
- foreground = images[1]
- view_title = images[2]
- self.scale = "NTS"
- # Add foreground
- view.append(self.parse_embedded_svg(foreground, {}))
+ background = None
+ foreground = None
+ view_title = None
- # Add background
- background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
+ for image in images:
+ if image.attrib["data-type"] == "background":
+ background = image
+ elif image.attrib["data-type"] == "foreground":
+ foreground = image
+ elif image.attrib["data-type"] == "view-title":
+ view_title = image
- copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
+ if foreground is not None:
+ view.append(self.parse_embedded_svg(foreground, {}))
- # Add view title
- foreground_path = self.get_href(foreground)
- view.append(
- self.parse_embedded_svg(
- view_title,
- {"no": self.group_number, "name": ntpath.basename(foreground_path)[0:-4], "scale": self.scale},
- )
- )
+ if background is not None:
+ background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
+ copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
+
+ if view_title is not None:
+ foreground_path = self.get_href(foreground)
+ data = reference.get_info()
+ if not data["Name"]:
+ data["Name"] = ntpath.basename(foreground_path)[0:-4]
+ data["Scale"] = tool.Drawing.get_drawing_human_scale(drawing)
+ view.append(self.parse_embedded_svg(view_title, data))
for image in images:
view.remove(image)
- self.group_number += 1
-
def build_schedules(self, schedules):
for group in schedules:
images = group.findall("{http://www.w3.org/2000/svg}image")
@@ -231,15 +245,13 @@ class SheetBuilder:
path = self.get_href(schedule)
group.append(
self.parse_embedded_svg(
- group_title, {"no": self.group_number, "name": ntpath.basename(path)[0:-4], "scale": self.scale}
+ group_title, {"no": 1, "name": ntpath.basename(path)[0:-4], "scale": self.scale}
)
)
for image in images:
group.remove(image)
- self.group_number += 1
-
def get_href(self, element):
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href"))
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
index b13656735c..b7e3b80a6e 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
@@ -453,9 +453,23 @@ class SvgWriter:
(symbol_position * self.scale)[1],
)
- self.svg.add(self.svg.use("#section-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
+ self.svg.add(
+ self.svg.use("#section-arrow", insert=tuple(symbol_position * self.scale), transform=transform)
+ )
self.svg.add(self.svg.use("#section-tag", insert=tuple(symbol_position * self.scale)))
+ reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
+ text_position = list(symbol_position * self.scale)
+ text_style = {
+ "font-size": annotation.Annotator.get_svg_text_size(2.5),
+ "font-family": "OpenGost Type B TT",
+ "text-anchor": "middle",
+ "alignment-baseline": "middle",
+ "dominant-baseline": "middle",
+ }
+ self.svg.add(self.svg.text(reference_id, insert=(text_position[0], text_position[1] - 2.5), **text_style))
+ self.svg.add(self.svg.text(sheet_id, insert=(text_position[0], text_position[1] + 2.5), **text_style))
+
def draw_elevation_annotation(self, obj):
x_offset = self.raw_width / 2
y_offset = self.raw_height / 2
@@ -475,6 +489,34 @@ class SvgWriter:
self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.scale)))
+ reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
+ text_position = list(symbol_position * self.scale)
+ text_style = {
+ "font-size": annotation.Annotator.get_svg_text_size(2.5),
+ "font-family": "OpenGost Type B TT",
+ "text-anchor": "middle",
+ "alignment-baseline": "middle",
+ "dominant-baseline": "middle",
+ }
+ self.svg.add(self.svg.text(reference_id, insert=(text_position[0], text_position[1] - 2.5), **text_style))
+ self.svg.add(self.svg.text(sheet_id, insert=(text_position[0], text_position[1] + 2.5), **text_style))
+
+ def get_reference_and_sheet_id_from_annotation(self, element):
+ reference_id = "-"
+ sheet_id = "-"
+ drawing = tool.Drawing.get_annotation_element(element)
+ reference = tool.Drawing.get_drawing_reference(drawing)
+ if reference:
+ sheet = tool.Drawing.get_reference_sheet(reference)
+ if sheet:
+ if tool.Ifc.get_schema() == "IFC2X3":
+ reference_id = reference.ItemReference or "-"
+ sheet_id = sheet.DocumentId or "-"
+ else:
+ reference_id = reference.Identification or "-"
+ sheet_id = sheet.Identification or "-"
+ return (reference_id, sheet_id)
+ return ("-", "-")
def draw_text_annotation(self, text_obj, position):
x_offset = self.raw_width / 2
@@ -502,7 +544,7 @@ class SvgWriter:
if text_obj.BIMTextProperties.symbol != "None":
self.svg.add(
- self.svg.use("#{}".format(text_obj.BIMTextProperties.symbol), insert=tuple(text_position * self.scale))
+ self.svg.use(f"#{text_obj.BIMTextProperties.symbol}", insert=tuple(text_position * self.scale))
)
if text_literal.BoxAlignment == "top-left":
@@ -533,11 +575,15 @@ class SvgWriter:
alignment_baseline = "baseline"
text_anchor = "end"
- text_body = text_literal.Literal
- if text_obj.name in self.annotations.get("template_variables", {}):
- text_body = pystache.render(text_body, self.annotations["template_variables"][text_obj.name])
+ literal = text_literal.Literal
- for line_number, text_line in enumerate(text_body.split("\n")):
+ product = tool.Drawing.get_text_product(element)
+ selector = ifcopenshell.util.selector.Selector
+ variables = {}
+ for variable in re.findall("{{.*?}}", literal):
+ literal = literal.replace(variable, selector.get_element_value(product, variable[2:-2]) or "")
+
+ for line_number, text_line in enumerate(literal.replace("\\n", "\n").split("\n")):
self.svg.add(
self.svg.text(
text_line,
diff --git a/src/blenderbim/blenderbim/bim/module/project/__init__.py b/src/blenderbim/blenderbim/bim/module/project/__init__.py
index 9ef8ba8b39..7898d1ddf0 100644
--- a/src/blenderbim/blenderbim/bim/module/project/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/project/__init__.py
@@ -28,6 +28,7 @@ classes = (
operator.UnlinkIfc,
operator.UnloadLink,
operator.LoadLink,
+ operator.ToggleLinkVisibility,
operator.SelectLibraryFile,
operator.ChangeLibraryElement,
operator.RefreshLibrary,
diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py
index f536698149..17ddfdc7bc 100644
--- a/src/blenderbim/blenderbim/bim/module/project/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/project/operator.py
@@ -685,10 +685,31 @@ class LoadLink(bpy.types.Operator):
continue
bpy.data.scenes[0].collection.children.link(child)
link = context.scene.BIMProjectProperties.links.get(filepath)
+ link.collection_name = child.name
link.is_loaded = True
return {"FINISHED"}
+class ToggleLinkVisibility(bpy.types.Operator):
+ bl_idname = "bim.toggle_link_visibility"
+ bl_label = "Toggle Link Visibility"
+ bl_options = {"REGISTER", "UNDO"}
+ bl_description = "Toggle visibility between SOLID and WIREFRAME"
+ collection_name: bpy.props.StringProperty()
+
+ def execute(self, context):
+ collection_name = self.collection_name
+ objs = filter(lambda obj: "IfcOpeningElement" not in obj.name, bpy.data.collections[collection_name].all_objects)
+ for i,obj in enumerate(objs):
+ if i == 0:
+ if obj.display_type == "WIRE":
+ display_type = "TEXTURED"
+ else:
+ display_type = "WIRE"
+ obj.display_type = display_type
+ return {"FINISHED"}
+
+
class ExportIFC(bpy.types.Operator):
bl_idname = "export_ifc.bim"
bl_label = "Export IFC"
diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py
index c04121aea8..fe49738432 100644
--- a/src/blenderbim/blenderbim/bim/module/project/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/project/prop.py
@@ -85,6 +85,7 @@ class FilterCategory(PropertyGroup):
class Link(PropertyGroup):
name: StringProperty(name="Name")
+ collection_name: StringProperty(name="Collection Name")
is_loaded: BoolProperty(name="Is Loaded", default=False)
diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py
index 271ce2de55..e90c818113 100644
--- a/src/blenderbim/blenderbim/bim/module/project/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/project/ui.py
@@ -286,6 +286,8 @@ class BIM_UL_links(UIList):
row = layout.row(align=True)
if item.is_loaded:
row.label(text=item.name)
+ op = row.operator("bim.toggle_link_visibility", text="", icon="HIDE_OFF")
+ op.collection_name = item.collection_name
op = row.operator("bim.unload_link", text="", icon="UNLINKED")
op.filepath = item.name
else:
diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py
index ac43fd3323..845d35ed89 100644
--- a/src/blenderbim/blenderbim/core/drawing.py
+++ b/src/blenderbim/blenderbim/core/drawing.py
@@ -115,7 +115,7 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
ifc.run("group.assign_group", group=group, product=element)
collector.assign(camera)
pset = ifc.run("pset.add_pset", product=element, name="EPset_Drawing")
- ifc.run("pset.edit_pset", pset=pset, properties={"TargetView": target_view, "Scale": "1/100"})
+ ifc.run("pset.edit_pset", pset=pset, properties={"TargetView": target_view, "Scale": "1/100", "HumanScale": "1:100"})
drawing.import_drawings()
diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py
index 5dd7d8022a..8926ae9b39 100644
--- a/src/blenderbim/blenderbim/tool/drawing.py
+++ b/src/blenderbim/blenderbim/tool/drawing.py
@@ -201,7 +201,7 @@ class Drawing(blenderbim.core.tool.Drawing):
name = document.Identification or "X"
else:
name = document.DocumentId or "X"
- name += " - " + document.Name or "Unnamed"
+ name += " - " + (document.Name or "Unnamed")
return name
@classmethod
@@ -291,24 +291,17 @@ class Drawing(blenderbim.core.tool.Drawing):
if not new.is_expanded:
continue
- if tool.Ifc.get_schema() == "IFC2X3":
- references = sheet.DocumentReferences or []
- else:
- references = sheet.HasDocumentReferences or []
-
- for reference in references:
+ for reference in cls.get_document_references(sheet):
new = props.sheets.add()
new.ifc_definition_id = reference.id()
new.is_sheet = False
if tool.Ifc.get_schema() == "IFC2X3":
new.identification = reference.ItemReference or "X"
- element = [
- r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference
- ][0].RelatedObjects[0]
else:
new.identification = reference.Identification or "X"
- element = reference.DocumentRefForObjects[0].RelatedObjects[0]
+
+ element = cls.get_reference_element(reference)
new.name = element.Name
new.reference_type = "DRAWING"
@@ -720,3 +713,39 @@ class Drawing(blenderbim.core.tool.Drawing):
if grid_obj.matrix_world != obj.matrix_world:
bpy.ops.bim.update_representation(obj=obj.name)
tool.Geometry.record_object_position(obj)
+
+ @classmethod
+ def get_document_references(cls, document):
+ if tool.Ifc.get_schema() == "IFC2X3":
+ return document.DocumentReferences or []
+ return document.HasDocumentReferences or []
+
+ @classmethod
+ def get_reference_element(cls, reference):
+ if tool.Ifc.get_schema() == "IFC2X3":
+ return [
+ r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference
+ ][0].RelatedObjects[0]
+ return reference.DocumentRefForObjects[0].RelatedObjects[0]
+
+ @classmethod
+ def get_drawing_human_scale(cls, drawing):
+ return ifcopenshell.util.element.get_psets(drawing)["EPset_Drawing"]["HumanScale"]
+
+ @classmethod
+ def get_annotation_element(cls, element):
+ for rel in element.HasAssignments:
+ if rel.is_a("IfcRelAssignsToProduct"):
+ return rel.RelatingProduct
+
+ @classmethod
+ def get_drawing_reference(cls, drawing):
+ for rel in drawing.HasAssociations:
+ if rel.is_a("IfcRelAssociatesDocument"):
+ return rel.RelatingDocument
+
+ @classmethod
+ def get_reference_sheet(cls, reference):
+ if tool.Ifc.get_schema() == "IFC2X3":
+ return reference.ReferenceToDocument[0]
+ return reference.ReferencedDocument
diff --git a/src/blenderbim/docs/blenderbim/installation.rst b/src/blenderbim/docs/blenderbim/installation.rst
index 0dc22f574b..dd13288af0 100644
--- a/src/blenderbim/docs/blenderbim/installation.rst
+++ b/src/blenderbim/docs/blenderbim/installation.rst
@@ -5,14 +5,14 @@ Installation
There are different methods of installation, depending on your situation.
-1. **Packaged installation** is recommended for regular users.
-2. **Daily build installation** is recommended for power users helping with testing.
-3. **Unpackaged installation** is recommended for package managers.
-4. **Distro installation** is recommended for those who use a Linux package manager.
-5. **Source installation** is recommended for developers.
+1. **Stable installation** is recommended for regular users.
+2. **Unstable installation** is recommended for power users helping with testing.
+3. **Building from source** is recommended for distributing a build from source.
+4. **Live development environment** is recommended for developers who are actively coding.
+5. **Distro installation** is recommended for those who use a Linux package manager.
-Packaged installation
----------------------
+Stable installation
+-------------------
The BlenderBIM Add-on is packaged like a regular Blender add-on, so installation
is the same as any other Blender add-on. The full instructions for end-user
@@ -24,25 +24,129 @@ Like all Blender add-ons, they can be installed using ``Edit > Preferences >
Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox``. You can
enable add-ons permanently by using ``Save User Settings`` from the Addons menu.
-Daily build installation
-------------------------
+Unstable installation
+---------------------
-Daily builds are almost the same as **Packaged installation**, except that they
-are typically updated every day. Simply download a daily build from the `Github
-releases page `__, then
-follow the same instructions as a packaged installation.
+**Unstable installation** is almost the same as **Stable installation**, except
+that they are typically updated every day. Simply download a daily build from
+the `Github releases page
+`__, then follow the same
+instructions as a packaged installation.
-You will need to choose which daily build to download.
+You will need to choose which build to download.
+- If you are on Blender >=3.1, choose py310
+- If you are on Blender >=2.93 and <3.1, choose py39
- If you are on Blender <2.93, choose py37
-- If you are on Blender >=2.93, choose py39
- Choose linux, macos, or win depending on your operating system
-Daily builds are not always stable. Sometimes, a build may be delayed, or
-contain broken code. We try to avoid this, but it happens.
+Sometimes, a build may be delayed, or contain broken code. We try to avoid this,
+but it happens.
-Unpackaged installation
------------------------
+Building from source
+--------------------
+
+It is possible to run the latest bleeding edge version of BlenderBIM without
+having to wait for an official release, since BlenderBIM is coded in Python and
+doesn't require any compilation.
+
+Note that the BlenderBIM Add-on does depend on IfcOpenShell, and IfcOpenShell
+does require compilation. The following instructions will use a pre-built
+IfcOpenShell (using an IfcOpenBot build) for convenience. Instructions on how to
+compile IfcOpenShell is out of scope of this document.
+
+You can create your own package by using the Makefile as shown below. You can
+choose between a ``PLATFORM`` of ``linux``, ``macos``, and ``win``. You can
+choose between a ``PYVERSION`` of ``py39``, ``py37``, or ``py310``.
+::
+
+ $ cd src/blenderbim
+ $ make dist PLATFORM=linux PYVERSION=py310
+ $ ls dist/
+
+This will give you a fully packaged Blender add-on zip that you can distribute
+and install.
+
+Live development environment
+----------------------------
+
+One option for developers who want to actively develop from source is to follow
+the instructions from **Building from source**. However, creating a build,
+uninstalling the old add-on, and installing a new build is a slow process.
+Although it works, it is very slow, so we do not recommend it.
+
+A more rapid approach is to follow the **Unstable installation** method, as this
+provides all dependencies for you out of the box. Then, we can replace certain
+Python files that tend to be updated frequently with those from the Git
+repository. We're going to use symlinks (Windows users can use ``mklink``), so
+we can code in our Git repository, and see the changes in our Blender
+installation (you will need to restart Blender to see changes).
+
+In addition, we're also going to replace the Python code of the IfcOpenShell
+dependency with our Git repository, since most of the BlenderBIM Add-on
+functionality is agnostic of Blender, and is actually part of IfcOpenShell.
+Therefore, we need to keep this dependency highly updated as well.
+
+The downside with this approach is that if a new dependency is added, or a
+compiled dependency version requirement has changed, or the build system
+changes, you'll need to fix your setup manually. But this is relatively rare.
+
+::
+
+ $ git clone https://github.com/IfcOpenShell/IfcOpenShell.git
+ $ cd IfcOpenShell
+
+ # Remove the Blender add-on Python code
+ $ rm -r /path/to/blender/X.XX/scripts/addons/blenderbim/bim/
+
+ # Replace them with links to the Git repository
+ $ ln -s src/blenderbim/blenderbim/bim /path/to/blender/X.XX/scripts/addons/blenderbim/bim
+
+ # Remove the IfcOpenShell dependency Python code
+ $ rm -r /path/to/blender/X.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/api
+ $ rm -r /path/to/blender/X.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/util
+
+ # Replace them with links to the Git repository
+ $ ln -s src/ifcopenshell-python/ifcopenshell/api /path/to/blender/X.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/api
+ $ ln -s src/ifcopenshell-python/ifcopenshell/util /path/to/blender/X.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/util
+
+On Windows:
+
+::
+
+ $ git clone https://github.com/IfcOpenShell/IfcOpenShell.git
+ $ cd IfcOpenShell
+
+ # Remove the Blender add-on Python code
+ $ rd /S /Q "\path\to\blender\X.XX\scripts\addons\blenderbim\bim\"
+
+ # Replace them with links to the Git repository
+ $ mklink /D "\path\to\blender\X.XX\scripts\addons\blenderbim\bim" "src\blenderbim\blenderbim\bim"
+
+ # Remove the IfcOpenShell dependency Python code
+ $ rd \S \Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api"
+ $ rd \S \Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util"
+
+ # Replace them with links to the Git repository
+ $ mklink \D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api" "src\ifcopenshell-python\ifcopenshell\api"
+ $ mklink \D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util" "src\ifcopenshell-python\ifcopenshell\util"
+
+
+After you modify your code in the Git repository, you will need to restart
+Blender for the changes to take effect. In ``Edit > Preferences > Add-ons`` you
+will see that the version number of the BlenderBIM Add-on has changed to
+``0.0.999999``, which represents an un-versioned BlenderBIM Add-on.
+
+Distro installation
+-------------------
+
+Those on Arch Linux can check out this `AUR package `__.
+
+Tips for package managers
+-------------------------
+
+If you are interested in packaging the BlenderBIM Add-on for a packaging
+manager, read on.
The BlenderBIM Add-on is fully contained in the ``blenderbim/`` subfolder of the
Blender add-ons directory. This is typically distributed as a zipfile as per
@@ -58,9 +162,8 @@ This corresponds to the structure found in the source code `here
`__.
The BlenderBIM Add-on is complex, and requires many dependencies, including
-Python modules, binaries, and static assets. These dependencies are bundled with
-the add-on for convenience in the **Packaged installation** and **Daily build
-installation** methods.
+Python modules, binaries, and static assets. When packaged for users, these
+dependencies are bundled with the add-on for convenience.
If you choose to install the BlenderBIM Add-on and use your own system
dependencies, the source of truth for how dependencies are bundled are found in
@@ -92,7 +195,7 @@ Required Python modules to be stored in ``libs/site/packages/`` are:
elementpath
six
lark-parser
- fcl
+ hppfcl
behave
parse
parse_type
@@ -108,14 +211,8 @@ Required Python modules to be stored in ``libs/site/packages/`` are:
Notes:
1. ``ifcopenshell`` almost always requires the latest version due to the fast paced nature of the add-on development.
-2. ``fcl`` is not bundled for MacOS, due to lack of maintained community build. This is required for clash detection.
-3. ``behave`` requires `patches `__.
-4. ``ifcjson`` can be found `here `__.
-
-Required binaries are:
-::
-
- libs/IfcConvert
+2. ``behave`` requires `patches `__.
+3. ``ifcjson`` can be found `here `__.
Required static assets are:
::
@@ -123,121 +220,31 @@ Required static assets are:
bim/data/gantt/jsgantt.js (from jsgantt-improved)
bim/data/gantt/jsgantt.css (from jsgantt-improved)
-Distro installation
--------------------
-
-Those on Arch Linux can check out this `AUR package `__.
-
-Source installation
--------------------
-
-It is possible to run the latest bleeding edge version of BlenderBIM without
-having to wait for an official release, since BlenderBIM is coded in Python and
-doesn't require any compilation.
-
-Note that the BlenderBIM Add-on does depend on IfcOpenShell, and IfcOpenShell
-does require compilation. The following instructions will use a pre-built
-IfcOpenShell (using an IfcOpenBot build) for convenience. Instructions on how to
-compile IfcOpenShell is out of scope of this document.
-
-You can create your own package by using the Makefile as shown below. You can
-choose between a ``PLATFORM`` of ``linux``, ``macos``, and ``win``. You can
-choose between a ``PYVERSION`` of ``py39`` and ``py37``.
-::
-
- $ cd src/blenderbim
- $ make dist PLATFORM=linux PYVERSION=py39
- $ ls dist/
-
-However, creating a build, uninstalling the old add-on, and installing a new
-build is a slow process. You can do this, but we do not recommend it. A more
-rapid approach is to follow the **Daily build installation** method, as this
-provides all dependencies for you out of the box. Then, we can replace certain
-Python files that tend to be updated frequently with those from the Git
-repository. We're going to use symlinks (Windows users can use ``mklink``), so
-we can code in our Git repository, and see the changes in our Blender
-installation.
-
-In addition, we're also going to replace the Python code of the IfcOpenShell
-dependency with our Git repository, since most of the BlenderBIM Add-on
-functionality is agnostic of Blender, and is actually part of IfcOpenShell.
-Therefore, we need to keep this dependency highly updated as well.
-
-The downside with this approach is that if a new dependency is added, or a
-compiled dependency version requirement has changed, or the build system
-changes, you'll need to fix your setup manually. But this is relatively rare.
-
-::
-
- $ git clone https://github.com/IfcOpenShell/IfcOpenShell.git
- $ cd IfcOpenShell
-
- # Remove the Blender add-on Python code
- $ rm -r /path/to/blender/2.XX/scripts/addons/blenderbim/bim/
-
- # Replace them with links to the Git repository
- $ ln -s src/blenderbim/blenderbim/bim /path/to/blender/2.XX/scripts/addons/blenderbim/bim
-
- # Remove the IfcOpenShell dependency Python code
- $ rm -r /path/to/blender/2.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/api
- $ rm -r /path/to/blender/2.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/util
-
- # Replace them with links to the Git repository
- $ ln -s src/ifcopenshell-python/ifcopenshell/api /path/to/blender/2.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/api
- $ ln -s src/ifcopenshell-python/ifcopenshell/util /path/to/blender/2.XX/scripts/addons/blenderbim/libs/site/packages/ifcopenshell/util
-
-On Windows:
-
-::
-
- $ git clone https://github.com/IfcOpenShell/IfcOpenShell.git
- $ cd IfcOpenShell
-
- # Remove the Blender add-on Python code
- $ rd /S /Q "\path\to\blender\2.XX\scripts\addons\blenderbim\bim\"
-
- # Replace them with links to the Git repository
- $ mklink /D "\path\to\blender\2.XX\scripts\addons\blenderbim\bim" "src\blenderbim\blenderbim\bim"
-
- # Remove the IfcOpenShell dependency Python code
- $ rd \S \Q "\path\to\blender\2.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api"
- $ rd \S \Q "\path\to\blender\2.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util"
-
- # Replace them with links to the Git repository
- $ mklink \D "\path\to\blender\2.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api" "src\ifcopenshell-python\ifcopenshell\api"
- $ mklink \D "\path\to\blender\2.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util" "src\ifcopenshell-python\ifcopenshell\util"
-
-
-After you modify your code in the Git repository, you will need to restart
-Blender for the changes to take effect. In ``Edit > Preferences > Add-ons`` you
-will see that the version number of BlenderBIM has changed to ``0.0.999999``,
-which represents an un-versioned BlenderBIM.
-
Where is the BlenderBIM Add-on installed?
-----------------------------------------
If you downloaded Blender as a ``.zip`` file without running an installer, you
will find the BlenderBIM Add-on installed in the following directory, where
-``2.XX`` is the Blender version:
+``X.XX`` is the Blender version:
::
- /path/to/blender/2.XX/scripts/addons/
+ /path/to/blender/X.XX/scripts/addons/
Otherwise, if you installed Blender using an installation package, the add-ons
folder depends on which operating system you use. On Linux:
::
- ~/.config/blender/2.XX/scripts/addons/
+ ~/.config/blender/X.XX/scripts/addons/
On Mac:
::
- /Users/{YOUR_USER}/Library/Application Support/Blender/2.XX/
+ /Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/
On Windows:
::
- C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\2.XX\scripts\addons
+ C:\Users\{YOUR_USER}\AppData\Roaming\Blender Foundation\X.XX\scripts\addons
Upon installation, the BlenderBIM Add-on is stored in the ``blenderbim/``
directory.
@@ -264,8 +271,10 @@ FAQ
1. I get an error similar to "ImportError: IfcOpenShell not built for 'linux/64bit/python3.7'"
-If you are using Blender <2.93, then you need to use a daily build. See the
-instructions above for a daily build installation.
+Check which BlenderBIM Add-on build you are using. The zip will have either
+``py37``, ``py39``, or ``py310`` in the name. See the instructions in the
+**Unstable installation** section to check that you have installed the correct
+version.
2. I am on Ubuntu and get an error similar to "ImportError: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found"
diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h
index 687a437c4c..69a04f1339 100644
--- a/src/ifcgeom/IfcGeom.h
+++ b/src/ifcgeom/IfcGeom.h
@@ -111,6 +111,18 @@ public:
std::map Shape;
};
+namespace util {
+ template
+ typename std::enable_if::value, T&>::type conditional_address_of(T& t) {
+ return t;
+ }
+
+ template
+ typename std::enable_if::value, T*>::type conditional_address_of(T& t) {
+ return &t;
+ }
+}
+
class IFC_GEOM_API MAKE_TYPE_NAME(Kernel) : public IfcGeom::Kernel {
private:
@@ -126,7 +138,7 @@ private:
class faceset_helper {
private:
MAKE_TYPE_NAME(Kernel)* kernel_;
- std::set duplicates_;
+ std::set::value, LP, const LP*>::type> duplicates_;
std::map vertex_mapping_;
std::map, TopoDS_Edge> edges_;
// not always in use
@@ -225,8 +237,8 @@ private:
return true;
}
- bool wire(LP loop, TopoDS_Wire& wire) {
- if (duplicates_.find(loop) != duplicates_.end()) {
+ bool wire(const LP& loop, TopoDS_Wire& wire) {
+ if (duplicates_.find(util::conditional_address_of(loop)) != duplicates_.end()) {
return false;
}
BRep_Builder builder;
diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp
index f0e233dde6..f156692b0d 100644
--- a/src/ifcgeom/IfcGeomFunctions.cpp
+++ b/src/ifcgeom/IfcGeomFunctions.cpp
@@ -5613,7 +5613,7 @@ IfcGeom::Kernel::faceset_helper::faceset_helper(
if (edge_sets.find(segment_set) != edge_sets.end()) {
duplicate_faces++;
- duplicates_.insert(*ps);
+ duplicates_.insert(util::conditional_address_of(*ps));
continue;
}
edge_sets.insert(segment_set);
@@ -5646,8 +5646,8 @@ IfcGeom::Kernel::faceset_helper::faceset_helper(
}
}
- if (loops_removed || (non_manifold && should_be_closed)) {
- Logger::Warning(boost::lexical_cast(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast(loops_removed) + " loops removed and " + boost::lexical_cast(non_manifold) + " non-manifold edges");
+ if (duplicates_.size() || loops_removed || (non_manifold && should_be_closed)) {
+ Logger::Warning(boost::lexical_cast(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast(loops_removed) + " degenerate loops eliminated and " + boost::lexical_cast(non_manifold) + " non-manifold edges");
}
}