mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Refactor: Improve profile preview decorator to prevent GPU context errors
This commit is contained in:
@@ -128,6 +128,7 @@ classes = (
|
||||
prop.SnapMousePoint,
|
||||
prop.PolylinePoint,
|
||||
prop.Polyline,
|
||||
prop.ProductPreviewItem,
|
||||
prop.BIMModelProperties,
|
||||
prop.BIMArrayProperties,
|
||||
prop.BIMStairProperties,
|
||||
@@ -137,6 +138,7 @@ classes = (
|
||||
prop.BIMRailingProperties,
|
||||
prop.BIMRoofProperties,
|
||||
prop.BIMPolylineProperties,
|
||||
prop.BIMProductPreviewProperties,
|
||||
ui.BIM_PT_array,
|
||||
ui.BIM_PT_stair,
|
||||
ui.BIM_PT_sverchok,
|
||||
@@ -219,6 +221,7 @@ def register():
|
||||
|
||||
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
||||
bpy.types.Scene.BIMPolylineProperties = bpy.props.PointerProperty(type=prop.BIMPolylineProperties)
|
||||
bpy.types.Scene.BIMProductPreviewProperties = bpy.props.PointerProperty(type=prop.BIMProductPreviewProperties)
|
||||
bpy.types.Object.BIMArrayProperties = bpy.props.PointerProperty(type=prop.BIMArrayProperties)
|
||||
bpy.types.Object.BIMStairProperties = bpy.props.PointerProperty(type=prop.BIMStairProperties)
|
||||
bpy.types.Object.BIMSverchokProperties = bpy.props.PointerProperty(type=prop.BIMSverchokProperties)
|
||||
@@ -249,6 +252,7 @@ def unregister():
|
||||
|
||||
del bpy.types.Scene.BIMModelProperties
|
||||
del bpy.types.Scene.BIMPolylineProperties
|
||||
del bpy.types.Scene.BIMProductPreviewProperties
|
||||
del bpy.types.Object.BIMArrayProperties
|
||||
del bpy.types.Object.BIMStairProperties
|
||||
del bpy.types.Object.BIMSverchokProperties
|
||||
|
||||
@@ -786,6 +786,10 @@ class ProductDecorator:
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
props = bpy.context.scene.BIMProductPreviewProperties # updated by model/polyline.py
|
||||
props.verts.clear()
|
||||
props.edges.clear()
|
||||
props.tris.clear()
|
||||
for handler in cls.handlers:
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(handler, "WINDOW")
|
||||
@@ -978,127 +982,12 @@ class ProductDecorator:
|
||||
data["verts"] = [tuple(rot_mat @ (Vector((v[0], v[1], (v[2] + rl)))) + mouse_point) for v in data["verts"]]
|
||||
return data
|
||||
|
||||
def get_profile_preview_data(self, context, relating_type):
|
||||
material = ifcopenshell.util.element.get_material(relating_type)
|
||||
try:
|
||||
profile_curve = material.MaterialProfiles[0].Profile
|
||||
except:
|
||||
return {}
|
||||
|
||||
model_props = context.scene.BIMModelProperties
|
||||
cardinal_point = model_props.cardinal_point
|
||||
|
||||
polyline_verts = []
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
if len(polyline_points) < 2:
|
||||
return
|
||||
for point in polyline_points:
|
||||
polyline_verts.append(Vector((point.x, point.y, point.z)))
|
||||
polyline_edges = [(i, i+1) for i in range(len(polyline_verts)-1)]
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile_curve)
|
||||
|
||||
verts = shape.verts
|
||||
if not verts:
|
||||
raise RuntimeError("Profile shape has no vertices, it probably is invalid.")
|
||||
|
||||
edges = shape.edges
|
||||
|
||||
grouped_verts = [[verts[i], verts[i + 1], 0] for i in range(0, len(verts), 3)]
|
||||
grouped_edges = [[edges[i], edges[i + 1]] for i in range(0, len(edges), 2)]
|
||||
|
||||
# Create offsets based on cardinal point
|
||||
min_x = min(v[0] for v in grouped_verts)
|
||||
max_x = max(v[0] for v in grouped_verts)
|
||||
min_y = min(v[1] for v in grouped_verts)
|
||||
max_y = max(v[1] for v in grouped_verts)
|
||||
|
||||
x_offset = (max_x - min_x) / 2
|
||||
y_offset = (max_y - min_y) / 2
|
||||
|
||||
match cardinal_point:
|
||||
case "1":
|
||||
grouped_verts = [(v[0] - x_offset, v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "2":
|
||||
grouped_verts = [(v[0], v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "3":
|
||||
grouped_verts = [(v[0] + x_offset, v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "4":
|
||||
grouped_verts = [(v[0] - x_offset, v[1], v[2]) for v in grouped_verts]
|
||||
case "5":
|
||||
grouped_verts = [(v[0], v[1], v[2]) for v in grouped_verts]
|
||||
case "6":
|
||||
grouped_verts = [(v[0] + x_offset, v[1], v[2]) for v in grouped_verts]
|
||||
case "7":
|
||||
grouped_verts = [(v[0] - x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
case "8":
|
||||
grouped_verts = [(v[0], v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
case "9":
|
||||
grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
|
||||
|
||||
scale_mat = Matrix.Scale(-1, 4, (1.0, 0.0, 0.0))
|
||||
grouped_verts = [scale_mat @ Vector(v) for v in grouped_verts]
|
||||
profile_curve = bpy.data.curves.new("Profile", type='CURVE')
|
||||
profile_curve.dimensions = "2D"
|
||||
profile_curve.splines.new('POLY')
|
||||
profile_curve.splines[0].points.add(len(grouped_verts) - 1)
|
||||
|
||||
for i, point in enumerate(profile_curve.splines[0].points):
|
||||
point.co = Vector((*grouped_verts[i], 0))
|
||||
|
||||
profile_obj = bpy.data.objects.new("Profile", profile_curve)
|
||||
|
||||
preview_curve = bpy.data.curves.new("Polyline", type='CURVE')
|
||||
preview_curve.dimensions = "2D"
|
||||
preview_curve.splines.new('POLY')
|
||||
preview_curve.splines[0].points.add(len(polyline_verts) - 1)
|
||||
for i, point in enumerate(preview_curve.splines[0].points):
|
||||
point.co = Vector((*polyline_verts[i], 0))
|
||||
preview_curve.splines[0].use_smooth = False
|
||||
preview_curve.bevel_mode = "OBJECT"
|
||||
preview_curve.bevel_object = profile_obj
|
||||
|
||||
preview_obj = bpy.data.objects.new("Preview", preview_curve)
|
||||
context.scene.collection.objects.link(preview_obj)
|
||||
bpy.context.view_layer.objects.active = preview_obj
|
||||
selection = preview_obj.select_get()
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
preview_obj.select_set(True)
|
||||
bpy.ops.object.convert(target="MESH")
|
||||
preview_obj = bpy.data.objects["Preview"]
|
||||
|
||||
bm = bmesh.new()
|
||||
new_verts = [bm.verts.new(v.co) for v in preview_obj.data.vertices]
|
||||
index = [[v for v in edge.vertices] for edge in preview_obj.data.edges]
|
||||
new_edges = [bm.edges.new((new_verts[i[0]], new_verts[i[1]])) for i in index]
|
||||
for face in preview_obj.data.polygons:
|
||||
verts = [new_verts[i] for i in face.vertices]
|
||||
bm.faces.new(verts)
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
|
||||
def get_profile_preview_data(self, context):
|
||||
props = context.scene.BIMProductPreviewProperties
|
||||
data = {}
|
||||
data["verts"] = [tuple(v.co) for v in bm.verts]
|
||||
data["edges"] = [(edge.verts[0].index, edge.verts[1].index) for edge in bm.edges]
|
||||
data["tris"] = tris
|
||||
|
||||
bpy.data.objects.remove(bpy.data.objects[preview_obj.name], do_unlink=True)
|
||||
bpy.data.objects.remove(bpy.data.objects[profile_obj.name], do_unlink=True)
|
||||
try:
|
||||
bpy.data.curves.remove(profile_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
bpy.data.curves.remove(preview_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
bm.free()
|
||||
|
||||
data["verts"] = [(*v.value,) for v in props.verts]
|
||||
data["edges"] = [(int(e.tvalue[0]),int(e.tvalue[1])) for e in props.edges]
|
||||
data["tris"] = [(int(t.value[0]), int(t.value[1]), int(t.value[2])) for t in props.tris]
|
||||
return data
|
||||
|
||||
def draw_product_preview(self, context):
|
||||
@@ -1144,7 +1033,7 @@ class ProductDecorator:
|
||||
|
||||
# Profile type products
|
||||
self.line_shader.uniform_float("lineWidth", 0.5)
|
||||
product_preview_data = self.get_profile_preview_data(context, self.relating_type)
|
||||
product_preview_data = self.get_profile_preview_data(context)
|
||||
if product_preview_data:
|
||||
self.draw_batch("LINES", product_preview_data["verts"], decorator_color, product_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
|
||||
@@ -43,6 +43,150 @@ from typing import Optional, Union, Literal
|
||||
from lark import Lark, Transformer
|
||||
|
||||
|
||||
def get_profile_preview_data(context, relating_type):
|
||||
material = ifcopenshell.util.element.get_material(relating_type)
|
||||
try:
|
||||
profile_curve = material.MaterialProfiles[0].Profile
|
||||
except:
|
||||
return {}
|
||||
|
||||
model_props = context.scene.BIMModelProperties
|
||||
cardinal_point = model_props.cardinal_point
|
||||
|
||||
polyline_verts = []
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
if len(polyline_points) < 2:
|
||||
return
|
||||
for point in polyline_points:
|
||||
polyline_verts.append(Vector((point.x, point.y, point.z)))
|
||||
polyline_edges = [(i, i+1) for i in range(len(polyline_verts)-1)]
|
||||
|
||||
# Get profile shape
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile_curve)
|
||||
|
||||
verts = shape.verts
|
||||
if not verts:
|
||||
raise RuntimeError("Profile shape has no vertices, it probably is invalid.")
|
||||
|
||||
edges = shape.edges
|
||||
|
||||
grouped_verts = [[verts[i], verts[i + 1], 0] for i in range(0, len(verts), 3)]
|
||||
grouped_edges = [[edges[i], edges[i + 1]] for i in range(0, len(edges), 2)]
|
||||
|
||||
# Create offsets based on cardinal point
|
||||
min_x = min(v[0] for v in grouped_verts)
|
||||
max_x = max(v[0] for v in grouped_verts)
|
||||
min_y = min(v[1] for v in grouped_verts)
|
||||
max_y = max(v[1] for v in grouped_verts)
|
||||
|
||||
x_offset = (max_x - min_x) / 2
|
||||
y_offset = (max_y - min_y) / 2
|
||||
|
||||
match cardinal_point:
|
||||
case "1":
|
||||
grouped_verts = [(v[0] - x_offset, v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "2":
|
||||
grouped_verts = [(v[0], v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "3":
|
||||
grouped_verts = [(v[0] + x_offset, v[1] + y_offset, v[2]) for v in grouped_verts]
|
||||
case "4":
|
||||
grouped_verts = [(v[0] - x_offset, v[1], v[2]) for v in grouped_verts]
|
||||
case "5":
|
||||
grouped_verts = [(v[0], v[1], v[2]) for v in grouped_verts]
|
||||
case "6":
|
||||
grouped_verts = [(v[0] + x_offset, v[1], v[2]) for v in grouped_verts]
|
||||
case "7":
|
||||
grouped_verts = [(v[0] - x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
case "8":
|
||||
grouped_verts = [(v[0], v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
case "9":
|
||||
grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
|
||||
|
||||
# Create profile curve
|
||||
scale_mat = Matrix.Scale(-1, 4, (1.0, 0.0, 0.0))
|
||||
grouped_verts = [scale_mat @ Vector(v) for v in grouped_verts]
|
||||
profile_curve = bpy.data.curves.new("Profile", type='CURVE')
|
||||
profile_curve.dimensions = "2D"
|
||||
profile_curve.splines.new('POLY')
|
||||
profile_curve.splines[0].points.add(len(grouped_verts))
|
||||
|
||||
for i, point in enumerate(profile_curve.splines[0].points):
|
||||
if i == len(grouped_verts): # Close curve
|
||||
point.co = Vector((*grouped_verts[0], 0))
|
||||
continue
|
||||
point.co = Vector((*grouped_verts[i], 0))
|
||||
profile_obj = bpy.data.objects.new("Profile", profile_curve)
|
||||
|
||||
# Create path curve with profile object as bevel
|
||||
path_curve = bpy.data.curves.new("Polyline", type='CURVE')
|
||||
path_curve.dimensions = "2D"
|
||||
path_curve.splines.new('POLY')
|
||||
path_curve.splines[0].points.add(len(polyline_verts) - 1)
|
||||
for i, point in enumerate(path_curve.splines[0].points):
|
||||
point.co = Vector((*polyline_verts[i], 0))
|
||||
path_curve.splines[0].use_smooth = False
|
||||
path_curve.bevel_mode = "OBJECT"
|
||||
path_curve.bevel_object = profile_obj
|
||||
|
||||
# Convert path curve to mesh
|
||||
# This operation throws a warning when done during gpu drawing, so it was removed from the decorator file to be handled here
|
||||
path_obj = bpy.data.objects.new("Preview", path_curve)
|
||||
context.scene.collection.objects.link(path_obj)
|
||||
bpy.context.view_layer.objects.active = path_obj
|
||||
dg = context.evaluated_depsgraph_get()
|
||||
path_obj = path_obj.evaluated_get(dg)
|
||||
me = path_obj.to_mesh()
|
||||
|
||||
# Create bmesh from path mesh
|
||||
bm = bmesh.new()
|
||||
new_verts = [bm.verts.new(v.co) for v in me.vertices]
|
||||
index = [[v for v in edge.vertices] for edge in me.edges]
|
||||
new_edges = [bm.edges.new((new_verts[i[0]], new_verts[i[1]])) for i in index]
|
||||
for face in me.polygons:
|
||||
verts = [new_verts[i] for i in face.vertices]
|
||||
bm.faces.new(verts)
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
|
||||
bpy.data.objects.remove(bpy.data.objects[path_obj.name], do_unlink=True)
|
||||
bpy.data.objects.remove(bpy.data.objects[profile_obj.name], do_unlink=True)
|
||||
try:
|
||||
bpy.data.curves.remove(profile_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
bpy.data.curves.remove(path_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {}
|
||||
data["verts"] = [tuple(v.co) for v in bm.verts]
|
||||
data["edges"] = [(edge.verts[0].index, edge.verts[1].index) for edge in bm.edges]
|
||||
data["tris"] = tris
|
||||
|
||||
# Update properties so it can be used by the decorator
|
||||
props = context.scene.BIMProductPreviewProperties
|
||||
props.verts.clear()
|
||||
props.edges.clear()
|
||||
props.tris.clear()
|
||||
|
||||
for vert in data["verts"]:
|
||||
v = props.verts.add()
|
||||
v.value = vert
|
||||
for edge in data["edges"]:
|
||||
e = props.edges.add()
|
||||
e.tvalue = edge
|
||||
for tri in data["tris"]:
|
||||
t = props.tris.add()
|
||||
t.value = tri
|
||||
|
||||
bm.free()
|
||||
|
||||
class PolylineOperator:
|
||||
# TODO Fill doc strings
|
||||
""" """
|
||||
@@ -387,6 +531,10 @@ class PolylineOperator:
|
||||
tool.Polyline.remove_last_polyline_point()
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def get_product_preview_data(self, context: bpy.types.Context, relating_type: ifcopenshell.entity_isntance):
|
||||
if relating_type.is_a() in ["IfcBeamType", "IfcCoveringType", "IfcMemberType"]:
|
||||
return get_profile_preview_data(context, relating_type)
|
||||
|
||||
def modal(self, context: bpy.types.Context, event: bpy.types.Event) -> Union[set[str], None]:
|
||||
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
@@ -1207,6 +1207,8 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
|
||||
|
||||
self.handle_inserting_polyline(context, event)
|
||||
|
||||
self.get_product_preview_data(context, self.relating_type)
|
||||
|
||||
cancel = self.handle_cancelation(context, event)
|
||||
if cancel is not None:
|
||||
ProductDecorator.uninstall()
|
||||
|
||||
@@ -820,3 +820,13 @@ class BIMPolylineProperties(PropertyGroup):
|
||||
snap_mouse_ref: bpy.props.CollectionProperty(type=SnapMousePoint)
|
||||
insertion_polyline: bpy.props.CollectionProperty(type=Polyline)
|
||||
measurement_polyline: bpy.props.CollectionProperty(type=Polyline)
|
||||
|
||||
class ProductPreviewItem(PropertyGroup):
|
||||
value: bpy.props.FloatVectorProperty()
|
||||
tvalue: bpy.props.FloatVectorProperty(size=2)
|
||||
|
||||
class BIMProductPreviewProperties(PropertyGroup):
|
||||
verts: bpy.props.CollectionProperty(type=ProductPreviewItem)
|
||||
edges: bpy.props.CollectionProperty(type=ProductPreviewItem)
|
||||
tris: bpy.props.CollectionProperty(type=ProductPreviewItem)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user