New hacktastic IFC patch to fix ArchiCAD spaces being loaded into Revit.

This commit is contained in:
Dion Moult
2021-10-27 12:29:40 +11:00
parent f69de3324a
commit 52e8ac386e
@@ -0,0 +1,79 @@
class Patcher:
def __init__(self, src, file, logger, args=None):
self.src = src
self.file = file
self.logger = logger
self.args = args
def patch(self):
# The underlying problem is that Revit does not bring in IFC spaces as
# spaces / rooms in Revit when you link an IFC in Revit. This has been
# broken for at least 3 years and counting. This is a problem typically
# for ArchiCAD architects who want to send rooms to MEP folks using
# Revit.
#
# See bug: https://github.com/Autodesk/revit-ifc/issues/15
#
# The solution is to open an IFC in Revit instead of linking it, which
# will convert IFC spaces into Revit rooms. However, there are very
# specific scenarios where Revit will convert these rooms, which have
# been painstakingly reverse engineered through trial and error.
# Firstly, the rooms should have a lower bound with a Z value matching
# the Z value of the storey it is on. Secondly, although faceted breps
# do work in some scenarios (I assume Revit has an internal topological
# analysis tool), conversion to an extruded area solid yield much more
# robust results. Finally, changing the Precision value to an obscene
# number very strangely seems to cause a lot more rooms to be converted
# successfully.
#
# This patch is designed to only work on ArchiCAD IFC exports where the
# only contents of the IFC is IFC space and _nothing else_. It also
# requires you to run it using Blender, as the geometric modification
# uses the Blender geometry engine.
import bpy
from blenderbim.bim.ifc import IfcStore
from mathutils import Vector, Matrix
if len(bpy.data.objects) > 0:
while bpy.data.objects:
bpy.data.objects.remove(bpy.data.objects[0])
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
bpy.ops.bim.load_project(filepath=self.src)
def recalculate_origin(wall):
new_origin = wall.matrix_world @ Vector(wall.bound_box[0])
if (wall.matrix_world.translation - new_origin).length < 0.001:
return
wall.data.transform(
Matrix.Translation(
(wall.matrix_world.inverted().to_quaternion() @ (wall.matrix_world.translation - new_origin))
)
)
wall.matrix_world.translation = new_origin
for obj in bpy.context.visible_objects:
if "IfcBuildingStorey" in obj.name and len(bpy.data.collections.get(obj.name).children) > 0:
target_z = obj.location.z
for obj in bpy.context.visible_objects:
bpy.context.view_layer.update()
if "IfcSpace" not in obj.name:
continue
local_target_z = (obj.matrix_world.inverted() @ Vector((0, 0, target_z))).z
local_target_zup = (obj.matrix_world.inverted() @ Vector((0, 0, target_z + 3))).z
for v in obj.data.vertices:
if round(v.co.z, 3) == 0:
v.co.z = local_target_z
bpy.context.view_layer.update()
recalculate_origin(obj)
obj.select_set(True)
bpy.ops.bim.update_representation(
ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
)
for context in IfcStore.get_file().by_type("IfcGeometricRepresentationContext"):
if context.Precision:
context.Precision = 10
self.file = IfcStore.get_file()