mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 14:31:39 +00:00
See #1227. Implement wall layers in SVG drawings.
This commit is contained in:
@@ -1760,19 +1760,19 @@ class CutDecorator:
|
|||||||
prev_co = None
|
prev_co = None
|
||||||
if not usage:
|
if not usage:
|
||||||
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
||||||
no = self.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
elif usage.LayerSetDirection == "AXIS2":
|
elif usage.LayerSetDirection == "AXIS2":
|
||||||
co = Vector((0.0, offset, 0.0))
|
co = Vector((0.0, offset, 0.0))
|
||||||
no = self.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
||||||
elif usage.LayerSetDirection == "AXIS3":
|
elif usage.LayerSetDirection == "AXIS3":
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = self.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([0.0, 0.0, 1.0])
|
no = Vector([0.0, 0.0, 1.0])
|
||||||
elif usage.LayerSetDirection == "AXIS1":
|
elif usage.LayerSetDirection == "AXIS1":
|
||||||
co = Vector((0.0, 0.0, offset))
|
co = Vector((0.0, 0.0, offset))
|
||||||
no = self.get_extrusion_vector(element).normalized()
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
no = Vector([1.0, 0.0, 0.0])
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
no *= sense_factor
|
no *= sense_factor
|
||||||
slice_plane_geom = []
|
slice_plane_geom = []
|
||||||
@@ -1797,19 +1797,19 @@ class CutDecorator:
|
|||||||
bm_fill, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_outer=True
|
bm_fill, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_outer=True
|
||||||
)
|
)
|
||||||
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||||
fill = bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
||||||
if i != 0:
|
if i != 0:
|
||||||
geom = bm_fill.verts[:] + bm_fill.edges[:] + bm_fill.faces[:]
|
geom = bm_fill.verts[:] + bm_fill.edges[:] + bm_fill.faces[:]
|
||||||
bisect = bmesh.ops.bisect_plane(
|
bisect = bmesh.ops.bisect_plane(
|
||||||
bm_fill, geom=geom, dist=0.0001, plane_co=prev_co, plane_no=no, clear_inner=True
|
bm_fill, geom=geom, dist=0.0001, plane_co=prev_co, plane_no=no, clear_inner=True
|
||||||
)
|
)
|
||||||
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||||
fill = bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
||||||
|
|
||||||
verts, tris = self.bisect_mesh_tris(obj, bm_fill, context.scene.camera)
|
verts, tris = self.bisect_mesh_tris(obj, bm_fill, context.scene.camera)
|
||||||
DecoratorData.fill_cache[element_id].setdefault(colour, []).append((verts, tris))
|
DecoratorData.fill_cache[element_id].setdefault(colour, []).append((verts, tris))
|
||||||
|
|
||||||
verts, edges = self.bisect_mesh(obj, bm, slice_plane_geom, context.scene.camera)
|
verts, edges = tool.Drawing.bisect_bmesh(obj, bm, slice_plane_geom, context.scene.camera)
|
||||||
DecoratorData.slice_cache[element.id()] = (verts, edges)
|
DecoratorData.slice_cache[element.id()] = (verts, edges)
|
||||||
bm_original.free()
|
bm_original.free()
|
||||||
|
|
||||||
@@ -1823,40 +1823,6 @@ class CutDecorator:
|
|||||||
colour = styles[0].SurfaceColour
|
colour = styles[0].SurfaceColour
|
||||||
return (colour.Red, colour.Green, colour.Blue, 1)
|
return (colour.Red, colour.Green, colour.Blue, 1)
|
||||||
|
|
||||||
def get_extrusion_vector(self, wall):
|
|
||||||
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
|
|
||||||
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
|
||||||
while item.is_a("IfcBooleanResult"):
|
|
||||||
item = item.FirstOperand
|
|
||||||
if item.is_a("IfcExtrudedAreaSolid"):
|
|
||||||
return Vector(item.ExtrudedDirection.DirectionRatios)
|
|
||||||
return Vector([0.0, 0.0, 1.0])
|
|
||||||
|
|
||||||
def bisect_mesh(self, obj, bm, geom, camera):
|
|
||||||
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
|
||||||
plane_co = camera_matrix.translation
|
|
||||||
plane_no = camera_matrix.col[2].xyz
|
|
||||||
|
|
||||||
global_offset = camera.matrix_world.col[2].xyz * -camera.data.clip_start
|
|
||||||
|
|
||||||
# Run the bisect operation
|
|
||||||
results = bmesh.ops.bisect_plane(bm, geom=geom, dist=0.0001, plane_co=plane_co, plane_no=plane_no)
|
|
||||||
|
|
||||||
vert_map = {}
|
|
||||||
verts = []
|
|
||||||
edges = []
|
|
||||||
i = 0
|
|
||||||
for geom in results["geom_cut"]:
|
|
||||||
if isinstance(geom, bmesh.types.BMVert):
|
|
||||||
verts.append(tuple((obj.matrix_world @ geom.co) + global_offset))
|
|
||||||
vert_map[geom.index] = i
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
# It seems as though edges always appear after verts
|
|
||||||
edges.append([vert_map[v.index] for v in geom.verts])
|
|
||||||
|
|
||||||
return verts, edges
|
|
||||||
|
|
||||||
def bisect_mesh_tris(self, obj, bm, camera):
|
def bisect_mesh_tris(self, obj, bm, camera):
|
||||||
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
||||||
plane_co = camera_matrix.translation
|
plane_co = camera_matrix.translation
|
||||||
|
|||||||
@@ -562,6 +562,105 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
path.attrib["d"] = d
|
path.attrib["d"] = d
|
||||||
group.append(g)
|
group.append(g)
|
||||||
|
|
||||||
|
def generate_wall_layers(self, context: bpy.types.Context, root):
|
||||||
|
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||||
|
if "projection" in el.get("class", "").split():
|
||||||
|
continue
|
||||||
|
element = self.get_element_by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||||
|
if not (obj := tool.Ifc.get_object(element)):
|
||||||
|
continue
|
||||||
|
if not (material := ifcopenshell.util.element.get_material(element)):
|
||||||
|
continue
|
||||||
|
if material.is_a() not in ("IfcMaterialLayerSet", "IfcMaterialLayerSetUsage"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
|
if material.is_a("IfcMaterialLayerSetUsage"):
|
||||||
|
usage = material
|
||||||
|
layer_set = material.ForLayerSet
|
||||||
|
offset = usage.OffsetFromReferenceLine * self.unit_scale
|
||||||
|
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
||||||
|
elif material.is_a("IfcMaterialLayerSet"):
|
||||||
|
usage = None
|
||||||
|
layer_set = material
|
||||||
|
offset = 0
|
||||||
|
sense_factor = 1
|
||||||
|
|
||||||
|
camera_matrix_i = context.scene.camera.matrix_world.inverted()
|
||||||
|
|
||||||
|
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||||
|
raw_width, raw_height = self.get_camera_dimensions()
|
||||||
|
x_offset = raw_width / 2
|
||||||
|
y_offset = raw_height / 2
|
||||||
|
svg_scale = self.scale * 1000 # IFC is in meters, SVG is in mm
|
||||||
|
|
||||||
|
mesh = obj.data
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
|
prev_co = None
|
||||||
|
if not usage:
|
||||||
|
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
||||||
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
|
co = Vector((0.0, 0.0, offset))
|
||||||
|
elif usage.LayerSetDirection == "AXIS2":
|
||||||
|
co = Vector((0.0, offset, 0.0))
|
||||||
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
|
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
||||||
|
elif usage.LayerSetDirection == "AXIS3":
|
||||||
|
co = Vector((0.0, 0.0, offset))
|
||||||
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
|
no = Vector([0.0, 0.0, 1.0])
|
||||||
|
elif usage.LayerSetDirection == "AXIS1":
|
||||||
|
co = Vector((0.0, 0.0, offset))
|
||||||
|
no = tool.Drawing.get_extrusion_vector(element).normalized()
|
||||||
|
no = Vector([1.0, 0.0, 0.0])
|
||||||
|
no *= sense_factor
|
||||||
|
last_i = len(layer_set.MaterialLayers) - 1
|
||||||
|
for i, layer in enumerate(layer_set.MaterialLayers):
|
||||||
|
prev_co = co.copy()
|
||||||
|
co += no * layer.LayerThickness * self.unit_scale
|
||||||
|
|
||||||
|
bm_fill = bm.copy()
|
||||||
|
if i != last_i:
|
||||||
|
geom = bm_fill.verts[:] + bm_fill.edges[:] + bm_fill.faces[:]
|
||||||
|
bisect = bmesh.ops.bisect_plane(
|
||||||
|
bm_fill, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_outer=True
|
||||||
|
)
|
||||||
|
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||||
|
bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
||||||
|
if i != 0:
|
||||||
|
geom = bm_fill.verts[:] + bm_fill.edges[:] + bm_fill.faces[:]
|
||||||
|
bisect = bmesh.ops.bisect_plane(
|
||||||
|
bm_fill, geom=geom, dist=0.0001, plane_co=prev_co, plane_no=no, clear_inner=True
|
||||||
|
)
|
||||||
|
edges = [g for g in bisect["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
|
||||||
|
bmesh.ops.edgenet_fill(bm_fill, edges=edges)
|
||||||
|
|
||||||
|
geom = bm_fill.verts[:] + bm_fill.edges[:] + bm_fill.faces[:]
|
||||||
|
verts, edges = tool.Drawing.bisect_bmesh(obj, bm_fill, geom, context.scene.camera)
|
||||||
|
|
||||||
|
g = etree.SubElement(root, "{http://www.w3.org/2000/svg}g")
|
||||||
|
g.attrib["{http://www.ifcopenshell.org/ns}guid"] = element.GlobalId
|
||||||
|
g.attrib["{http://www.ifcopenshell.org/ns}name"] = element.Name or ""
|
||||||
|
g.attrib["{http://www.ifcopenshell.org/ns}layer-id"] = str(layer.id())
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for edge in edges:
|
||||||
|
start = [o for o in (camera_matrix_i @ Vector(verts[edge[0]])).xy]
|
||||||
|
end = [o for o in (camera_matrix_i @ Vector(verts[edge[1]])).xy]
|
||||||
|
coords = [start, end]
|
||||||
|
d = " ".join(
|
||||||
|
["L{},{}".format((x_offset + p[0]) * svg_scale, (y_offset - p[1]) * svg_scale) for p in coords]
|
||||||
|
)
|
||||||
|
d = "M{}".format(d[1:])
|
||||||
|
path = etree.SubElement(g, "{http://www.w3.org/2000/svg}path")
|
||||||
|
path.attrib["d"] = d
|
||||||
|
group.append(g)
|
||||||
|
|
||||||
|
bm.free()
|
||||||
|
|
||||||
def generate_freestyle_linework(self, context: bpy.types.Context) -> str | None:
|
def generate_freestyle_linework(self, context: bpy.types.Context) -> str | None:
|
||||||
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasLinework"):
|
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasLinework"):
|
||||||
return
|
return
|
||||||
@@ -769,10 +868,12 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
if self.camera.data.BIMCameraProperties.cut_mode == "BISECT":
|
if self.camera.data.BIMCameraProperties.cut_mode == "BISECT":
|
||||||
self.remove_cut_linework(root)
|
self.remove_cut_linework(root)
|
||||||
self.generate_bisect_linework(context, root)
|
self.generate_bisect_linework(context, root)
|
||||||
|
self.generate_wall_layers(context, root)
|
||||||
self.merge_linework_and_add_metadata(root)
|
self.merge_linework_and_add_metadata(root)
|
||||||
self.move_elements_to_top(root)
|
self.move_elements_to_top(root)
|
||||||
elif self.camera.data.BIMCameraProperties.cut_mode == "OPENCASCADE":
|
elif self.camera.data.BIMCameraProperties.cut_mode == "OPENCASCADE":
|
||||||
self.move_projection_to_bottom(root)
|
self.move_projection_to_bottom(root)
|
||||||
|
self.generate_wall_layers(context, root)
|
||||||
self.merge_linework_and_add_metadata(root)
|
self.merge_linework_and_add_metadata(root)
|
||||||
self.move_elements_to_top(root)
|
self.move_elements_to_top(root)
|
||||||
|
|
||||||
@@ -1101,7 +1202,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
# the style of the face when running tree.select_ray()
|
# the style of the face when running tree.select_ray()
|
||||||
# tree.enable_face_styles(True)
|
# tree.enable_face_styles(True)
|
||||||
|
|
||||||
def get_svg_classes(self, element):
|
def get_svg_classes(self, element, layer=None):
|
||||||
classes = [element.is_a()]
|
classes = [element.is_a()]
|
||||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||||
material_name = ""
|
material_name = ""
|
||||||
@@ -1113,7 +1214,13 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
material_name = tool.Drawing.canonicalise_class_name(material_name)
|
material_name = tool.Drawing.canonicalise_class_name(material_name)
|
||||||
classes.append(f"material-{material_name}")
|
classes.append(f"material-{material_name}")
|
||||||
else:
|
else:
|
||||||
classes.append(f"material-null")
|
classes.append("material-null")
|
||||||
|
|
||||||
|
if layer:
|
||||||
|
classes.append(layer.is_a())
|
||||||
|
material_name = layer.Material.Name or "null"
|
||||||
|
material_name = tool.Drawing.canonicalise_class_name(material_name)
|
||||||
|
classes.append(f"layer-material-{material_name}")
|
||||||
|
|
||||||
for key in self.metadata:
|
for key in self.metadata:
|
||||||
value = ifcopenshell.util.selector.get_element_value(element, key)
|
value = ifcopenshell.util.selector.get_element_value(element, key)
|
||||||
@@ -1152,6 +1259,23 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
def get_element_by_id(self, step_id):
|
||||||
|
try:
|
||||||
|
step_id = int(step_id)
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
return tool.Ifc.get().by_id(step_id)
|
||||||
|
except:
|
||||||
|
props = tool.Project.get_project_props()
|
||||||
|
for link in props.links:
|
||||||
|
if link.name not in IfcStore.session_files:
|
||||||
|
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||||
|
try:
|
||||||
|
return IfcStore.session_files[link.name].by_id(step_id)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
def remove_cut_linework(self, root):
|
def remove_cut_linework(self, root):
|
||||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||||
if "projection" not in el.get("class", "").split():
|
if "projection" not in el.get("class", "").split():
|
||||||
@@ -1163,15 +1287,15 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
join_criteria = join_criteria.split(",")
|
join_criteria = join_criteria.split(",")
|
||||||
else:
|
else:
|
||||||
# Drawing convention states that same objects classes with the same material are merged when cut.
|
# Drawing convention states that same objects classes with the same material are merged when cut.
|
||||||
join_criteria = ["class", "material.Name", "/Pset_.*Common/.Status", "EPset_Status.Status"]
|
join_criteria = ["class", "material.Name", "/Pset_.*Common/.Status", "EPset_Status.Status", "Material.Name"]
|
||||||
|
|
||||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||||
joined_paths = {}
|
joined_paths = {}
|
||||||
self.is_manifold_cache = {}
|
self.is_manifold_cache = {}
|
||||||
|
|
||||||
ifc = tool.Ifc.get()
|
|
||||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||||
element = self.get_element_by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
element = self.get_element_by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||||
|
layer = self.get_element_by_id(el.get("{http://www.ifcopenshell.org/ns}layer-id"))
|
||||||
|
|
||||||
if "projection" in el.get("class", "").split():
|
if "projection" in el.get("class", "").split():
|
||||||
classes = self.get_svg_classes(element)
|
classes = self.get_svg_classes(element)
|
||||||
@@ -1179,7 +1303,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
el.set("class", " ".join(classes))
|
el.set("class", " ".join(classes))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
classes = self.get_svg_classes(element)
|
classes = self.get_svg_classes(element, layer)
|
||||||
classes.append("cut")
|
classes.append("cut")
|
||||||
el.set("class", " ".join(classes))
|
el.set("class", " ".join(classes))
|
||||||
|
|
||||||
@@ -1188,7 +1312,12 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
if not obj: # This is a linked model object. For now, do nothing.
|
if not obj: # This is a linked model object. For now, do nothing.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.is_manifold(obj):
|
if (material := ifcopenshell.util.element.get_material(element)) and material.is_a() in (
|
||||||
|
"IfcMaterialLayerSet",
|
||||||
|
"IfcMaterialLayerSetUsage",
|
||||||
|
):
|
||||||
|
pass # These are always manifold
|
||||||
|
elif not self.is_manifold(obj):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# An element group will contain a bunch of paths representing the
|
# An element group will contain a bunch of paths representing the
|
||||||
@@ -1273,6 +1402,15 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
keys.append(key)
|
keys.append(key)
|
||||||
|
|
||||||
|
if layer:
|
||||||
|
for query in join_criteria:
|
||||||
|
key = ifcopenshell.util.selector.get_element_value(layer, query)
|
||||||
|
print("got layer key", query, key)
|
||||||
|
if isinstance(key, (list, tuple)):
|
||||||
|
keys.extend(key)
|
||||||
|
else:
|
||||||
|
keys.append(key)
|
||||||
|
|
||||||
hash_keys = hash(tuple(keys))
|
hash_keys = hash(tuple(keys))
|
||||||
|
|
||||||
if el.findall("{http://www.w3.org/2000/svg}path"):
|
if el.findall("{http://www.w3.org/2000/svg}path"):
|
||||||
@@ -1289,7 +1427,8 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
for path in el.findall("{http://www.w3.org/2000/svg}path"):
|
for path in el.findall("{http://www.w3.org/2000/svg}path"):
|
||||||
for subpath in path.attrib["d"].split("M")[1:]:
|
for subpath in path.attrib["d"].split("M")[1:]:
|
||||||
subpath_co = "M" + subpath.strip(" Z")
|
subpath_co = "M" + subpath.strip(" Z")
|
||||||
coords = [[float(o) for o in co[1:].split(",")] for co in subpath_co.split()]
|
# Round due to inaccuracies from Blender meshes and bisection
|
||||||
|
coords = [[round(float(o), 3) for o in co[1:].split(",")] for co in subpath_co.split()]
|
||||||
if subpath.strip().lower().endswith("z"):
|
if subpath.strip().lower().endswith("z"):
|
||||||
coords.append(coords[0])
|
coords.append(coords[0])
|
||||||
if len(coords) > 2 and coords[0] == coords[-1]:
|
if len(coords) > 2 and coords[0] == coords[-1]:
|
||||||
|
|||||||
@@ -2112,6 +2112,7 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bisect_mesh(cls, obj: bpy.types.Object, camera: bpy.types.Object) -> tuple[list[Vector], list[list[int]]]:
|
def bisect_mesh(cls, obj: bpy.types.Object, camera: bpy.types.Object) -> tuple[list[Vector], list[list[int]]]:
|
||||||
|
# TODO consolidate with other bisect functions
|
||||||
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
||||||
plane_co = camera_matrix.translation
|
plane_co = camera_matrix.translation
|
||||||
plane_no = camera_matrix.col[2].xyz
|
plane_no = camera_matrix.col[2].xyz
|
||||||
@@ -2121,10 +2122,38 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
return cls.bisect_mesh_with_plane(obj, plane_co, plane_no, global_offset=global_offset)
|
return cls.bisect_mesh_with_plane(obj, plane_co, plane_no, global_offset=global_offset)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def bisect_bmesh(cls, obj, bm, geom, camera):
|
||||||
|
# TODO consolidate with other bisect functions
|
||||||
|
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
||||||
|
plane_co = camera_matrix.translation
|
||||||
|
plane_no = camera_matrix.col[2].xyz
|
||||||
|
|
||||||
|
global_offset = camera.matrix_world.col[2].xyz * -camera.data.clip_start
|
||||||
|
|
||||||
|
# Run the bisect operation
|
||||||
|
results = bmesh.ops.bisect_plane(bm, geom=geom, dist=0.0001, plane_co=plane_co, plane_no=plane_no)
|
||||||
|
|
||||||
|
vert_map = {}
|
||||||
|
verts = []
|
||||||
|
edges = []
|
||||||
|
i = 0
|
||||||
|
for geom in results["geom_cut"]:
|
||||||
|
if isinstance(geom, bmesh.types.BMVert):
|
||||||
|
verts.append(tuple((obj.matrix_world @ geom.co) + global_offset))
|
||||||
|
vert_map[geom.index] = i
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
# It seems as though edges always appear after verts
|
||||||
|
edges.append([vert_map[v.index] for v in geom.verts])
|
||||||
|
|
||||||
|
return verts, edges
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bisect_mesh_with_plane(
|
def bisect_mesh_with_plane(
|
||||||
cls, obj: bpy.types.Object, plane_co: Vector, plane_no: Vector, global_offset: Optional[Vector] = None
|
cls, obj: bpy.types.Object, plane_co: Vector, plane_no: Vector, global_offset: Optional[Vector] = None
|
||||||
) -> tuple[list[Vector], list[list[int]]]:
|
) -> tuple[list[Vector], list[list[int]]]:
|
||||||
|
# TODO consolidate with other bisect functions
|
||||||
if global_offset is None:
|
if global_offset is None:
|
||||||
global_offset = Vector()
|
global_offset = Vector()
|
||||||
|
|
||||||
@@ -2152,6 +2181,16 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
return verts, edges
|
return verts, edges
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_extrusion_vector(cls, wall):
|
||||||
|
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
|
||||||
|
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
||||||
|
while item.is_a("IfcBooleanResult"):
|
||||||
|
item = item.FirstOperand
|
||||||
|
if item.is_a("IfcExtrudedAreaSolid"):
|
||||||
|
return Vector(item.ExtrudedDirection.DirectionRatios)
|
||||||
|
return Vector([0.0, 0.0, 1.0])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_scale_ratio(cls, scale: str) -> float:
|
def get_scale_ratio(cls, scale: str) -> float:
|
||||||
numerator, denominator = scale.split("/")
|
numerator, denominator = scale.split("/")
|
||||||
|
|||||||
Reference in New Issue
Block a user