Merge branch 'IfcOpenShell:v0.7.0' into pr-daily-builds
@@ -173,7 +173,7 @@
|
|||||||
style="stroke-width:0.999999px"
|
style="stroke-width:0.999999px"
|
||||||
y="1036.1166"
|
y="1036.1166"
|
||||||
x="3030.9653"
|
x="3030.9653"
|
||||||
id="tspan2527">{{revision}}</tspan></text>
|
id="tspan2527">{{Revision}}</tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
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"
|
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"
|
id="tspan2531"
|
||||||
x="2804.7021"
|
x="2804.7021"
|
||||||
y="1036.1166"
|
y="1036.1166"
|
||||||
style="stroke-width:0.999999px">{{number}}</tspan></text>
|
style="stroke-width:0.999999px">{{Identification}} - {{Name}}</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
transform="matrix(0.26458333,0,0,0.26458334,-3.180917e-5,297.00003)"
|
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"
|
style="stroke-width:0.264583px"
|
||||||
y="397.13919"
|
y="397.13919"
|
||||||
x="554.94287"
|
x="554.94287"
|
||||||
id="tspan2527">{{revision}}</tspan></text>
|
id="tspan2527">{{Revision}}</tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
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"
|
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"
|
id="tspan2531"
|
||||||
x="495.07742"
|
x="495.07742"
|
||||||
y="397.13919"
|
y="397.13919"
|
||||||
style="stroke-width:0.264583px">{{number}}</tspan></text>
|
style="stroke-width:0.264583px">{{Identification}} - {{Name}}</tspan></text>
|
||||||
<g
|
<g
|
||||||
style="stroke-width:3.77953"
|
style="stroke-width:3.77953"
|
||||||
id="g90669"
|
id="g90669"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -172,7 +172,7 @@
|
|||||||
id="tspan2527"
|
id="tspan2527"
|
||||||
x="544.94287"
|
x="544.94287"
|
||||||
y="284.13919"
|
y="284.13919"
|
||||||
style="stroke-width:0.264583px">{{revision}}</tspan></text>
|
style="stroke-width:0.264583px">{{Revision}}</tspan></text>
|
||||||
<text
|
<text
|
||||||
id="text2533"
|
id="text2533"
|
||||||
y="284.13919"
|
y="284.13919"
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
style="stroke-width:0.264583px"
|
style="stroke-width:0.264583px"
|
||||||
y="284.13919"
|
y="284.13919"
|
||||||
x="485.07742"
|
x="485.07742"
|
||||||
id="tspan2531">{{number}}</tspan></text>
|
id="tspan2531">{{Identification}} - {{Name}}</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
style="stroke-width:3.77953"
|
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"
|
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"
|
y="103.10933"
|
||||||
x="32.822662"
|
x="32.822662"
|
||||||
id="tspan857">{{no}}</tspan></text>
|
id="tspan857">{{Identification}}</tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
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"
|
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"
|
id="tspan861"
|
||||||
x="39.12896"
|
x="39.12896"
|
||||||
y="100.19173"
|
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
|
<text
|
||||||
id="text867"
|
id="text867"
|
||||||
y="105.0553"
|
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"
|
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"
|
y="105.0553"
|
||||||
x="39.285267"
|
x="39.285267"
|
||||||
id="tspan865">{{scale}}</tspan></text>
|
id="tspan865">{{Scale}}</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</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):
|
def execute(self, context):
|
||||||
reference = tool.Ifc.get().by_id(self.reference)
|
reference = tool.Ifc.get().by_id(self.reference)
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
sheet = tool.Drawing.get_reference_sheet(reference)
|
||||||
sheet = reference.ReferenceToDocument[0]
|
drawing = tool.Drawing.get_reference_element(reference)
|
||||||
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]
|
|
||||||
|
|
||||||
tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
|
tool.Ifc.run("document.unassign_document", product=drawing, document=reference)
|
||||||
tool.Ifc.run("document.remove_reference", reference=reference)
|
tool.Ifc.run("document.remove_reference", reference=reference)
|
||||||
@@ -667,10 +661,11 @@ class CreateSheets(bpy.types.Operator):
|
|||||||
scene = context.scene
|
scene = context.scene
|
||||||
props = scene.DocProperties
|
props = scene.DocProperties
|
||||||
active_sheet = props.sheets[props.active_sheet_index]
|
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 = sheeter.SheetBuilder()
|
||||||
sheet_builder.data_dir = scene.BIMProperties.data_dir
|
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
|
svg2pdf_command = context.preferences.addons["blenderbim"].preferences.svg2pdf_command
|
||||||
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
|
svg2dxf_command = context.preferences.addons["blenderbim"].preferences.svg2dxf_command
|
||||||
|
|||||||
@@ -72,8 +72,10 @@ def update_diagram_scale(self, context):
|
|||||||
scale = self.diagram_scale
|
scale = self.diagram_scale
|
||||||
if scale == "CUSTOM":
|
if scale == "CUSTOM":
|
||||||
scale = self.custom_diagram_scale
|
scale = self.custom_diagram_scale
|
||||||
scale = scale.split("|")[1]
|
if "|" not in scale:
|
||||||
element = tool.Ifc.get_entity(context.scene.camera)
|
return
|
||||||
|
human_scale, scale = scale.split("|")
|
||||||
|
element = tool.Ifc.get_entity(context.active_object)
|
||||||
if not element:
|
if not element:
|
||||||
return
|
return
|
||||||
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
|
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"])
|
pset = tool.Ifc.get().by_id(pset["id"])
|
||||||
else:
|
else:
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing")
|
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):
|
def get_diagram_scales(self, context):
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class SheetBuilder:
|
|||||||
drawing_dir = os.path.join(self.data_dir, "diagrams")
|
drawing_dir = os.path.join(self.data_dir, "diagrams")
|
||||||
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
|
sheet_path = os.path.join(sheet_dir, sheet_name + ".svg")
|
||||||
drawing_path = os.path.join(drawing_dir, filename + ".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):
|
if not os.path.isfile(sheet_path):
|
||||||
raise FileNotFoundError
|
raise FileNotFoundError
|
||||||
@@ -92,6 +92,7 @@ class SheetBuilder:
|
|||||||
|
|
||||||
if os.path.isfile(underlay_path):
|
if os.path.isfile(underlay_path):
|
||||||
background = ET.SubElement(view, "image")
|
background = ET.SubElement(view, "image")
|
||||||
|
background.attrib["data-type"] = "background"
|
||||||
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
|
background.attrib["xlink:href"] = os.path.relpath(underlay_path, sheet_dir)
|
||||||
background.attrib["x"] = "30"
|
background.attrib["x"] = "30"
|
||||||
background.attrib["y"] = "30"
|
background.attrib["y"] = "30"
|
||||||
@@ -100,6 +101,7 @@ class SheetBuilder:
|
|||||||
|
|
||||||
if os.path.isfile(drawing_path):
|
if os.path.isfile(drawing_path):
|
||||||
foreground = ET.SubElement(view, "image")
|
foreground = ET.SubElement(view, "image")
|
||||||
|
foreground.attrib["data-type"] = "foreground"
|
||||||
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
|
foreground.attrib["xlink:href"] = os.path.relpath(drawing_path, sheet_dir)
|
||||||
foreground.attrib["x"] = "30"
|
foreground.attrib["x"] = "30"
|
||||||
foreground.attrib["y"] = "30"
|
foreground.attrib["y"] = "30"
|
||||||
@@ -119,9 +121,7 @@ class SheetBuilder:
|
|||||||
sheet_tree = ET.parse(sheet_path)
|
sheet_tree = ET.parse(sheet_path)
|
||||||
sheet_root = sheet_tree.getroot()
|
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"):
|
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:
|
if g.attrib["data-type"] == "drawing" and g.attrib["data-guid"] == drawing.GlobalId:
|
||||||
sheet_root.remove(g)
|
sheet_root.remove(g)
|
||||||
break
|
break
|
||||||
@@ -160,13 +160,15 @@ class SheetBuilder:
|
|||||||
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg"))
|
title_tree = ET.parse(os.path.join(self.data_dir, "templates", "view-title.svg"))
|
||||||
title_root = title_tree.getroot()
|
title_root = title_tree.getroot()
|
||||||
title = ET.SubElement(parent, "image")
|
title = ET.SubElement(parent, "image")
|
||||||
|
title.attrib["data-type"] = "view-title"
|
||||||
title.attrib["xlink:href"] = "../templates/view-title.svg"
|
title.attrib["xlink:href"] = "../templates/view-title.svg"
|
||||||
title.attrib["x"] = str(x)
|
title.attrib["x"] = str(x)
|
||||||
title.attrib["y"] = str(y)
|
title.attrib["y"] = str(y)
|
||||||
title.attrib["width"] = str(self.convert_to_mm(title_root.attrib.get("width")))
|
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")))
|
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)
|
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")
|
sheet_path = os.path.join(self.data_dir, "sheets", f"{sheet_name}.svg")
|
||||||
@@ -177,48 +179,60 @@ class SheetBuilder:
|
|||||||
tree = ET.parse(sheet_path)
|
tree = ET.parse(sheet_path)
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
|
|
||||||
titleblock = root.findall('{http://www.w3.org/2000/svg}g[@data-type="titleblock"]')[0]
|
self.build_titleblock(root, sheet)
|
||||||
image = titleblock.findall("{http://www.w3.org/2000/svg}image")[0]
|
self.build_drawings(root, sheet)
|
||||||
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_schedules(root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'))
|
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:
|
with open(os.path.join(self.data_dir, "build", sheet_name, f"{sheet_name}.svg"), "wb") as output:
|
||||||
tree.write(output)
|
tree.write(output)
|
||||||
|
|
||||||
def build_drawings(self, drawings, sheet_name):
|
def build_titleblock(self, root, sheet):
|
||||||
for view in drawings:
|
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")
|
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
|
background = None
|
||||||
view.append(self.parse_embedded_svg(foreground, {}))
|
foreground = None
|
||||||
|
view_title = None
|
||||||
|
|
||||||
# Add background
|
for image in images:
|
||||||
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
|
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
|
if background is not None:
|
||||||
foreground_path = self.get_href(foreground)
|
background_path = os.path.join(self.data_dir, "sheets", self.get_href(background))
|
||||||
view.append(
|
copy(background_path, os.path.join(self.data_dir, "build", sheet_name))
|
||||||
self.parse_embedded_svg(
|
|
||||||
view_title,
|
if view_title is not None:
|
||||||
{"no": self.group_number, "name": ntpath.basename(foreground_path)[0:-4], "scale": self.scale},
|
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:
|
for image in images:
|
||||||
view.remove(image)
|
view.remove(image)
|
||||||
|
|
||||||
self.group_number += 1
|
|
||||||
|
|
||||||
def build_schedules(self, schedules):
|
def build_schedules(self, schedules):
|
||||||
for group in schedules:
|
for group in schedules:
|
||||||
images = group.findall("{http://www.w3.org/2000/svg}image")
|
images = group.findall("{http://www.w3.org/2000/svg}image")
|
||||||
@@ -231,15 +245,13 @@ class SheetBuilder:
|
|||||||
path = self.get_href(schedule)
|
path = self.get_href(schedule)
|
||||||
group.append(
|
group.append(
|
||||||
self.parse_embedded_svg(
|
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:
|
for image in images:
|
||||||
group.remove(image)
|
group.remove(image)
|
||||||
|
|
||||||
self.group_number += 1
|
|
||||||
|
|
||||||
def get_href(self, element):
|
def get_href(self, element):
|
||||||
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href"))
|
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],
|
(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)))
|
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):
|
def draw_elevation_annotation(self, obj):
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
y_offset = self.raw_height / 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-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
|
||||||
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.scale)))
|
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):
|
def draw_text_annotation(self, text_obj, position):
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
@@ -502,7 +544,7 @@ class SvgWriter:
|
|||||||
|
|
||||||
if text_obj.BIMTextProperties.symbol != "None":
|
if text_obj.BIMTextProperties.symbol != "None":
|
||||||
self.svg.add(
|
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":
|
if text_literal.BoxAlignment == "top-left":
|
||||||
@@ -533,11 +575,15 @@ class SvgWriter:
|
|||||||
alignment_baseline = "baseline"
|
alignment_baseline = "baseline"
|
||||||
text_anchor = "end"
|
text_anchor = "end"
|
||||||
|
|
||||||
text_body = text_literal.Literal
|
literal = 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])
|
|
||||||
|
|
||||||
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.add(
|
||||||
self.svg.text(
|
self.svg.text(
|
||||||
text_line,
|
text_line,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ classes = (
|
|||||||
operator.UnlinkIfc,
|
operator.UnlinkIfc,
|
||||||
operator.UnloadLink,
|
operator.UnloadLink,
|
||||||
operator.LoadLink,
|
operator.LoadLink,
|
||||||
|
operator.ToggleLinkVisibility,
|
||||||
operator.SelectLibraryFile,
|
operator.SelectLibraryFile,
|
||||||
operator.ChangeLibraryElement,
|
operator.ChangeLibraryElement,
|
||||||
operator.RefreshLibrary,
|
operator.RefreshLibrary,
|
||||||
|
|||||||
@@ -685,10 +685,31 @@ class LoadLink(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
bpy.data.scenes[0].collection.children.link(child)
|
bpy.data.scenes[0].collection.children.link(child)
|
||||||
link = context.scene.BIMProjectProperties.links.get(filepath)
|
link = context.scene.BIMProjectProperties.links.get(filepath)
|
||||||
|
link.collection_name = child.name
|
||||||
link.is_loaded = True
|
link.is_loaded = True
|
||||||
return {"FINISHED"}
|
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):
|
class ExportIFC(bpy.types.Operator):
|
||||||
bl_idname = "export_ifc.bim"
|
bl_idname = "export_ifc.bim"
|
||||||
bl_label = "Export IFC"
|
bl_label = "Export IFC"
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class FilterCategory(PropertyGroup):
|
|||||||
|
|
||||||
class Link(PropertyGroup):
|
class Link(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
|
collection_name: StringProperty(name="Collection Name")
|
||||||
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -286,6 +286,8 @@ class BIM_UL_links(UIList):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
if item.is_loaded:
|
if item.is_loaded:
|
||||||
row.label(text=item.name)
|
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 = row.operator("bim.unload_link", text="", icon="UNLINKED")
|
||||||
op.filepath = item.name
|
op.filepath = item.name
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -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)
|
ifc.run("group.assign_group", group=group, product=element)
|
||||||
collector.assign(camera)
|
collector.assign(camera)
|
||||||
pset = ifc.run("pset.add_pset", product=element, name="EPset_Drawing")
|
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()
|
drawing.import_drawings()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
name = document.Identification or "X"
|
name = document.Identification or "X"
|
||||||
else:
|
else:
|
||||||
name = document.DocumentId or "X"
|
name = document.DocumentId or "X"
|
||||||
name += " - " + document.Name or "Unnamed"
|
name += " - " + (document.Name or "Unnamed")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -291,24 +291,17 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
if not new.is_expanded:
|
if not new.is_expanded:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
for reference in cls.get_document_references(sheet):
|
||||||
references = sheet.DocumentReferences or []
|
|
||||||
else:
|
|
||||||
references = sheet.HasDocumentReferences or []
|
|
||||||
|
|
||||||
for reference in references:
|
|
||||||
new = props.sheets.add()
|
new = props.sheets.add()
|
||||||
new.ifc_definition_id = reference.id()
|
new.ifc_definition_id = reference.id()
|
||||||
new.is_sheet = False
|
new.is_sheet = False
|
||||||
|
|
||||||
if tool.Ifc.get_schema() == "IFC2X3":
|
if tool.Ifc.get_schema() == "IFC2X3":
|
||||||
new.identification = reference.ItemReference or "X"
|
new.identification = reference.ItemReference or "X"
|
||||||
element = [
|
|
||||||
r for r in tool.Ifc.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference
|
|
||||||
][0].RelatedObjects[0]
|
|
||||||
else:
|
else:
|
||||||
new.identification = reference.Identification or "X"
|
new.identification = reference.Identification or "X"
|
||||||
element = reference.DocumentRefForObjects[0].RelatedObjects[0]
|
|
||||||
|
element = cls.get_reference_element(reference)
|
||||||
|
|
||||||
new.name = element.Name
|
new.name = element.Name
|
||||||
new.reference_type = "DRAWING"
|
new.reference_type = "DRAWING"
|
||||||
@@ -720,3 +713,39 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
if grid_obj.matrix_world != obj.matrix_world:
|
if grid_obj.matrix_world != obj.matrix_world:
|
||||||
bpy.ops.bim.update_representation(obj=obj.name)
|
bpy.ops.bim.update_representation(obj=obj.name)
|
||||||
tool.Geometry.record_object_position(obj)
|
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
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ Installation
|
|||||||
|
|
||||||
There are different methods of installation, depending on your situation.
|
There are different methods of installation, depending on your situation.
|
||||||
|
|
||||||
1. **Packaged installation** is recommended for regular users.
|
1. **Stable installation** is recommended for regular users.
|
||||||
2. **Daily build installation** is recommended for power users helping with testing.
|
2. **Unstable installation** is recommended for power users helping with testing.
|
||||||
3. **Unpackaged installation** is recommended for package managers.
|
3. **Building from source** is recommended for distributing a build from source.
|
||||||
4. **Distro installation** is recommended for those who use a Linux package manager.
|
4. **Live development environment** is recommended for developers who are actively coding.
|
||||||
5. **Source installation** is recommended for developers.
|
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
|
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
|
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
|
Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox``. You can
|
||||||
enable add-ons permanently by using ``Save User Settings`` from the Addons menu.
|
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
|
**Unstable installation** is almost the same as **Stable installation**, except
|
||||||
are typically updated every day. Simply download a daily build from the `Github
|
that they are typically updated every day. Simply download a daily build from
|
||||||
releases page <https://github.com/IfcOpenShell/IfcOpenShell/releases>`__, then
|
the `Github releases page
|
||||||
follow the same instructions as a packaged installation.
|
<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 py37
|
||||||
- If you are on Blender >=2.93, choose py39
|
|
||||||
- Choose linux, macos, or win depending on your operating system
|
- Choose linux, macos, or win depending on your operating system
|
||||||
|
|
||||||
Daily builds are not always stable. Sometimes, a build may be delayed, or
|
Sometimes, a build may be delayed, or contain broken code. We try to avoid this,
|
||||||
contain broken code. We try to avoid this, but it happens.
|
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
|
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
|
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>`__.
|
<https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.6.0/src/blenderbim/blenderbim>`__.
|
||||||
|
|
||||||
The BlenderBIM Add-on is complex, and requires many dependencies, including
|
The BlenderBIM Add-on is complex, and requires many dependencies, including
|
||||||
Python modules, binaries, and static assets. These dependencies are bundled with
|
Python modules, binaries, and static assets. When packaged for users, these
|
||||||
the add-on for convenience in the **Packaged installation** and **Daily build
|
dependencies are bundled with the add-on for convenience.
|
||||||
installation** methods.
|
|
||||||
|
|
||||||
If you choose to install the BlenderBIM Add-on and use your own system
|
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
|
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
|
elementpath
|
||||||
six
|
six
|
||||||
lark-parser
|
lark-parser
|
||||||
fcl
|
hppfcl
|
||||||
behave
|
behave
|
||||||
parse
|
parse
|
||||||
parse_type
|
parse_type
|
||||||
@@ -108,14 +211,8 @@ Required Python modules to be stored in ``libs/site/packages/`` are:
|
|||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
1. ``ifcopenshell`` almost always requires the latest version due to the fast paced nature of the add-on development.
|
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.
|
2. ``behave`` requires `patches <https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.6.0/src/ifcbimtester/patch>`__.
|
||||||
3. ``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>`__.
|
||||||
4. ``ifcjson`` can be found `here <https://github.com/IFCJSON-Team/IFC2JSON_python/tree/master/file_converters>`__.
|
|
||||||
|
|
||||||
Required binaries are:
|
|
||||||
::
|
|
||||||
|
|
||||||
libs/IfcConvert
|
|
||||||
|
|
||||||
Required static assets are:
|
Required static assets are:
|
||||||
::
|
::
|
||||||
@@ -123,121 +220,31 @@ Required static assets are:
|
|||||||
bim/data/gantt/jsgantt.js (from jsgantt-improved)
|
bim/data/gantt/jsgantt.js (from jsgantt-improved)
|
||||||
bim/data/gantt/jsgantt.css (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?
|
Where is the BlenderBIM Add-on installed?
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
If you downloaded Blender as a ``.zip`` file without running an installer, you
|
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
|
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
|
Otherwise, if you installed Blender using an installation package, the add-ons
|
||||||
folder depends on which operating system you use. On Linux:
|
folder depends on which operating system you use. On Linux:
|
||||||
::
|
::
|
||||||
|
|
||||||
~/.config/blender/2.XX/scripts/addons/
|
~/.config/blender/X.XX/scripts/addons/
|
||||||
|
|
||||||
On Mac:
|
On Mac:
|
||||||
::
|
::
|
||||||
|
|
||||||
/Users/{YOUR_USER}/Library/Application Support/Blender/2.XX/
|
/Users/{YOUR_USER}/Library/Application Support/Blender/X.XX/
|
||||||
|
|
||||||
On Windows:
|
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/``
|
Upon installation, the BlenderBIM Add-on is stored in the ``blenderbim/``
|
||||||
directory.
|
directory.
|
||||||
@@ -264,8 +271,10 @@ FAQ
|
|||||||
|
|
||||||
1. I get an error similar to "ImportError: IfcOpenShell not built for 'linux/64bit/python3.7'"
|
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
|
Check which BlenderBIM Add-on build you are using. The zip will have either
|
||||||
instructions above for a daily build installation.
|
``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"
|
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"
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,18 @@ public:
|
|||||||
std::map<int, TopoDS_Shape> Shape;
|
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 {
|
class IFC_GEOM_API MAKE_TYPE_NAME(Kernel) : public IfcGeom::Kernel {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -126,7 +138,7 @@ private:
|
|||||||
class faceset_helper {
|
class faceset_helper {
|
||||||
private:
|
private:
|
||||||
MAKE_TYPE_NAME(Kernel)* kernel_;
|
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<const void*, int> vertex_mapping_;
|
||||||
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
|
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
|
||||||
// not always in use
|
// not always in use
|
||||||
@@ -225,8 +237,8 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wire(LP loop, TopoDS_Wire& wire) {
|
bool wire(const LP& loop, TopoDS_Wire& wire) {
|
||||||
if (duplicates_.find(loop) != duplicates_.end()) {
|
if (duplicates_.find(util::conditional_address_of(loop)) != duplicates_.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BRep_Builder builder;
|
BRep_Builder builder;
|
||||||
|
|||||||
@@ -5613,7 +5613,7 @@ IfcGeom::Kernel::faceset_helper<CP, LP>::faceset_helper(
|
|||||||
|
|
||||||
if (edge_sets.find(segment_set) != edge_sets.end()) {
|
if (edge_sets.find(segment_set) != edge_sets.end()) {
|
||||||
duplicate_faces++;
|
duplicate_faces++;
|
||||||
duplicates_.insert(*ps);
|
duplicates_.insert(util::conditional_address_of(*ps));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
edge_sets.insert(segment_set);
|
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)) {
|
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) + " loops removed and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||