Merge branch 'IfcOpenShell:v0.7.0' into pr-daily-builds

This commit is contained in:
Kristoffer Andersen
2022-04-14 13:39:56 +02:00
committed by GitHub
17 changed files with 341 additions and 209 deletions
@@ -173,7 +173,7 @@
style="stroke-width:0.999999px"
y="1036.1166"
x="3030.9653"
id="tspan2527">{{revision}}</tspan></text>
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3918px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -183,7 +183,7 @@
id="tspan2531"
x="2804.7021"
y="1036.1166"
style="stroke-width:0.999999px">{{number}}</tspan></text>
style="stroke-width:0.999999px">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -169,7 +169,7 @@
style="stroke-width:0.264583px"
y="397.13919"
x="554.94287"
id="tspan2527">{{revision}}</tspan></text>
id="tspan2527">{{Revision}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.54325px;line-height:125%;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
@@ -179,7 +179,7 @@
id="tspan2531"
x="495.07742"
y="397.13919"
style="stroke-width:0.264583px">{{number}}</tspan></text>
style="stroke-width:0.264583px">{{Identification}} - {{Name}}</tspan></text>
<g
style="stroke-width:3.77953"
id="g90669"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -172,7 +172,7 @@
id="tspan2527"
x="544.94287"
y="284.13919"
style="stroke-width:0.264583px">{{revision}}</tspan></text>
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
<text
id="text2533"
y="284.13919"
@@ -182,7 +182,7 @@
style="stroke-width:0.264583px"
y="284.13919"
x="485.07742"
id="tspan2531">{{number}}</tspan></text>
id="tspan2531">{{Identification}} - {{Name}}</tspan></text>
</g>
<g
style="stroke-width:3.77953"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -70,7 +70,7 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.96055365px;font-family:'OpenGost Type B TT';-inkscape-font-specification:'OpenGost Type B TT';text-align:center;text-anchor:middle;stroke-width:0.26458332px"
y="103.10933"
x="32.822662"
id="tspan857">{{no}}</tspan></text>
id="tspan857">{{Identification}}</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.71626091px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458329px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -80,7 +80,7 @@
id="tspan861"
x="39.12896"
y="100.19173"
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}}</tspan></text>
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}}</tspan></text>
<text
id="text867"
y="105.0553"
@@ -90,6 +90,6 @@
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.26458332px"
y="105.0553"
x="39.285267"
id="tspan865">{{scale}}</tspan></text>
id="tspan865">{{Scale}}</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -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
@@ -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):
@@ -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"))
@@ -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,
@@ -28,6 +28,7 @@ classes = (
operator.UnlinkIfc,
operator.UnloadLink,
operator.LoadLink,
operator.ToggleLinkVisibility,
operator.SelectLibraryFile,
operator.ChangeLibraryElement,
operator.RefreshLibrary,
@@ -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"
@@ -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)
@@ -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:
+1 -1
View File
@@ -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()
+40 -11
View File
@@ -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
+137 -128
View File
@@ -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 <https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, 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
<https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, 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 <https://aur.archlinux.org/packages/ifcopenshell-git/>`__.
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
<https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.6.0/src/blenderbim/blenderbim>`__.
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 <https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.6.0/src/ifcbimtester/patch>`__.
4. ``ifcjson`` can be found `here <https://github.com/IFCJSON-Team/IFC2JSON_python/tree/master/file_converters>`__.
Required binaries are:
::
libs/IfcConvert
2. ``behave`` requires `patches <https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.6.0/src/ifcbimtester/patch>`__.
3. ``ifcjson`` can be found `here <https://github.com/IFCJSON-Team/IFC2JSON_python/tree/master/file_converters>`__.
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 <https://aur.archlinux.org/packages/ifcopenshell-git/>`__.
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"
+15 -3
View File
@@ -111,6 +111,18 @@ public:
std::map<int, TopoDS_Shape> Shape;
};
namespace util {
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T&>::type conditional_address_of(T& t) {
return t;
}
template <typename T>
typename std::enable_if<!std::is_pointer<T>::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<LP> duplicates_;
std::set<typename std::conditional<std::is_pointer<LP>::value, LP, const LP*>::type> duplicates_;
std::map<const void*, int> vertex_mapping_;
std::map<std::pair<int, int>, 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;
+3 -3
View File
@@ -5613,7 +5613,7 @@ IfcGeom::Kernel::faceset_helper<CP, LP>::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<CP, LP>::faceset_helper(
}
}
if (loops_removed || (non_manifold && should_be_closed)) {
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " loops removed and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
if (duplicates_.size() || loops_removed || (non_manifold && should_be_closed)) {
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " degenerate loops eliminated and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
}
}