Merge branch 'IfcOpenShell:v0.7.0' into v0.7.0

This commit is contained in:
Carlos Dias
2023-07-03 08:48:10 -03:00
committed by GitHub
22 changed files with 298 additions and 152 deletions
+4 -1
View File
@@ -94,4 +94,7 @@ src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# apple
.DS_Store
.DS_Store
# Brickschema
src/blenderbim/blenderbim/bim/schema/Brick.ttl
+2
View File
@@ -275,6 +275,7 @@ class TopicHandler:
"""
new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler)
self.add_visinfo_handler(new_viewpoint)
return new_viewpoint
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None:
"""Add a viewpoint pointing at an XYZ point in space
@@ -287,6 +288,7 @@ class TopicHandler:
position, *guids, xml_handler=self._xml_handler
)
self.add_visinfo_handler(vi_handler)
return vi_handler
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
@@ -57,6 +57,7 @@ classes = (
operator.SelectBcfBimSnippetReference,
operator.SelectBcfDocumentReference,
operator.SelectBcfHeaderFile,
operator.UnloadBcfProject,
operator.ViewBcfTopic,
prop.BcfReferenceLink,
prop.BcfLabel,
@@ -58,7 +58,6 @@ class LoadBcfProject(bpy.types.Operator):
filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"})
def execute(self, context):
context.scene.BCFProperties.is_loaded = False
if self.filepath:
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
bcfxml = bcfstore.BcfStore.get_bcfxml()
@@ -72,6 +71,17 @@ class LoadBcfProject(bpy.types.Operator):
return {"RUNNING_MODAL"}
class UnloadBcfProject(bpy.types.Operator):
bl_idname = "bim.unload_bcf_project"
bl_label = "Unload BCF Project"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
bcfstore.BcfStore.set(None)
context.scene.BCFProperties.is_loaded = False
return {"FINISHED"}
class LoadBcfTopics(bpy.types.Operator):
bl_idname = "bim.load_bcf_topics"
bl_label = "Load BCF Topics"
@@ -39,14 +39,16 @@ class BIM_PT_bcf(Panel):
scene = context.scene
props = scene.BCFProperties
row = layout.row(align=True)
row.operator("bim.new_bcf_project", text="New Project")
row.operator("bim.load_bcf_project", text="Load Project")
if not props.is_loaded:
row = layout.row(align=True)
row.operator("bim.new_bcf_project", text="New Project")
row.operator("bim.load_bcf_project", text="Load Project")
return
row.operator("bim.save_bcf_project", text="Save Project")
row = layout.row(align=True)
row.operator("bim.save_bcf_project", icon="EXPORT", text="Save Project")
row.operator("bim.unload_bcf_project", text="", icon="CANCEL")
row = layout.row()
row.prop(props, "name")
@@ -33,6 +33,9 @@ classes = (
operator.RewindBrickClass,
operator.ViewBrickClass,
operator.ViewBrickItem,
operator.UndoBrick,
operator.RedoBrick,
operator.SerializeBrick,
prop.Brick,
prop.BIMBrickProperties,
ui.BIM_PT_brickschema,
@@ -110,7 +110,8 @@ class BrickschemaData:
return []
results = []
for alias, uri in BrickStore.graph.namespaces():
results.append((uri, f"{alias}: {uri}", ""))
# results.append((uri, f"{alias}: {uri}", ""))
results.append((uri, f"{alias}", ""))
return results
@classmethod
@@ -189,3 +189,24 @@ class RemoveBrick(bpy.types.Operator, Operator):
library=tool.Ifc.get().by_id(int(props.libraries)) if props.libraries else None,
brick_uri=props.bricks[props.active_brick_index].uri,
)
class UndoBrick(bpy.types.Operator, Operator):
bl_idname = "bim.undo_brick"
bl_label = "Undo Brick"
def _execute(self, context):
core.undo_brick(tool.Brick)
class RedoBrick(bpy.types.Operator, Operator):
bl_idname = "bim.redo_brick"
bl_label = "Redo Brick"
def _execute(self, context):
core.redo_brick(tool.Brick)
class SerializeBrick(bpy.types.Operator, Operator):
bl_idname = "bim.serialize_brick"
bl_label = "Serialize Brick"
def _execute(self, context):
core.serialize_brick(tool.Brick)
@@ -58,6 +58,13 @@ class BIM_PT_brickschema(Panel):
row.operator("bim.add_brick_feed", text="", icon="PLUGIN")
row.operator("bim.remove_brick", text="", icon="X")
row = self.layout.row(align=True)
row.operator("bim.undo_brick", icon="LOOP_BACK")
row.operator("bim.redo_brick", icon="LOOP_FORWARDS")
row = self.layout.row(align=True)
row.operator("bim.serialize_brick")
self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
for attribute in BrickschemaData.data["attributes"]:
@@ -23,7 +23,7 @@ import bmesh
import logging
import numpy as np
import ifcopenshell
from mathutils import Matrix
from mathutils import Matrix, Vector
from math import radians
from blenderbim.bim.ifc import IfcStore
@@ -222,6 +222,7 @@ class ExecuteIfcClash(bpy.types.Operator):
_, extension = os.path.splitext(self.filepath)
if extension != ".json":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
settings = ifcclash.ClashSettings()
settings.output = self.filepath
settings.logger = logging.getLogger("Clash")
@@ -230,12 +231,28 @@ class ExecuteIfcClash(bpy.types.Operator):
if context.scene.BIMClashProperties.should_create_clash_snapshots:
def get_viewpoint_snapshot(viewpoint, mat):
def get_viewpoint_snapshot(viewpoint):
camera = bpy.data.objects.get("IFC Clash Camera")
if not camera:
camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera"))
context.scene.collection.objects.link(camera)
camera.matrix_world = Matrix(mat)
bcf_camera = viewpoint.visualization_info.perspective_camera
p = bcf_camera.camera_view_point
z = bcf_camera.camera_direction
z = Vector([z.x, z.y, z.z]) * -1
y = bcf_camera.camera_up_vector
y = Vector([y.x, y.y, y.z])
x = y.cross(z)
mat = Matrix([
[x[0], y[0], z[0], p.x],
[x[1], y[1], z[1], p.y],
[x[2], y[2], z[2], p.z],
[0, 0, 0, 0],
])
camera.matrix_world = mat
context.scene.camera = camera
camera.data.angle = radians(60)
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
@@ -246,7 +263,8 @@ class ExecuteIfcClash(bpy.types.Operator):
context.scene.render.image_settings.file_format = "PNG"
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True)
return context.scene.render.filepath
with open(context.scene.render.filepath, "rb") as f:
return ("snapshot.png", f.read())
clasher.get_viewpoint_snapshot = get_viewpoint_snapshot
@@ -111,10 +111,13 @@ class ConnectionsData:
for rel in connected_to:
if element.is_a("IfcDistributionPort"):
related_element = rel.RelatedPort
related_element_connection_type = ""
else:
related_element = rel.RelatedElement
if element.is_a("IfcRelConnectsPathElements"):
related_element_connection_type = rel.RelatedConnectionType
else:
related_element_connection_type = ""
results.append(
{
@@ -128,10 +131,13 @@ class ConnectionsData:
for rel in connected_from:
if element.is_a("IfcDistributionPort"):
relating_element = rel.RelatingPort
relating_element_connection_type = ""
else:
relating_element = rel.RelatingElement
if element.is_a("IfcRelConnectsPathElements"):
relating_element_connection_type = rel.RelatingConnectionType
else:
relating_element_connection_type = ""
results.append(
{
@@ -292,8 +292,6 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
offset: bpy.props.IntProperty()
def _execute(self, context):
from PIL import Image, ImageDraw
if bpy.app.background:
return
@@ -310,106 +308,11 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
offset = 0
queue = queue[offset : offset + 9]
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
while queue:
# if bpy.app.is_job_running("RENDER_PREVIEW") does not seem to reflect asset preview generation
element = queue.pop()
obj = tool.Ifc.get_object(element)
if not obj:
continue # Nothing to process
elif AuthoringData.type_thumbnails.get(element.id(), None):
continue # Already processed
elif obj.preview and obj.preview.icon_id:
AuthoringData.type_thumbnails[element.id()] = obj.preview.icon_id
continue
if obj.data:
obj.asset_generate_preview()
while not obj.preview:
pass
else:
size = 128
img = Image.new("RGBA", (size, size))
draw = ImageDraw.Draw(img)
material = ifcopenshell.util.element.get_material(element)
if material and material.is_a("IfcMaterialProfileSet"):
profile = material.MaterialProfiles[0].Profile
tool.Profile.draw_image_for_ifc_profile(draw, profile, size)
elif material and material.is_a("IfcMaterialLayerSet"):
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
total_thickness = sum(thicknesses)
si_total_thickness = total_thickness * unit_scale
if si_total_thickness <= 0.051:
width = 10
elif si_total_thickness <= 0.11:
width = 20
elif si_total_thickness <= 0.21:
width = 30
elif si_total_thickness <= 0.31:
width = 40
else:
width = 50
height = 100
is_horizontal = False
if element.is_a("IfcSlabType"):
is_horizontal = True
parametric = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric")
if parametric:
layer_set_direction = parametric.get("LayerSetDirection", None)
if layer_set_direction == "AXIS2":
is_horizontal = False
elif layer_set_direction == "AXIS3":
is_horizontal = True
if is_horizontal:
width, height = height, width
x_offset = (size / 2) - (width / 2)
y_offset = (size / 2) - (height / 2)
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
current_thickness = 0
del thicknesses[-1]
for thickness in thicknesses:
current_thickness += thickness
if element.is_a("IfcSlabType"):
y = (current_thickness / total_thickness) * height
line = [x_offset, y_offset + y, x_offset + width, y_offset + y]
else:
x = (current_thickness / total_thickness) * width
line = [x_offset + x, y_offset, x_offset + x, y_offset + height]
draw.line(line, fill="white", width=2)
elif False:
# TODO: things like parametric duct segments
pass
elif not element.RepresentationMaps:
# Empties are represented by a generic thumbnail
width = height = 100
x_offset = (size / 2) - (width / 2)
y_offset = (size / 2) - (height / 2)
draw.line([x_offset, y_offset, width + x_offset, height + y_offset], fill="white", width=2)
draw.line([x_offset, y_offset + height, width + x_offset, y_offset], fill="white", width=2)
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
else:
draw.line([0, 0, size, size], fill="red", width=2)
draw.line([0, size, size, 0], fill="red", width=2)
pixels = [item for sublist in img.getdata() for item in sublist]
obj.asset_generate_preview()
while not obj.preview:
pass
obj.preview.image_size = size, size
obj.preview.image_pixels_float = pixels
queue.append(element)
if tool.Model.update_thumbnail_for_element(element):
queue.append(element)
return {"FINISHED"}
@@ -472,7 +375,6 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator):
obj.matrix_world = newmat
def generate_box(usecase_path, ifc_file, settings):
box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
if not box_context:
@@ -145,12 +145,21 @@ class DumbProfileRegenerator:
objs = []
if not profile:
return
element_types = set()
for element in self.get_elements_using_profile(profile):
obj = tool.Ifc.get_object(element)
if obj:
objs.append(obj)
if element.is_a("IfcElementType"):
element_types.add(element)
DumbProfileRecalculator().recalculate(objs)
# update related thumbnails
for element in self.get_element_types_using_profile(profile):
tool.Model.update_thumbnail_for_element(element, refresh=True)
def regenerate_from_profile(self, usecase_path, ifc_file, settings):
self.file = ifc_file
objs = []
@@ -165,9 +174,10 @@ class DumbProfileRegenerator:
def get_elements_using_profile(self, profile):
results = []
for profile_set in [
profile_sets = [
mp.ToMaterialProfileSet[0] for mp in self.file.get_inverse(profile) if mp.is_a("IfcMaterialProfile")
]:
]
for profile_set in profile_sets:
for inverse in self.file.get_inverse(profile_set):
if not inverse.is_a("IfcMaterialProfileSetUsage"):
continue
@@ -181,6 +191,18 @@ class DumbProfileRegenerator:
results.extend(rel.RelatedObjects)
return results
def get_element_types_using_profile(self, profile):
results = []
profile_sets = [
mp.ToMaterialProfileSet[0] for mp in self.file.get_inverse(profile) if mp.is_a("IfcMaterialProfile")
]
for profile_set in profile_sets:
for inverse in self.file.get_inverse(profile_set):
if not inverse.is_a("IfcRelAssociatesMaterial"):
continue
results.extend(inverse.RelatedObjects)
return results
def regenerate_from_type(self, usecase_path, ifc_file, settings):
obj = tool.Ifc.get_object(settings["related_object"])
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
@@ -164,7 +164,7 @@ class BIM_PT_project(Panel):
op.should_save_as = False
op = row.operator("export_ifc.bim", icon="FILE_TICK", text="Save As")
op.should_save_as = True
row.operator("bim.unload_project", text="", icon="X")
row.operator("bim.unload_project", text="", icon="CANCEL")
def draw_create_project_ui(self, context):
props = context.scene.BIMProperties
+11
View File
@@ -109,3 +109,14 @@ def remove_brick(ifc, brick, library=None, brick_uri=None):
ifc.run("library.remove_reference", reference=reference)
brick.remove_brick(brick_uri)
brick.run_refresh_brick_viewer()
def undo_brick(brick):
brick.undo_brick()
brick.run_refresh_brick_viewer()
def redo_brick(brick):
brick.redo_brick()
brick.run_refresh_brick_viewer()
def serialize_brick(brick, file_name="BlenderBIMSerializeTest.ttl"):
brick.serialize_brick(file_name)
+56 -22
View File
@@ -25,6 +25,7 @@ import blenderbim.tool as tool
try:
import brickschema
import brickschema.persistent
import urllib.parse
from rdflib import Literal, URIRef, Namespace
from rdflib.namespace import RDF
@@ -32,14 +33,20 @@ except:
# See #1860
print("Warning: brickschema not available.")
# silence known rdflib_sqlalchemy TypeError warning
# see https://github.com/BrickSchema/Brick/issues/513#issuecomment-1558493675
import logging
logger = logging.getLogger("rdflib")
logger.setLevel(logging.ERROR)
class Brick(blenderbim.core.tool.Brick):
@classmethod
def add_brick(cls, namespace, brick_class):
ns = Namespace(namespace)
brick = ns[ifcopenshell.guid.expand(ifcopenshell.guid.new())]
BrickStore.graph.add((brick, RDF.type, URIRef(brick_class)))
BrickStore.graph.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Unnamed")))
with BrickStore.graph.new_changeset("PROJECT") as cs:
cs.add((brick, RDF.type, URIRef(brick_class)))
cs.add((brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("Unnamed")))
return str(brick)
@classmethod
@@ -100,7 +107,7 @@ class Brick(blenderbim.core.tool.Brick):
@classmethod
def clear_project(cls):
BrickStore.graph = None
BrickStore.purge()
bpy.context.scene.BIMBrickProperties.active_brick_class == ""
bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.clear()
@@ -249,24 +256,24 @@ class Brick(blenderbim.core.tool.Brick):
@classmethod
def load_brick_file(cls, filepath):
if not BrickStore.schema:
BrickStore.schema = brickschema.Graph()
if not BrickStore.schema: # important check for running under test cases
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "bim", "schema", "Brick.ttl")
BrickStore.schema.load_file(schema_path)
BrickStore.graph = brickschema.Graph().load_file(filepath) + BrickStore.schema
BrickStore.schema = os.path.join(cwd, "..", "bim", "schema", "Brick.ttl")
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
with BrickStore.graph.new_changeset("SCHEMA") as cs:
cs.load_file(BrickStore.schema)
with BrickStore.graph.new_changeset("PROJECT") as cs:
cs.load_file(filepath)
BrickStore.path = filepath
@classmethod
def new_brick_file(cls):
if not BrickStore.schema:
BrickStore.schema = brickschema.Graph()
#BrickStore.schema = brickschema.persistent.VersionedGraphCollection("sqlite://")
if not BrickStore.schema: # important check for running under test cases
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "bim", "schema", "Brick.ttl")
BrickStore.schema.load_file(schema_path)
#BrickStore.schema.load_graph(schema_path)
BrickStore.graph = brickschema.Graph() + BrickStore.schema
BrickStore.schema = os.path.join(cwd, "..", "bim", "schema", "Brick.ttl")
BrickStore.graph = brickschema.persistent.VersionedGraphCollection("sqlite://")
with BrickStore.graph.new_changeset("SCHEMA") as cs:
cs.load_file(BrickStore.schema)
BrickStore.graph.bind("digitaltwin", Namespace("https://example.org/digitaltwin#"))
BrickStore.graph.bind("brick", Namespace("https://brickschema.org/schema/Brick#"))
BrickStore.graph.bind("rdfs", Namespace("http://www.w3.org/2000/01/rdf-schema#"))
@@ -281,8 +288,10 @@ class Brick(blenderbim.core.tool.Brick):
@classmethod
def remove_brick(cls, brick_uri):
for triple in BrickStore.graph.triples((URIRef(brick_uri), None, None)):
BrickStore.graph.remove(triple)
if(BrickStore.graph.triples((URIRef(brick_uri), None, None))):
with BrickStore.graph.new_changeset("PROJECT") as cs:
for triple in BrickStore.graph.triples((URIRef(brick_uri), None, None)):
cs.remove(triple)
@classmethod
def run_assign_brick_reference(cls, element=None, library=None, brick_uri=None):
@@ -308,14 +317,39 @@ class Brick(blenderbim.core.tool.Brick):
def set_active_brick_class(cls, brick_class):
bpy.context.scene.BIMBrickProperties.active_brick_class = brick_class
@classmethod
def undo_brick(cls):
if(len(BrickStore.graph.versions()) > 1):
BrickStore.graph.undo()
@classmethod
def redo_brick(cls):
with BrickStore.graph.conn() as conn:
redo_record = conn.execute(
"SELECT * from redos " "ORDER BY timestamp ASC LIMIT 1"
).fetchone()
if redo_record is not None:
BrickStore.graph.redo()
@classmethod
def serialize_brick(cls, file_name):
#temporary file path, could either be user selected for "save as" or use the BrickStore.path for simply "save"
cwd = os.path.dirname(os.path.realpath(__file__))
dest = os.path.join(cwd, "..", "bim", "schema", file_name)
BrickStore.get_project().serialize(destination=dest, format="turtle")
class BrickStore:
schema = None
graph = None
path = None
schema = None # this is now a os path
path = None # file path if the project was loaded in
graph = None # this is the VersionedGraphCollection with 2 arbitrarily named graphs: "schema" and "project"
# "SCHEMA" holds the Brick.ttl metadata; "PROJECT" holds all the authored entities
@staticmethod
def purge():
BrickStore.schema = None
BrickStore.graph = None
BrickStore.path = None
BrickStore.path = None
@classmethod
def get_project(cls):
return BrickStore.graph.graph_at(graph="PROJECT")
-1
View File
@@ -202,7 +202,6 @@ class Loader(blenderbim.core.tool.Loader):
ifc_path = Path(tool.Ifc.get_path())
image_url = ifc_path.parent / image_url
# import pdb; pdb.set_trace()
if not image_url.exists():
print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.")
continue
+99
View File
@@ -29,6 +29,7 @@ from mathutils import Matrix, Vector
from blenderbim.bim import import_ifc
from blenderbim.bim.module.geometry.helper import Helper
import collections
from blenderbim.bim.module.model.data import AuthoringData
class Model(blenderbim.core.tool.Model):
@@ -630,3 +631,101 @@ class Model(blenderbim.core.tool.Model):
is_global=True,
should_sync_changes_first=False,
)
@classmethod
def update_thumbnail_for_element(cls, element, refresh=False):
if bpy.app.background:
return
from PIL import Image, ImageDraw
obj = tool.Ifc.get_object(element)
if not obj:
return # Nothing to process
if not refresh and element.id() in AuthoringData.type_thumbnails:
return # Already processed
obj.asset_generate_preview()
while not obj.preview:
pass
# if object has .data we can use default blender .asset_generate_preview()
if not obj.data:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
size = 128
img = Image.new("RGBA", (size, size))
draw = ImageDraw.Draw(img)
material = ifcopenshell.util.element.get_material(element)
if material and material.is_a("IfcMaterialProfileSet"):
profile = material.MaterialProfiles[0].Profile
tool.Profile.draw_image_for_ifc_profile(draw, profile, size)
elif material and material.is_a("IfcMaterialLayerSet"):
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
total_thickness = sum(thicknesses)
si_total_thickness = total_thickness * unit_scale
if si_total_thickness <= 0.051:
width = 10
elif si_total_thickness <= 0.11:
width = 20
elif si_total_thickness <= 0.21:
width = 30
elif si_total_thickness <= 0.31:
width = 40
else:
width = 50
height = 100
is_horizontal = False
if element.is_a("IfcSlabType"):
is_horizontal = True
parametric = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric")
if parametric:
layer_set_direction = parametric.get("LayerSetDirection", None)
if layer_set_direction == "AXIS2":
is_horizontal = False
elif layer_set_direction == "AXIS3":
is_horizontal = True
if is_horizontal:
width, height = height, width
x_offset = (size / 2) - (width / 2)
y_offset = (size / 2) - (height / 2)
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
current_thickness = 0
del thicknesses[-1]
for thickness in thicknesses:
current_thickness += thickness
if element.is_a("IfcSlabType"):
y = (current_thickness / total_thickness) * height
line = [x_offset, y_offset + y, x_offset + width, y_offset + y]
else:
x = (current_thickness / total_thickness) * width
line = [x_offset + x, y_offset, x_offset + x, y_offset + height]
draw.line(line, fill="white", width=2)
elif False:
# TODO: things like parametric duct segments
pass
elif not element.RepresentationMaps:
# Empties are represented by a generic thumbnail
width = height = 100
x_offset = (size / 2) - (width / 2)
y_offset = (size / 2) - (height / 2)
draw.line([x_offset, y_offset, width + x_offset, height + y_offset], fill="white", width=2)
draw.line([x_offset, y_offset + height, width + x_offset, y_offset], fill="white", width=2)
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
else:
draw.line([0, 0, size, size], fill="red", width=2)
draw.line([0, size, size, 0], fill="red", width=2)
pixels = [item for sublist in img.getdata() for item in sublist]
obj.preview.image_size = size, size
obj.preview.image_pixels_float = pixels
AuthoringData.type_thumbnails[element.id()] = obj.preview.icon_id
+2 -1
View File
@@ -75,7 +75,8 @@ def an_empty_blender_session():
# default project settings
bpy.context.scene.unit_settings.system = "METRIC"
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bpy.context.scene.BIMProjectProperties.template_file = '0'
bpy.context.scene.BIMProjectProperties.template_file = "0"
bpy.context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound = False
@given("an empty IFC project")
+2 -6
View File
@@ -307,10 +307,8 @@ class TestImportBrickItems(NewFile):
class TestLoadBrickFile(NewFile):
def test_run(self):
# We stub the schema to make tests run faster
BrickStore.schema = brickschema.Graph()
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "files", "BrickStub.ttl")
BrickStore.schema.load_file(schema_path)
BrickStore.schema = os.path.join(cwd, "..", "files", "BrickStub.ttl")
# This is the actual test
cwd = os.path.dirname(os.path.realpath(__file__))
@@ -322,10 +320,8 @@ class TestLoadBrickFile(NewFile):
class TestNewBrickFile(NewFile):
def test_run(self):
# We stub the schema to make tests run faster
BrickStore.schema = brickschema.Graph()
cwd = os.path.dirname(os.path.realpath(__file__))
schema_path = os.path.join(cwd, "..", "files", "BrickStub.ttl")
BrickStore.schema.load_file(schema_path)
BrickStore.schema = os.path.join(cwd, "..", "files", "BrickStub.ttl")
# This is the actual test
subject.new_brick_file()
+9 -3
View File
@@ -122,14 +122,20 @@ class Clasher:
for clash in clash_set["clashes"].values():
title = f'{clash["a_ifc_class"]}/{clash["a_name"]} and {clash["b_ifc_class"]}/{clash["b_name"]}'
topic = bcfxml.add_topic(title, title, "IfcClash")
topic.add_viewpoint_from_point_and_guids(
viewpoint = topic.add_viewpoint_from_point_and_guids(
np.array(clash["position"]), clash["a_global_id"], clash["b_global_id"],
)
snapshot = self.get_viewpoint_snapshot(viewpoint)
if snapshot:
topic.markup.viewpoints[0].snapshot = snapshot[0]
viewpoint.snapshot = snapshot[1]
suffix = f".{i}" if i else ""
bcfxml.save_project(f"{self.settings.output}{suffix}")
def get_viewpoint_snapshot(self, viewpoint, mat):
return None # Possible to overload this function in a GUI application if used as a library
def get_viewpoint_snapshot(self, viewpoint):
# Possible to overload this function in a GUI application if used as a library.
# Should return a tuple of (filename, bytes).
return None
def export_json(self):
clash_sets = self.clash_sets.copy()
+5 -3
View File
@@ -29,9 +29,10 @@ import multiprocessing
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.element
import ifcopenshell.util.selector
import ifcopenshell.util.placement
import ifcopenshell.util.classification
import ifcopenshell.util.selector
import ifcopenshell.util.representation
from deepdiff import DeepDiff
@@ -141,8 +142,9 @@ class IfcDiff:
continue
if should_check_geometry:
# Option 1: check everything heuristically using the iterator (seems faster)
potential_old_changes.append(old)
potential_new_changes.append(new)
if ifcopenshell.util.representation.get_representation(new, "Model", "Body", "MODEL_VIEW"):
potential_old_changes.append(old)
potential_new_changes.append(new)
# Option 2: check first using Python, then fallback to iterator (twice as slow)
# diff = self.diff_element_basic_geometry(old, new)
# if diff: