mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Fix #2077. You can now edit profiles via their axis line.
This commit is contained in:
@@ -48,7 +48,9 @@ class CadTool(WorkSpaceTool):
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
obj = context.active_object
|
||||
if obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.is_profile:
|
||||
if not obj or not obj.data:
|
||||
return
|
||||
if hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_Q")
|
||||
@@ -100,6 +102,32 @@ class CadTool(WorkSpaceTool):
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_X")
|
||||
row.operator("bim.reset_vertex", text="Reset Vertex")
|
||||
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_Q")
|
||||
row.operator("bim.edit_extrusion_axis", text="Save Axis")
|
||||
row.operator("bim.disable_editing_extrusion_axis", text="", icon="CANCEL")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_E")
|
||||
row.operator("bim.cad_hotkey", text="Extend").hotkey = "S_E"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_T")
|
||||
row.operator("bim.cad_hotkey", text="Mitre").hotkey = "S_T"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_F")
|
||||
row.operator("bim.cad_hotkey", text="Fillet").hotkey = "S_F"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_O")
|
||||
row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O"
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
@@ -187,7 +215,10 @@ class CadHotkey(bpy.types.Operator):
|
||||
|
||||
def hotkey_S_Q(self):
|
||||
if tool.Ifc.get_entity(bpy.context.active_object):
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
if bpy.context.active_object.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
else:
|
||||
bpy.ops.bim.edit_arbitrary_profile()
|
||||
|
||||
@@ -210,4 +241,9 @@ class CadHotkey(bpy.types.Operator):
|
||||
|
||||
def is_profile(self):
|
||||
obj = bpy.context.active_object
|
||||
return obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.is_profile
|
||||
return (
|
||||
obj
|
||||
and obj.data
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
)
|
||||
|
||||
@@ -74,6 +74,9 @@ classes = (
|
||||
opening.ShowOpenings,
|
||||
profile.ChangeCardinalPoint,
|
||||
profile.ChangeProfileDepth,
|
||||
profile.DisableEditingExtrusionAxis,
|
||||
profile.EditExtrusionAxis,
|
||||
profile.EnableEditingExtrusionAxis,
|
||||
profile.ExtendProfile,
|
||||
profile.RecalculateProfile,
|
||||
profile.Rotate90,
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gpu
|
||||
import bgl
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
from math import pi, degrees, sin, cos, radians
|
||||
from bpy.types import SpaceView3D
|
||||
from mathutils import Vector, Matrix
|
||||
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
|
||||
class ProfileDecorator:
|
||||
installed = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW")
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.installed = None
|
||||
|
||||
def __call__(self, context):
|
||||
obj = context.active_object
|
||||
if obj.mode != "EDIT":
|
||||
return
|
||||
|
||||
bgl.glLineWidth(2)
|
||||
bgl.glPointSize(6)
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
all_vertices = []
|
||||
error_vertices = []
|
||||
selected_vertices = []
|
||||
unselected_vertices = []
|
||||
special_vertices = []
|
||||
special_vertex_indices = {}
|
||||
selected_edges = []
|
||||
unselected_edges = []
|
||||
special_edges = [] # edges that have a circle or an arc associated with them
|
||||
|
||||
arc_groups = []
|
||||
circle_groups = []
|
||||
for i, group in enumerate(obj.vertex_groups):
|
||||
if "IFCARCINDEX" in group.name:
|
||||
arc_groups.append(i)
|
||||
elif "IFCCIRCLE" in group.name:
|
||||
circle_groups.append(i)
|
||||
|
||||
arcs = {}
|
||||
circles = {}
|
||||
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
# https://docs.blender.org/api/blender_python_api_2_63_8/bmesh.html#CustomDataAccess
|
||||
# This is how we access vertex groups via bmesh, apparently, it's not very intuitive
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
|
||||
for vertex in bm.verts:
|
||||
co = tuple(obj.matrix_world @ vertex.co)
|
||||
all_vertices.append(co)
|
||||
if vertex.hide:
|
||||
continue
|
||||
|
||||
is_arc = False
|
||||
for group_index in arc_groups:
|
||||
if group_index in vertex[deform_layer]:
|
||||
is_arc = True
|
||||
break
|
||||
if is_arc:
|
||||
arcs.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
is_circle = False
|
||||
for group_index in circle_groups:
|
||||
if group_index in vertex[deform_layer]:
|
||||
is_circle = True
|
||||
break
|
||||
if is_circle:
|
||||
circles.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
if vertex.select:
|
||||
selected_vertices.append(co)
|
||||
else:
|
||||
if len(vertex.link_edges) > 1 and is_circle:
|
||||
error_vertices.append(co)
|
||||
elif is_circle:
|
||||
special_vertices.append(co)
|
||||
elif len(vertex.link_edges) != 2:
|
||||
error_vertices.append(co)
|
||||
elif is_arc:
|
||||
special_vertices.append(co)
|
||||
else:
|
||||
unselected_vertices.append(co)
|
||||
|
||||
for edge in bm.edges:
|
||||
edge_indices = [v.index for v in edge.verts]
|
||||
if edge.hide:
|
||||
continue
|
||||
if edge.select:
|
||||
selected_edges.append(edge_indices)
|
||||
else:
|
||||
i1, i2 = edge.verts[0].index, edge.verts[1].index
|
||||
if i1 in special_vertex_indices and special_vertex_indices[i1] == special_vertex_indices.get(i2, None):
|
||||
special_edges.append(edge_indices)
|
||||
else:
|
||||
unselected_edges.append(edge_indices)
|
||||
indices = [[v.index for v in e.verts] for e in bm.edges]
|
||||
|
||||
white = (1, 1, 1, 1)
|
||||
green = (0.545, 0.863, 0, 1)
|
||||
red = (1, 0.2, 0.322, 1)
|
||||
blue = (0.157, 0.565, 1, 1)
|
||||
grey = (0.2, 0.2, 0.2, 1)
|
||||
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=unselected_edges)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=selected_edges)
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=special_edges)
|
||||
self.shader.uniform_float("color", grey)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": unselected_vertices})
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": error_vertices})
|
||||
self.shader.uniform_float("color", red)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": special_vertices})
|
||||
self.shader.uniform_float("color", blue)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": selected_vertices})
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
|
||||
# Draw arcs
|
||||
|
||||
arc_centroids = []
|
||||
arc_segments = []
|
||||
for arc in arcs.values():
|
||||
if len(arc) != 3:
|
||||
continue
|
||||
sorted_arc = [None, None, None]
|
||||
for v1 in arc:
|
||||
connections = 0
|
||||
for link_edge in v1.link_edges:
|
||||
v2 = link_edge.other_vert(v1)
|
||||
if v2 in arc:
|
||||
connections += 1
|
||||
if connections == 2: # Midpoint
|
||||
sorted_arc[1] = v1
|
||||
else:
|
||||
sorted_arc[2 if sorted_arc[2] is None else 0] = v1
|
||||
points = [tuple(obj.matrix_world @ v.co) for v in sorted_arc]
|
||||
centroid = tool.Cad.get_center_of_arc(points)
|
||||
if centroid:
|
||||
arc_centroids.append(tuple(centroid))
|
||||
arc_segments.append(tool.Cad.create_arc_segments(pts=points, num_verts=17, make_edges=True))
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": arc_centroids})
|
||||
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
for verts, edges in arc_segments:
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.uniform_float("color", (0.157, 0.565, 1, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
# Draw circles
|
||||
|
||||
circle_centroids = []
|
||||
circle_segments = []
|
||||
for circle in circles.values():
|
||||
if len(circle) != 2:
|
||||
continue
|
||||
p1 = obj.matrix_world @ circle[0].co
|
||||
p2 = obj.matrix_world @ circle[1].co
|
||||
radius = (p2 - p1).length / 2
|
||||
centroid = p1.lerp(p2, 0.5)
|
||||
circle_centroids.append(tuple(centroid))
|
||||
segments = self.create_circle_segments(360, 20, radius)
|
||||
matrix = obj.matrix_world.copy()
|
||||
matrix.col[3] = centroid.to_4d()
|
||||
segments = [[list(matrix @ Vector(v)) for v in segments[0]], segments[1]]
|
||||
circle_segments.append(segments)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": circle_centroids})
|
||||
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
for verts, edges in circle_segments:
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.uniform_float("color", (0.157, 0.565, 1, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
def create_matrix(self, p, x, y, z):
|
||||
return Matrix([x, y, z, p]).to_4x4().transposed()
|
||||
|
||||
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
|
||||
# This function is taken from Sverchok, licensed under GPL v2-or-later.
|
||||
# This is a combination of the make_verts and make_edges function.
|
||||
def create_circle_segments(self, Angle, Vertices, Radius):
|
||||
if Angle < 360:
|
||||
theta = Angle / (Vertices - 1)
|
||||
else:
|
||||
theta = Angle / Vertices
|
||||
listVertX = []
|
||||
listVertY = []
|
||||
for i in range(Vertices):
|
||||
listVertX.append(Radius * cos(radians(theta * i)))
|
||||
listVertY.append(Radius * sin(radians(theta * i)))
|
||||
|
||||
if Angle < 360 and self.mode_ == 0:
|
||||
sigma = radians(Angle)
|
||||
listVertX[-1] = Radius * cos(sigma)
|
||||
listVertY[-1] = Radius * sin(sigma)
|
||||
elif Angle < 360 and self.mode_ == 1:
|
||||
listVertX.append(0.0)
|
||||
listVertY.append(0.0)
|
||||
|
||||
points = list((x, y, 0) for x, y in zip(listVertX, listVertY))
|
||||
|
||||
listEdg = [(i, i + 1) for i in range(Vertices - 1)]
|
||||
|
||||
if Angle < 360 and self.mode_ == 1:
|
||||
listEdg.append((0, Vertices))
|
||||
listEdg.append((Vertices - 1, Vertices))
|
||||
else:
|
||||
listEdg.append((Vertices - 1, 0))
|
||||
|
||||
return points, listEdg
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bpy
|
||||
import copy
|
||||
import math
|
||||
import bmesh
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
@@ -29,10 +28,10 @@ import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.type
|
||||
import blenderbim.core.geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import pi, degrees, inf
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils import Vector, Matrix, Quaternion
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
|
||||
|
||||
def element_listener(element, obj):
|
||||
@@ -48,12 +47,12 @@ def mode_callback(obj, data):
|
||||
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||
):
|
||||
return
|
||||
product = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
product = tool.Ifc.get().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbProfile":
|
||||
return
|
||||
if obj.mode == "EDIT":
|
||||
IfcStore.edited_objs.add(obj)
|
||||
tool.Ifc.edit(obj)
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
@@ -97,8 +96,8 @@ class DumbProfileGenerator:
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
def generate(self):
|
||||
self.file = IfcStore.get_file()
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
||||
self.file = tool.Ifc.get()
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
material = ifcopenshell.util.element.get_material(self.relating_type)
|
||||
if material and material.is_a("IfcMaterialProfileSet"):
|
||||
self.profile_set = material
|
||||
@@ -328,7 +327,7 @@ class DumbProfileJoiner:
|
||||
body = copy.deepcopy(axis1)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
si_length = unit_scale * length
|
||||
end = profile1.matrix_world @ Vector((si_length, 0, 0))
|
||||
end = profile1.matrix_world @ Vector((0, 0, si_length))
|
||||
axis[1] = end
|
||||
body[1] = end
|
||||
self.recreate_profile(element1, profile1, axis, body)
|
||||
@@ -500,9 +499,7 @@ class DumbProfileJoiner:
|
||||
# Openings should move with the host overall ...
|
||||
# ... except their position should stay the same along the local Z axis of the wall
|
||||
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
||||
percent = tool.Cad.edge_percent(
|
||||
self.body[0], (previous_origin, (previous_matrix @ Vector((0, 0, 1))))
|
||||
)
|
||||
percent = tool.Cad.edge_percent(self.body[0], (previous_origin, (previous_matrix @ Vector((0, 0, 1)))))
|
||||
is_z_offset_increased = True if percent < 0 else False
|
||||
|
||||
change_in_z = (self.body[0] - previous_origin).length / self.unit_scale
|
||||
@@ -913,3 +910,121 @@ class Rotate90(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.context.view_layer.update()
|
||||
DumbProfileRecalculator().recalculate(objs)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_extrusion_axis"
|
||||
bl_label = "Enable Editing Extrusion Axis"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
axis = ifcopenshell.util.representation.get_representation(element, "Model", "Axis", "GRAPH_VIEW")
|
||||
if axis:
|
||||
position = obj.matrix_world.copy()
|
||||
tool.Model.import_axis(axis.Items[0], obj=obj)
|
||||
else:
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
extrusion = tool.Model.get_extrusion(body)
|
||||
|
||||
if extrusion.Position:
|
||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(extrusion.Position).tolist())
|
||||
position[0][3] *= self.unit_scale
|
||||
position[1][3] *= self.unit_scale
|
||||
position[2][3] *= self.unit_scale
|
||||
else:
|
||||
position = Matrix()
|
||||
|
||||
direction = Vector(extrusion.ExtrudedDirection.DirectionRatios).normalized()
|
||||
tool.Model.import_axis([Vector((0, 0, 0)), direction * extrusion.Depth], obj=obj, position=position)
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
bpy.ops.wm.tool_set_by_id(name="bim.cad_tool")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_extrusion_axis"
|
||||
bl_label = "Disable Editing Extrusion Axis"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=body,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_extrusion_axis"
|
||||
bl_label = "Edit Extrusion Axis"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
start = obj.matrix_world @ obj.data.vertices[0].co.copy()
|
||||
end = obj.matrix_world @ obj.data.vertices[1].co.copy()
|
||||
depth = (end - start).length
|
||||
z_axis = (end - start).normalized()
|
||||
y_axis = Vector((0, 0, 1))
|
||||
x_axis = y_axis.cross(z_axis).normalized()
|
||||
y_axis = z_axis.cross(x_axis).normalized()
|
||||
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=body,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
matrix = Matrix(
|
||||
(
|
||||
[x_axis[0], y_axis[0], z_axis[0], start.x],
|
||||
[x_axis[1], y_axis[1], z_axis[1], start.y],
|
||||
[x_axis[2], y_axis[2], z_axis[2], start.z],
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
|
||||
obj.matrix_world = matrix
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
joiner = DumbProfileJoiner()
|
||||
joiner.set_depth(obj, depth / self.unit_scale)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -16,30 +16,21 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import gpu
|
||||
import blf
|
||||
import bgl
|
||||
import bpy
|
||||
import math
|
||||
import json
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
import mathutils.geometry
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.core.type
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import SpaceView3D
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import pi, degrees, sin, cos, radians
|
||||
from math import radians
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
|
||||
|
||||
def calculate_quantities(usecase_path, ifc_file, settings):
|
||||
@@ -123,8 +114,8 @@ class DumbSlabGenerator:
|
||||
self.relating_type = relating_type
|
||||
|
||||
def generate(self):
|
||||
self.file = IfcStore.get_file()
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
||||
self.file = tool.Ifc.get()
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
thicknesses = []
|
||||
for rel in self.relating_type.HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesMaterial"):
|
||||
@@ -268,7 +259,7 @@ class DumbSlabPlaner:
|
||||
|
||||
def change_thickness(self, element, thickness):
|
||||
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
obj = IfcStore.get_element(element.id())
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj:
|
||||
return
|
||||
|
||||
@@ -573,7 +564,7 @@ class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
DecorationsHandler.uninstall()
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
@@ -620,7 +611,7 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Model.import_profile(extrusion.SweptArea, obj=obj, position=position)
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
DecorationsHandler.install(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
bpy.ops.wm.tool_set_by_id(name="bim.cad_tool")
|
||||
return {"FINISHED"}
|
||||
@@ -633,7 +624,7 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
DecorationsHandler.uninstall()
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
obj = context.active_object
|
||||
@@ -657,7 +648,7 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.layout.label(text="INVALID PROFILE")
|
||||
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
DecorationsHandler.install(context)
|
||||
ProfileDecorator.install(context)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return
|
||||
|
||||
@@ -749,245 +740,3 @@ class SetArcIndex(bpy.types.Operator):
|
||||
group.add(selected_vertices, 1, "REPLACE")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DecorationsHandler:
|
||||
installed = None
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW")
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.installed = None
|
||||
|
||||
def __call__(self, context):
|
||||
obj = context.active_object
|
||||
if obj.mode != "EDIT":
|
||||
return
|
||||
|
||||
bgl.glLineWidth(2)
|
||||
bgl.glPointSize(6)
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
all_vertices = []
|
||||
error_vertices = []
|
||||
selected_vertices = []
|
||||
unselected_vertices = []
|
||||
special_vertices = []
|
||||
special_vertex_indices = {}
|
||||
selected_edges = []
|
||||
unselected_edges = []
|
||||
special_edges = [] # edges that have a circle or an arc associated with them
|
||||
|
||||
arc_groups = []
|
||||
circle_groups = []
|
||||
for i, group in enumerate(obj.vertex_groups):
|
||||
if "IFCARCINDEX" in group.name:
|
||||
arc_groups.append(i)
|
||||
elif "IFCCIRCLE" in group.name:
|
||||
circle_groups.append(i)
|
||||
|
||||
arcs = {}
|
||||
circles = {}
|
||||
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
# https://docs.blender.org/api/blender_python_api_2_63_8/bmesh.html#CustomDataAccess
|
||||
# This is how we access vertex groups via bmesh, apparently, it's not very intuitive
|
||||
deform_layer = bm.verts.layers.deform.active
|
||||
|
||||
for vertex in bm.verts:
|
||||
co = tuple(obj.matrix_world @ vertex.co)
|
||||
all_vertices.append(co)
|
||||
if vertex.hide:
|
||||
continue
|
||||
|
||||
is_arc = False
|
||||
for group_index in arc_groups:
|
||||
if group_index in vertex[deform_layer]:
|
||||
is_arc = True
|
||||
break
|
||||
if is_arc:
|
||||
arcs.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
is_circle = False
|
||||
for group_index in circle_groups:
|
||||
if group_index in vertex[deform_layer]:
|
||||
is_circle = True
|
||||
break
|
||||
if is_circle:
|
||||
circles.setdefault(group_index, []).append(vertex)
|
||||
special_vertex_indices[vertex.index] = group_index
|
||||
|
||||
if vertex.select:
|
||||
selected_vertices.append(co)
|
||||
else:
|
||||
if len(vertex.link_edges) > 1 and is_circle:
|
||||
error_vertices.append(co)
|
||||
elif is_circle:
|
||||
special_vertices.append(co)
|
||||
elif len(vertex.link_edges) != 2:
|
||||
error_vertices.append(co)
|
||||
elif is_arc:
|
||||
special_vertices.append(co)
|
||||
else:
|
||||
unselected_vertices.append(co)
|
||||
|
||||
for edge in bm.edges:
|
||||
edge_indices = [v.index for v in edge.verts]
|
||||
if edge.hide:
|
||||
continue
|
||||
if edge.select:
|
||||
selected_edges.append(edge_indices)
|
||||
else:
|
||||
i1, i2 = edge.verts[0].index, edge.verts[1].index
|
||||
if i1 in special_vertex_indices and special_vertex_indices[i1] == special_vertex_indices.get(i2, None):
|
||||
special_edges.append(edge_indices)
|
||||
else:
|
||||
unselected_edges.append(edge_indices)
|
||||
indices = [[v.index for v in e.verts] for e in bm.edges]
|
||||
|
||||
white = (1, 1, 1, 1)
|
||||
green = (0.545, 0.863, 0, 1)
|
||||
red = (1, 0.2, 0.322, 1)
|
||||
blue = (0.157, 0.565, 1, 1)
|
||||
grey = (0.2, 0.2, 0.2, 1)
|
||||
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=unselected_edges)
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=selected_edges)
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=special_edges)
|
||||
self.shader.uniform_float("color", grey)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": unselected_vertices})
|
||||
self.shader.uniform_float("color", white)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": error_vertices})
|
||||
self.shader.uniform_float("color", red)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": special_vertices})
|
||||
self.shader.uniform_float("color", blue)
|
||||
batch.draw(self.shader)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": selected_vertices})
|
||||
self.shader.uniform_float("color", green)
|
||||
batch.draw(self.shader)
|
||||
|
||||
# Draw arcs
|
||||
|
||||
arc_centroids = []
|
||||
arc_segments = []
|
||||
for arc in arcs.values():
|
||||
if len(arc) != 3:
|
||||
continue
|
||||
sorted_arc = [None, None, None]
|
||||
for v1 in arc:
|
||||
connections = 0
|
||||
for link_edge in v1.link_edges:
|
||||
v2 = link_edge.other_vert(v1)
|
||||
if v2 in arc:
|
||||
connections += 1
|
||||
if connections == 2: # Midpoint
|
||||
sorted_arc[1] = v1
|
||||
else:
|
||||
sorted_arc[2 if sorted_arc[2] is None else 0] = v1
|
||||
points = [tuple(obj.matrix_world @ v.co) for v in sorted_arc]
|
||||
centroid = tool.Cad.get_center_of_arc(points)
|
||||
if centroid:
|
||||
arc_centroids.append(tuple(centroid))
|
||||
arc_segments.append(tool.Cad.create_arc_segments(pts=points, num_verts=17, make_edges=True))
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": arc_centroids})
|
||||
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
for verts, edges in arc_segments:
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.uniform_float("color", (0.157, 0.565, 1, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
# Draw circles
|
||||
|
||||
circle_centroids = []
|
||||
circle_segments = []
|
||||
for circle in circles.values():
|
||||
if len(circle) != 2:
|
||||
continue
|
||||
p1 = obj.matrix_world @ circle[0].co
|
||||
p2 = obj.matrix_world @ circle[1].co
|
||||
radius = (p2 - p1).length / 2
|
||||
centroid = p1.lerp(p2, 0.5)
|
||||
circle_centroids.append(tuple(centroid))
|
||||
segments = self.create_circle_segments(360, 20, radius)
|
||||
matrix = obj.matrix_world.copy()
|
||||
matrix.col[3] = centroid.to_4d()
|
||||
segments = [[list(matrix @ Vector(v)) for v in segments[0]], segments[1]]
|
||||
circle_segments.append(segments)
|
||||
|
||||
batch = batch_for_shader(self.shader, "POINTS", {"pos": circle_centroids})
|
||||
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
for verts, edges in circle_segments:
|
||||
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=edges)
|
||||
self.shader.uniform_float("color", (0.157, 0.565, 1, 1))
|
||||
batch.draw(self.shader)
|
||||
|
||||
def create_matrix(self, p, x, y, z):
|
||||
return Matrix([x, y, z, p]).to_4x4().transposed()
|
||||
|
||||
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
|
||||
# This function is taken from Sverchok, licensed under GPL v2-or-later.
|
||||
# This is a combination of the make_verts and make_edges function.
|
||||
def create_circle_segments(self, Angle, Vertices, Radius):
|
||||
if Angle < 360:
|
||||
theta = Angle / (Vertices - 1)
|
||||
else:
|
||||
theta = Angle / Vertices
|
||||
listVertX = []
|
||||
listVertY = []
|
||||
for i in range(Vertices):
|
||||
listVertX.append(Radius * cos(radians(theta * i)))
|
||||
listVertY.append(Radius * sin(radians(theta * i)))
|
||||
|
||||
if Angle < 360 and self.mode_ == 0:
|
||||
sigma = radians(Angle)
|
||||
listVertX[-1] = Radius * cos(sigma)
|
||||
listVertY[-1] = Radius * sin(sigma)
|
||||
elif Angle < 360 and self.mode_ == 1:
|
||||
listVertX.append(0.0)
|
||||
listVertY.append(0.0)
|
||||
|
||||
points = list((x, y, 0) for x, y in zip(listVertX, listVertY))
|
||||
|
||||
listEdg = [(i, i + 1) for i in range(Vertices - 1)]
|
||||
|
||||
if Angle < 360 and self.mode_ == 1:
|
||||
listEdg.append((0, Vertices))
|
||||
listEdg.append((Vertices - 1, Vertices))
|
||||
else:
|
||||
listEdg.append((Vertices - 1, 0))
|
||||
|
||||
return points, listEdg
|
||||
|
||||
@@ -83,17 +83,18 @@ class BimTool(WorkSpaceTool):
|
||||
("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
|
||||
("bim.hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
|
||||
("bim.hotkey", {"type": "G", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_G")]}),
|
||||
("bim.hotkey", {"type": "K", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_K")]}),
|
||||
("bim.hotkey", {"type": "M", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_M")]}),
|
||||
("bim.hotkey", {"type": "O", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_O")]}),
|
||||
("bim.hotkey", {"type": "Q", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Q")]}),
|
||||
("bim.hotkey", {"type": "R", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_R")]}),
|
||||
("bim.hotkey", {"type": "K", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_K")]}),
|
||||
("bim.hotkey", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_T")]}),
|
||||
("bim.hotkey", {"type": "V", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_V")]}),
|
||||
("bim.hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}),
|
||||
("bim.hotkey", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Y")]}),
|
||||
("bim.hotkey", {"type": "D", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_D")]}),
|
||||
("bim.hotkey", {"type": "E", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_E")]}),
|
||||
("bim.hotkey", {"type": "O", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_O")]}),
|
||||
("bim.hotkey", {"type": "Q", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_Q")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, ws_tool):
|
||||
@@ -253,6 +254,10 @@ class BimToolUI:
|
||||
row.label(text="", icon="EVENT_E")
|
||||
row.operator("bim.hotkey", text="Extend").hotkey = "S_E"
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_ALT")
|
||||
row.label(text="", icon="EVENT_E")
|
||||
row.operator("bim.hotkey", text="Edit Axis").hotkey = "A_E"
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_T")
|
||||
row.operator("bim.hotkey", text="Butt").hotkey = "S_T"
|
||||
@@ -607,6 +612,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return
|
||||
bpy.ops.bim.select_decomposition()
|
||||
|
||||
def hotkey_A_E(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
if self.active_material_usage == "PROFILE":
|
||||
bpy.ops.bim.enable_editing_extrusion_axis()
|
||||
|
||||
def hotkey_A_O(self):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
|
||||
@@ -72,4 +72,4 @@ class ProfileData:
|
||||
@classmethod
|
||||
def is_editing_arbitrary_profile(cls):
|
||||
obj = bpy.context.active_object
|
||||
return obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.is_profile
|
||||
return obj and obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
|
||||
@@ -21,7 +21,7 @@ import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.model.profile as model_profile
|
||||
from blenderbim.bim.module.model.slab import DecorationsHandler
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_profile
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class EnableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
DecorationsHandler.install(context)
|
||||
ProfileDecorator.install(context)
|
||||
bpy.ops.wm.tool_set_by_id(name="bim.cad_tool")
|
||||
|
||||
|
||||
@@ -149,8 +149,8 @@ class DisableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
if obj and obj.data and obj.data.BIMMeshProperties.is_profile:
|
||||
DecorationsHandler.uninstall()
|
||||
if obj and obj.data and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@@ -166,7 +166,7 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
obj = context.active_object
|
||||
|
||||
DecorationsHandler.uninstall()
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
profile = tool.Model.export_profile(obj)
|
||||
@@ -176,7 +176,7 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.layout.label(text="INVALID PROFILE: " + indices[1])
|
||||
|
||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||
DecorationsHandler.install(context)
|
||||
ProfileDecorator.install(context)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return
|
||||
|
||||
|
||||
@@ -421,7 +421,7 @@ class BIMMeshProperties(PropertyGroup):
|
||||
is_native: BoolProperty(name="Is Native", default=False)
|
||||
is_swept_solid: BoolProperty(name="Is Swept Solid")
|
||||
is_parametric: BoolProperty(name="Is Parametric", default=False)
|
||||
is_profile: BoolProperty(name="Is Profile", default=False)
|
||||
subshape_type: StringProperty(name="Subshape Type")
|
||||
ifc_definition: StringProperty(name="IFC Definition")
|
||||
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
||||
material_checksum: StringProperty(name="Material Checksum", default="[]")
|
||||
|
||||
@@ -140,6 +140,38 @@ class Model(blenderbim.core.tool.Model):
|
||||
else:
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def import_axis(cls, axis, obj=None, position=None):
|
||||
cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
if position is None:
|
||||
position = Matrix()
|
||||
|
||||
cls.vertices = []
|
||||
cls.edges = []
|
||||
cls.arcs = []
|
||||
cls.circles = []
|
||||
|
||||
if isinstance(axis, list):
|
||||
cls.vertices.extend([
|
||||
position @ Vector(cls.convert_unit_to_si(axis[0])).to_3d(),
|
||||
position @ Vector(cls.convert_unit_to_si(axis[1])).to_3d(),
|
||||
])
|
||||
cls.edges.append([0, 1])
|
||||
else:
|
||||
cls.import_curve(obj, position, axis)
|
||||
|
||||
mesh = bpy.data.meshes.new("Axis")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "AXIS"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Axis", mesh)
|
||||
else:
|
||||
obj.data = mesh
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_profile(cls, profile, obj=None, position=None):
|
||||
cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
@@ -162,7 +194,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
|
||||
mesh = bpy.data.meshes.new("Profile")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.is_profile = True
|
||||
mesh.BIMMeshProperties.subshape_type = "PROFILE"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Profile", mesh)
|
||||
@@ -195,6 +227,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
cls.edges[-1] = (len(cls.vertices) - 1, offset) # Close the loop
|
||||
elif curve.is_a("IfcIndexedPolyCurve"):
|
||||
is_arc = False
|
||||
is_closed = False
|
||||
if curve.Segments:
|
||||
for segment in curve.Segments:
|
||||
if len(segment[0]) == 3: # IfcArcIndex
|
||||
@@ -213,15 +246,21 @@ class Model(blenderbim.core.tool.Model):
|
||||
if is_arc:
|
||||
cls.arcs[-1].append(len(cls.vertices) - 1)
|
||||
is_arc = False
|
||||
|
||||
if curve.Segments[0][0][0] == curve.Segments[-1][0][-1]:
|
||||
is_closed = True
|
||||
else:
|
||||
for local_point in curve.Points.CoordList:
|
||||
global_point = position @ Vector(cls.convert_unit_to_si(local_point)).to_3d()
|
||||
cls.vertices.append(global_point)
|
||||
# Curves without segments are cls closing
|
||||
del cls.vertices[-1]
|
||||
|
||||
cls.edges.extend([(i, i + 1) for i in range(offset, len(cls.vertices))])
|
||||
cls.edges[-1] = (len(cls.vertices) - 1, offset) # Close the loop
|
||||
if cls.vertices[offset] == cls.vertices[-1]:
|
||||
is_closed = True
|
||||
del cls.vertices[-1]
|
||||
|
||||
cls.edges.extend([(i, i + 1) for i in range(offset, len(cls.vertices) - 1)])
|
||||
if is_closed:
|
||||
cls.edges.append([len(cls.vertices) - 1, offset]) # Close the loop
|
||||
elif curve.is_a("IfcCircle"):
|
||||
center = cls.convert_unit_to_si(
|
||||
Matrix(ifcopenshell.util.placement.get_axis2placement(curve.Position).tolist()).col[3].to_3d()
|
||||
|
||||
Reference in New Issue
Block a user