mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Move product preview logic to its own decorator.
This commit is contained in:
@@ -323,9 +323,6 @@ class PolylineDecorator:
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_snap_point, (context,), "WINDOW", "POST_PIXEL"))
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_measurements, (context,), "WINDOW", "POST_PIXEL"))
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_input_ui, (context,), "WINDOW", "POST_PIXEL"))
|
||||
cls.handlers.append(
|
||||
SpaceView3D.draw_handler_add(handler.draw_product_preview, (context,), "WINDOW", "POST_VIEW")
|
||||
)
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW"))
|
||||
cls.is_installed = True
|
||||
|
||||
@@ -402,358 +399,6 @@ class PolylineDecorator:
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def get_wall_preview_data(cls, context, relating_type):
|
||||
def create_bmesh_from_vertices(vertices):
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in polyline_vertices]
|
||||
if is_closed:
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||
new_edges.append(
|
||||
bm.edges.new((new_verts[-1], new_verts[0]))
|
||||
) # Add an edge between the last an first point to make it closed.
|
||||
else:
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
return bm
|
||||
|
||||
# Get properties from object type
|
||||
layers = tool.Model.get_material_layer_parameters(relating_type)
|
||||
if not layers["thickness"]:
|
||||
return
|
||||
thickness = layers["thickness"]
|
||||
model_props = context.scene.BIMModelProperties
|
||||
direction_sense = model_props.direction_sense
|
||||
direction = 1
|
||||
if direction_sense == "NEGATIVE":
|
||||
direction = -1
|
||||
offset_type = model_props.offset_type
|
||||
offset = 0
|
||||
if offset_type == "CENTER":
|
||||
offset = -thickness / 2
|
||||
elif offset_type == "INTERIOR":
|
||||
offset = -thickness
|
||||
|
||||
unit_system = tool.Drawing.get_unit_system()
|
||||
factor = 1
|
||||
if unit_system == "IMPERIAL":
|
||||
factor = 3.048
|
||||
if unit_system == "METRIC":
|
||||
unit_length = context.scene.unit_settings.length_unit
|
||||
if unit_length == "MILLIMETERS":
|
||||
factor = 1000
|
||||
|
||||
# For the model properties, the offset value should just be converted
|
||||
# However, for the wall preview logic that follows, offset and thickness must change direction
|
||||
model_props.offset = offset * factor
|
||||
thickness *= direction
|
||||
offset *= direction
|
||||
|
||||
height = float(model_props.extrusion_depth)
|
||||
rl = float(model_props.rl1)
|
||||
x_angle = float(model_props.x_angle)
|
||||
angle_distortion = height * tan(x_angle)
|
||||
|
||||
wall_preview_data = {}
|
||||
wall_preview_data["verts"] = []
|
||||
|
||||
# Verts
|
||||
polyline_vertices = []
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
if len(polyline_points) < 2:
|
||||
wall_preview_data = []
|
||||
return
|
||||
for point in polyline_points:
|
||||
polyline_vertices.append(Vector((point.x, point.y, point.z)))
|
||||
|
||||
is_closed = False
|
||||
if (
|
||||
polyline_vertices[0].x == polyline_vertices[-1].x
|
||||
and polyline_vertices[0].y == polyline_vertices[-1].y
|
||||
and polyline_vertices[0].z == polyline_vertices[-1].z
|
||||
):
|
||||
is_closed = True
|
||||
polyline_vertices.pop(-1) # Remove the last point. The edges are going to inform that the shape is closed.
|
||||
|
||||
bm_base = create_bmesh_from_vertices(polyline_vertices)
|
||||
base_vertices = tool.Cad.offset_edges(bm_base, offset)
|
||||
offset_base_verts = tool.Cad.offset_edges(bm_base, thickness + offset)
|
||||
top_vertices = tool.Cad.offset_edges(bm_base, angle_distortion + offset)
|
||||
offset_top_verts = tool.Cad.offset_edges(bm_base, angle_distortion + thickness + offset)
|
||||
if is_closed:
|
||||
base_vertices.append(base_vertices[0])
|
||||
offset_base_verts.append(offset_base_verts[0])
|
||||
top_vertices.append(top_vertices[0])
|
||||
offset_top_verts.append(offset_top_verts[0])
|
||||
|
||||
if offset_base_verts is not None:
|
||||
for v in base_vertices:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl))
|
||||
|
||||
for v in offset_base_verts[::-1]:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl))
|
||||
|
||||
for v in top_vertices:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl + height))
|
||||
|
||||
for v in offset_top_verts[::-1]:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl + height))
|
||||
|
||||
bm_base.free()
|
||||
|
||||
# Edges and Tris
|
||||
points = []
|
||||
side_edges_1 = []
|
||||
side_edges_2 = []
|
||||
base_edges = []
|
||||
|
||||
for i in range(len(wall_preview_data["verts"])):
|
||||
points.append(Vector(wall_preview_data["verts"][i]))
|
||||
|
||||
n = len(points) // 2
|
||||
bottom_side_1 = [[i, (i + 1) % (n)] for i in range((n - 1) // 2)]
|
||||
bottom_side_2 = [[i, (i + 1) % (n)] for i in range(n // 2, n - 1)]
|
||||
bottom_connections = [[i, n - i - 1] for i in range(n // 2)]
|
||||
bottom_loop = bottom_connections + bottom_side_1 + bottom_side_2
|
||||
side_edges_1.extend(bottom_side_1)
|
||||
side_edges_2.extend(bottom_side_2)
|
||||
base_edges.extend(bottom_loop)
|
||||
|
||||
upper_side_1 = [[i + n for i in edges] for edges in bottom_side_1]
|
||||
upper_side_2 = [[i + n for i in edges] for edges in bottom_side_2]
|
||||
upper_loop = [[i + n for i in edges] for edges in bottom_loop]
|
||||
side_edges_1.extend(upper_side_1)
|
||||
side_edges_2.extend(upper_side_2)
|
||||
base_edges.extend(upper_loop)
|
||||
|
||||
loops = [side_edges_1, side_edges_2, base_edges]
|
||||
|
||||
wall_preview_data["edges"] = []
|
||||
wall_preview_data["tris"] = []
|
||||
for i, group in enumerate(loops):
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in points]
|
||||
new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in group]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
|
||||
if i == 2:
|
||||
new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges)
|
||||
new_faces = bmesh.ops.bridge_loops(bm, edges=bm.edges, use_pairs=True, use_cyclic=True)
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
edges = [[v.index for v in e.verts] for e in bm.edges]
|
||||
tris = [[l.vert.index for l in loop] for loop in bm.calc_loop_triangles()]
|
||||
wall_preview_data["edges"].extend(edges)
|
||||
wall_preview_data["tris"].extend(tris)
|
||||
|
||||
wall_preview_data["edges"] = list(set(tuple(e) for e in wall_preview_data["edges"]))
|
||||
wall_preview_data["tris"] = list(set(tuple(t) for t in wall_preview_data["tris"]))
|
||||
|
||||
return wall_preview_data
|
||||
|
||||
def get_product_preview_data(cls, context, relating_type):
|
||||
model_props = context.scene.BIMModelProperties
|
||||
if relating_type.is_a("IfcDoorType"):
|
||||
rl = float(model_props.rl1)
|
||||
elif relating_type.is_a("IfcWindowType"):
|
||||
rl = float(model_props.rl2)
|
||||
else:
|
||||
rl = 0
|
||||
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0]
|
||||
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
|
||||
snap_obj = bpy.data.objects.get(snap_prop.snap_object)
|
||||
snap_element = tool.Ifc.get_entity(snap_obj)
|
||||
rot_mat = Quaternion()
|
||||
if snap_element and snap_element.is_a("IfcWall"):
|
||||
rot_mat = snap_obj.matrix_world.to_quaternion()
|
||||
|
||||
obj_type = tool.Ifc.get_object(relating_type)
|
||||
if obj_type.data:
|
||||
data = ItemDecorator.get_obj_data(obj_type)
|
||||
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)
|
||||
profile = material.MaterialProfiles[0].Profile
|
||||
model_props = context.scene.BIMModelProperties
|
||||
extrusion_depth = model_props.extrusion_depth
|
||||
cardinal_point = model_props.cardinal_point
|
||||
rot_mat = Quaternion()
|
||||
if relating_type.is_a("IfcBeamType"):
|
||||
y_rot = Quaternion((0.0, 1.0, 0.0), radians(90))
|
||||
z_rot = Quaternion((0.0, 0.0, 1.0), radians(90))
|
||||
rot_mat = y_rot @ z_rot
|
||||
# Get profile data
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile)
|
||||
|
||||
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 extrusion bmesh
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in grouped_verts]
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(grouped_verts) - 1)]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
|
||||
new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges)
|
||||
|
||||
new_faces = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
||||
new_verts = [e for e in new_faces["geom"] if isinstance(e, bmesh.types.BMVert)]
|
||||
new_faces = bmesh.ops.translate(bm, verts=new_verts, vec=(0.0, 0.0, extrusion_depth))
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
|
||||
# Create bounding box
|
||||
verts = bm.verts
|
||||
i = len(verts)
|
||||
|
||||
min_x = min(v.co.x for v in verts)
|
||||
max_x = max(v.co.x for v in verts)
|
||||
min_y = min(v.co.y for v in verts)
|
||||
max_y = max(v.co.y for v in verts)
|
||||
min_z = min(v.co.z for v in verts)
|
||||
max_z = max(v.co.z for v in verts)
|
||||
|
||||
bbox_verts = [
|
||||
(min_x, min_y, min_z),
|
||||
(max_x, min_y, min_z),
|
||||
(max_x, max_y, min_z),
|
||||
(min_x, max_y, min_z),
|
||||
(min_x, min_y, max_z),
|
||||
(max_x, min_y, max_z),
|
||||
(max_x, max_y, max_z),
|
||||
(min_x, max_y, max_z),
|
||||
]
|
||||
|
||||
bbox_edges = [
|
||||
(0 + i, 3 + i),
|
||||
(3 + i, 7 + i),
|
||||
(7 + i, 4 + i),
|
||||
(4 + i, 0 + i),
|
||||
(0 + i, 1 + i),
|
||||
(3 + i, 2 + i),
|
||||
(7 + i, 6 + i),
|
||||
(4 + i, 5 + i),
|
||||
(1 + i, 2 + i),
|
||||
(2 + i, 6 + i),
|
||||
(6 + i, 5 + i),
|
||||
(5 + i, 1 + i),
|
||||
]
|
||||
|
||||
# Calculate rotation, mouse position, angle and cardinal point
|
||||
# TODO Angle
|
||||
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0]
|
||||
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
|
||||
data = {}
|
||||
|
||||
verts = [tuple(v.co) for v in verts]
|
||||
verts.extend(bbox_verts)
|
||||
verts = [tuple(rot_mat @ Vector(v)) for v in verts]
|
||||
verts = [tuple(Vector(v) + mouse_point) for v in verts]
|
||||
data["verts"] = verts
|
||||
data["edges"] = bbox_edges
|
||||
# data["edges"] = [(edge.verts[0].index, edge.verts[1].index) for edge in bm.edges]
|
||||
data["tris"] = tris
|
||||
|
||||
bm.free()
|
||||
return data
|
||||
|
||||
def draw_product_preview(self, context):
|
||||
def transparent_color(color, alpha=0.1):
|
||||
color = [i for i in color]
|
||||
color[3] = alpha
|
||||
return color
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
decorator_color = self.addon_prefs.decorator_color_special
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
|
||||
self.relating_type = None
|
||||
props = context.scene.BIMModelProperties
|
||||
relating_type_id = props.relating_type_id
|
||||
if relating_type_id:
|
||||
self.relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
# Wall
|
||||
wall_preview_data = self.get_wall_preview_data(context, self.relating_type)
|
||||
if wall_preview_data:
|
||||
self.draw_batch("LINES", wall_preview_data["verts"], decorator_color, wall_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", wall_preview_data["verts"], transparent_color(decorator_color), wall_preview_data["tris"]
|
||||
)
|
||||
|
||||
# Mesh type products
|
||||
product_preview_data = self.get_product_preview_data(context, self.relating_type)
|
||||
if product_preview_data:
|
||||
self.draw_batch("LINES", product_preview_data["verts"], decorator_color, product_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", product_preview_data["verts"], transparent_color(decorator_color), product_preview_data["tris"]
|
||||
)
|
||||
|
||||
# Profile type products
|
||||
product_preview_data = self.get_profile_preview_data(context, self.relating_type)
|
||||
if product_preview_data:
|
||||
self.draw_batch("LINES", product_preview_data["verts"], decorator_color, product_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", product_preview_data["verts"], transparent_color(decorator_color), product_preview_data["tris"]
|
||||
)
|
||||
|
||||
def draw_input_ui(self, context):
|
||||
texts = {
|
||||
"D": "Distance: ",
|
||||
@@ -1064,3 +709,390 @@ class PolylineDecorator:
|
||||
self.draw_batch("POINTS", polyline_verts, decorator_color_unselected)
|
||||
if len(polyline_verts) > 1:
|
||||
self.draw_batch("LINES", polyline_verts, decorator_color_unselected, polyline_edges)
|
||||
|
||||
|
||||
class ProductDecorator:
|
||||
is_installed = False
|
||||
handlers = []
|
||||
relating_type = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.is_installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.handlers.append(
|
||||
SpaceView3D.draw_handler_add(handler.draw_product_preview, (context,), "WINDOW", "POST_VIEW")
|
||||
)
|
||||
cls.is_installed = True
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
for handler in cls.handlers:
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(handler, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.is_installed = False
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def get_wall_preview_data(cls, context, relating_type):
|
||||
def create_bmesh_from_vertices(vertices):
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in polyline_vertices]
|
||||
if is_closed:
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||
new_edges.append(
|
||||
bm.edges.new((new_verts[-1], new_verts[0]))
|
||||
) # Add an edge between the last an first point to make it closed.
|
||||
else:
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
return bm
|
||||
|
||||
# Get properties from object type
|
||||
layers = tool.Model.get_material_layer_parameters(relating_type)
|
||||
if not layers["thickness"]:
|
||||
return
|
||||
thickness = layers["thickness"]
|
||||
model_props = context.scene.BIMModelProperties
|
||||
direction_sense = model_props.direction_sense
|
||||
direction = 1
|
||||
if direction_sense == "NEGATIVE":
|
||||
direction = -1
|
||||
offset_type = model_props.offset_type
|
||||
offset = 0
|
||||
if offset_type == "CENTER":
|
||||
offset = -thickness / 2
|
||||
elif offset_type == "INTERIOR":
|
||||
offset = -thickness
|
||||
|
||||
unit_system = tool.Drawing.get_unit_system()
|
||||
factor = 1
|
||||
if unit_system == "IMPERIAL":
|
||||
factor = 3.048
|
||||
if unit_system == "METRIC":
|
||||
unit_length = context.scene.unit_settings.length_unit
|
||||
if unit_length == "MILLIMETERS":
|
||||
factor = 1000
|
||||
|
||||
# For the model properties, the offset value should just be converted
|
||||
# However, for the wall preview logic that follows, offset and thickness must change direction
|
||||
model_props.offset = offset * factor
|
||||
thickness *= direction
|
||||
offset *= direction
|
||||
|
||||
height = float(model_props.extrusion_depth)
|
||||
rl = float(model_props.rl1)
|
||||
x_angle = float(model_props.x_angle)
|
||||
angle_distortion = height * tan(x_angle)
|
||||
|
||||
wall_preview_data = {}
|
||||
wall_preview_data["verts"] = []
|
||||
|
||||
# Verts
|
||||
polyline_vertices = []
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
if len(polyline_points) < 2:
|
||||
wall_preview_data = []
|
||||
return
|
||||
for point in polyline_points:
|
||||
polyline_vertices.append(Vector((point.x, point.y, point.z)))
|
||||
|
||||
is_closed = False
|
||||
if (
|
||||
polyline_vertices[0].x == polyline_vertices[-1].x
|
||||
and polyline_vertices[0].y == polyline_vertices[-1].y
|
||||
and polyline_vertices[0].z == polyline_vertices[-1].z
|
||||
):
|
||||
is_closed = True
|
||||
polyline_vertices.pop(-1) # Remove the last point. The edges are going to inform that the shape is closed.
|
||||
|
||||
bm_base = create_bmesh_from_vertices(polyline_vertices)
|
||||
base_vertices = tool.Cad.offset_edges(bm_base, offset)
|
||||
offset_base_verts = tool.Cad.offset_edges(bm_base, thickness + offset)
|
||||
top_vertices = tool.Cad.offset_edges(bm_base, angle_distortion + offset)
|
||||
offset_top_verts = tool.Cad.offset_edges(bm_base, angle_distortion + thickness + offset)
|
||||
if is_closed:
|
||||
base_vertices.append(base_vertices[0])
|
||||
offset_base_verts.append(offset_base_verts[0])
|
||||
top_vertices.append(top_vertices[0])
|
||||
offset_top_verts.append(offset_top_verts[0])
|
||||
|
||||
if offset_base_verts is not None:
|
||||
for v in base_vertices:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl))
|
||||
|
||||
for v in offset_base_verts[::-1]:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl))
|
||||
|
||||
for v in top_vertices:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl + height))
|
||||
|
||||
for v in offset_top_verts[::-1]:
|
||||
wall_preview_data["verts"].append((v.co.x, v.co.y, v.co.z + rl + height))
|
||||
|
||||
bm_base.free()
|
||||
|
||||
# Edges and Tris
|
||||
points = []
|
||||
side_edges_1 = []
|
||||
side_edges_2 = []
|
||||
base_edges = []
|
||||
|
||||
for i in range(len(wall_preview_data["verts"])):
|
||||
points.append(Vector(wall_preview_data["verts"][i]))
|
||||
|
||||
n = len(points) // 2
|
||||
bottom_side_1 = [[i, (i + 1) % (n)] for i in range((n - 1) // 2)]
|
||||
bottom_side_2 = [[i, (i + 1) % (n)] for i in range(n // 2, n - 1)]
|
||||
bottom_connections = [[i, n - i - 1] for i in range(n // 2)]
|
||||
bottom_loop = bottom_connections + bottom_side_1 + bottom_side_2
|
||||
side_edges_1.extend(bottom_side_1)
|
||||
side_edges_2.extend(bottom_side_2)
|
||||
base_edges.extend(bottom_loop)
|
||||
|
||||
upper_side_1 = [[i + n for i in edges] for edges in bottom_side_1]
|
||||
upper_side_2 = [[i + n for i in edges] for edges in bottom_side_2]
|
||||
upper_loop = [[i + n for i in edges] for edges in bottom_loop]
|
||||
side_edges_1.extend(upper_side_1)
|
||||
side_edges_2.extend(upper_side_2)
|
||||
base_edges.extend(upper_loop)
|
||||
|
||||
loops = [side_edges_1, side_edges_2, base_edges]
|
||||
|
||||
wall_preview_data["edges"] = []
|
||||
wall_preview_data["tris"] = []
|
||||
for i, group in enumerate(loops):
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in points]
|
||||
new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in group]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
|
||||
if i == 2:
|
||||
new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges)
|
||||
new_faces = bmesh.ops.bridge_loops(bm, edges=bm.edges, use_pairs=True, use_cyclic=True)
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
edges = [[v.index for v in e.verts] for e in bm.edges]
|
||||
tris = [[l.vert.index for l in loop] for loop in bm.calc_loop_triangles()]
|
||||
wall_preview_data["edges"].extend(edges)
|
||||
wall_preview_data["tris"].extend(tris)
|
||||
|
||||
wall_preview_data["edges"] = list(set(tuple(e) for e in wall_preview_data["edges"]))
|
||||
wall_preview_data["tris"] = list(set(tuple(t) for t in wall_preview_data["tris"]))
|
||||
|
||||
return wall_preview_data
|
||||
|
||||
def get_product_preview_data(cls, context, relating_type):
|
||||
model_props = context.scene.BIMModelProperties
|
||||
if relating_type.is_a("IfcDoorType"):
|
||||
rl = float(model_props.rl1)
|
||||
elif relating_type.is_a("IfcWindowType"):
|
||||
rl = float(model_props.rl2)
|
||||
else:
|
||||
rl = 0
|
||||
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0]
|
||||
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
|
||||
snap_obj = bpy.data.objects.get(snap_prop.snap_object)
|
||||
snap_element = tool.Ifc.get_entity(snap_obj)
|
||||
rot_mat = Quaternion()
|
||||
if snap_element and snap_element.is_a("IfcWall"):
|
||||
rot_mat = snap_obj.matrix_world.to_quaternion()
|
||||
|
||||
obj_type = tool.Ifc.get_object(relating_type)
|
||||
if obj_type.data:
|
||||
data = ItemDecorator.get_obj_data(obj_type)
|
||||
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 = material.MaterialProfiles[0].Profile
|
||||
except:
|
||||
return {}
|
||||
|
||||
model_props = context.scene.BIMModelProperties
|
||||
extrusion_depth = model_props.extrusion_depth
|
||||
cardinal_point = model_props.cardinal_point
|
||||
rot_mat = Quaternion()
|
||||
if relating_type.is_a("IfcBeamType"):
|
||||
y_rot = Quaternion((0.0, 1.0, 0.0), radians(90))
|
||||
z_rot = Quaternion((0.0, 0.0, 1.0), radians(90))
|
||||
rot_mat = y_rot @ z_rot
|
||||
# Get profile data
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile)
|
||||
|
||||
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 extrusion bmesh
|
||||
bm = bmesh.new()
|
||||
|
||||
new_verts = [bm.verts.new(v) for v in grouped_verts]
|
||||
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(grouped_verts) - 1)]
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
|
||||
new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges)
|
||||
|
||||
new_faces = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
||||
new_verts = [e for e in new_faces["geom"] if isinstance(e, bmesh.types.BMVert)]
|
||||
new_faces = bmesh.ops.translate(bm, verts=new_verts, vec=(0.0, 0.0, extrusion_depth))
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
|
||||
# Create bounding box
|
||||
verts = bm.verts
|
||||
i = len(verts)
|
||||
|
||||
min_x = min(v.co.x for v in verts)
|
||||
max_x = max(v.co.x for v in verts)
|
||||
min_y = min(v.co.y for v in verts)
|
||||
max_y = max(v.co.y for v in verts)
|
||||
min_z = min(v.co.z for v in verts)
|
||||
max_z = max(v.co.z for v in verts)
|
||||
|
||||
bbox_verts = [
|
||||
(min_x, min_y, min_z),
|
||||
(max_x, min_y, min_z),
|
||||
(max_x, max_y, min_z),
|
||||
(min_x, max_y, min_z),
|
||||
(min_x, min_y, max_z),
|
||||
(max_x, min_y, max_z),
|
||||
(max_x, max_y, max_z),
|
||||
(min_x, max_y, max_z),
|
||||
]
|
||||
|
||||
bbox_edges = [
|
||||
(0 + i, 3 + i),
|
||||
(3 + i, 7 + i),
|
||||
(7 + i, 4 + i),
|
||||
(4 + i, 0 + i),
|
||||
(0 + i, 1 + i),
|
||||
(3 + i, 2 + i),
|
||||
(7 + i, 6 + i),
|
||||
(4 + i, 5 + i),
|
||||
(1 + i, 2 + i),
|
||||
(2 + i, 6 + i),
|
||||
(6 + i, 5 + i),
|
||||
(5 + i, 1 + i),
|
||||
]
|
||||
|
||||
# Calculate rotation, mouse position, angle and cardinal point
|
||||
# TODO Angle
|
||||
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0]
|
||||
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
|
||||
data = {}
|
||||
|
||||
verts = [tuple(v.co) for v in verts]
|
||||
verts.extend(bbox_verts)
|
||||
verts = [tuple(rot_mat @ Vector(v)) for v in verts]
|
||||
verts = [tuple(Vector(v) + mouse_point) for v in verts]
|
||||
data["verts"] = verts
|
||||
data["edges"] = bbox_edges
|
||||
# data["edges"] = [(edge.verts[0].index, edge.verts[1].index) for edge in bm.edges]
|
||||
data["tris"] = tris
|
||||
|
||||
bm.free()
|
||||
return data
|
||||
|
||||
def draw_product_preview(self, context):
|
||||
def transparent_color(color, alpha=0.1):
|
||||
color = [i for i in color]
|
||||
color[3] = alpha
|
||||
return color
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
decorator_color = self.addon_prefs.decorator_color_special
|
||||
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
|
||||
polyline_points = polyline_data[0].polyline_points if polyline_data else []
|
||||
|
||||
self.relating_type = None
|
||||
props = context.scene.BIMModelProperties
|
||||
relating_type_id = props.relating_type_id
|
||||
if relating_type_id:
|
||||
self.relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
# Wall
|
||||
wall_preview_data = self.get_wall_preview_data(context, self.relating_type)
|
||||
if wall_preview_data:
|
||||
self.draw_batch("LINES", wall_preview_data["verts"], decorator_color, wall_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", wall_preview_data["verts"], transparent_color(decorator_color), wall_preview_data["tris"]
|
||||
)
|
||||
|
||||
# Mesh type products
|
||||
product_preview_data = self.get_product_preview_data(context, self.relating_type)
|
||||
if product_preview_data:
|
||||
self.draw_batch("LINES", product_preview_data["verts"], decorator_color, product_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", product_preview_data["verts"], transparent_color(decorator_color), product_preview_data["tris"]
|
||||
)
|
||||
|
||||
# Profile type products
|
||||
product_preview_data = self.get_profile_preview_data(context, self.relating_type)
|
||||
if product_preview_data:
|
||||
self.draw_batch("LINES", product_preview_data["verts"], decorator_color, product_preview_data["edges"])
|
||||
self.draw_batch(
|
||||
"TRIS", product_preview_data["verts"], transparent_color(decorator_color), product_preview_data["tris"]
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import get_enum_items
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator, ProductDecorator
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy_extras.object_utils import AddObjectHelper
|
||||
import json
|
||||
@@ -187,12 +187,14 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
|
||||
|
||||
cancel = self.handle_cancelation(context, event)
|
||||
if cancel is not None:
|
||||
ProductDecorator.uninstall()
|
||||
return cancel
|
||||
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
super().invoke(context, event)
|
||||
ProductDecorator.install(context)
|
||||
self.tool_state.use_default_container = True
|
||||
self.tool_state.plane_method = "XY"
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@@ -37,7 +37,7 @@ from bonsai.bim.ifc import IfcStore
|
||||
from math import pi, sin, cos, degrees
|
||||
from mathutils import Vector, Matrix
|
||||
from bonsai.bim.module.model.opening import FilledOpeningGenerator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator, ProductDecorator
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
from typing import Optional
|
||||
from lark import Lark, Transformer
|
||||
@@ -383,6 +383,7 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
|
||||
):
|
||||
self.create_walls_from_polyline(context)
|
||||
context.workspace.status_text_set(text=None)
|
||||
ProductDecorator.uninstall()
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Polyline.clear_polyline()
|
||||
tool.Blender.update_viewport()
|
||||
@@ -394,12 +395,14 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
|
||||
|
||||
cancel = self.handle_cancelation(context, event)
|
||||
if cancel is not None:
|
||||
ProductDecorator.uninstall()
|
||||
return cancel
|
||||
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
super().invoke(context, event)
|
||||
ProductDecorator.install(context)
|
||||
self.tool_state.use_default_container = True
|
||||
self.tool_state.plane_method = "XY"
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
Reference in New Issue
Block a user