mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-17 02:49:12 +00:00
Various tweaks and fixes (#1683)
* Use operator's poll method to ensure correct execution * Add utilities to get active collection item * Minor fixes zombie code list comprehensions typos cleanup tooltips, descriptions ensure file extension in folder operator use custom UI List instead of generic one ensure active object stays the same after operator execution use passed context instead of bpy * Jump through hoops to not display empty dynamic enums * Group mesh calculation methods in helper module If not using bmesh, the polygon selection status is updated only when getting out of Edit mode. Using bmesh we can get the values in realtime * Remove unused arg name in dosctring
This commit is contained in:
@@ -39,6 +39,11 @@ class ExecuteBIMTester(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_bim_tester"
|
||||
bl_label = "Execute BIMTester"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BimTesterProperties
|
||||
return props.ifc_file and props.feature
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BimTesterProperties
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import logging
|
||||
import numpy as np
|
||||
from mathutils import Matrix
|
||||
from math import radians
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class ExportClashSets(bpy.types.Operator):
|
||||
@@ -124,7 +125,7 @@ class AddClashSource(bpy.types.Operator):
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
|
||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
||||
source = getattr(clash_set, self.group).add()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -137,7 +138,7 @@ class RemoveClashSource(bpy.types.Operator):
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
|
||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
||||
getattr(clash_set, self.group).remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -152,7 +153,7 @@ class SelectClashSource(bpy.types.Operator):
|
||||
group: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash_set = context.scene.BIMClashProperties.clash_sets[context.scene.BIMClashProperties.active_clash_set_index]
|
||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
||||
getattr(clash_set, self.group)[self.index].name = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -276,9 +277,7 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
clash_set_name = context.scene.BIMClashProperties.clash_sets[
|
||||
context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
||||
global_ids = []
|
||||
for clash_set in clash_sets:
|
||||
if clash_set["name"] != clash_set_name:
|
||||
@@ -303,6 +302,10 @@ class SmartClashGroup(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BIMClashProperties.clash_results_path
|
||||
|
||||
def execute(self, context):
|
||||
import ifcclash
|
||||
|
||||
@@ -326,9 +329,7 @@ class SmartClashGroup(bpy.types.Operator):
|
||||
with open(save_path, "w") as f:
|
||||
f.write(json.dumps(smart_grouped_clashes))
|
||||
|
||||
clash_set_name = context.scene.BIMClashProperties.clash_sets[
|
||||
context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
||||
|
||||
# Reset the list of smart_clash_groups for the UI
|
||||
context.scene.BIMClashProperties.smart_clash_groups.clear()
|
||||
@@ -355,12 +356,14 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
||||
bl_label = "Load Smart Groups for Active Clash Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.BIMClashProperties.active_clash_set
|
||||
|
||||
def execute(self, context):
|
||||
smart_groups_path = bpy.path.ensure_ext(context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
||||
|
||||
clash_set_name = context.scene.BIMClashProperties.clash_sets[
|
||||
context.scene.BIMClashProperties.active_clash_set_index
|
||||
].name
|
||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
||||
|
||||
with open(smart_groups_path) as f:
|
||||
smart_grouped_clashes = json.load(f)
|
||||
@@ -389,11 +392,14 @@ class SelectSmartGroup(bpy.types.Operator):
|
||||
bl_label = "Select Smart Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
# Select smart group in view
|
||||
selected_smart_group = context.scene.BIMClashProperties.smart_clash_groups[
|
||||
context.scene.BIMCLashProperties.active_smart_group_index
|
||||
]
|
||||
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
|
||||
# print(selected_smart_group.number)
|
||||
|
||||
for obj in context.visible_objects:
|
||||
|
||||
@@ -69,3 +69,15 @@ class BIMClashProperties(PropertyGroup):
|
||||
smart_clash_grouping_max_distance: IntProperty(
|
||||
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
|
||||
)
|
||||
|
||||
@property
|
||||
def active_clash_set(self):
|
||||
if not self.clash_sets:
|
||||
return None
|
||||
return self.clash_sets[self.active_clash_set_index]
|
||||
|
||||
@property
|
||||
def active_smart_group(self):
|
||||
if not self.smart_clash_groups:
|
||||
return None
|
||||
return self.smart_clash_groups[self.active_smart_group_index]
|
||||
|
||||
@@ -57,7 +57,7 @@ class BIM_PT_ifcclash(Panel):
|
||||
layout.template_list("BIM_UL_clash_sets", "", props, "clash_sets", props, "active_clash_set_index")
|
||||
|
||||
if props.active_clash_set_index < len(props.clash_sets):
|
||||
clash_set = props.clash_sets[props.active_clash_set_index]
|
||||
clash_set = props.active_clash_set
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(clash_set, "name")
|
||||
|
||||
@@ -165,7 +165,7 @@ class SelectHighPolygonMeshes(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
[o.select_set(True) for o in context.view_layer.objects
|
||||
if o.type == 'MESH'
|
||||
if o.type == "MESH"
|
||||
and len(o.data.polygons) > context.scene.BIMDebugProperties.number_of_polygons]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ def get_active_drawing(scene):
|
||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
||||
return None, None
|
||||
try:
|
||||
drawing = props.drawings[props.active_drawing_index]
|
||||
drawing = props.active_drawing
|
||||
return scene.collection.children["Views"].children[f"IfcGroup/{drawing.name}"], drawing.camera
|
||||
except (KeyError, IndexError):
|
||||
raise RuntimeError("missing drawing collection")
|
||||
|
||||
@@ -60,6 +60,10 @@ class AddDrawing(bpy.types.Operator):
|
||||
bl_label = "Add Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
@@ -502,12 +506,14 @@ class AddAnnotation(bpy.types.Operator):
|
||||
obj_name: bpy.props.StringProperty()
|
||||
data_type: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.scene.camera
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
if not context.scene.camera:
|
||||
return {"FINISHED"}
|
||||
subcontext = ifcopenshell.util.representation.get_context(
|
||||
IfcStore.get_file(), "Plan", "Annotation", context.scene.camera.data.BIMCameraProperties.target_view
|
||||
)
|
||||
@@ -563,7 +569,7 @@ class OpenSheet(bpy.types.Operator):
|
||||
open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command,
|
||||
os.path.join(
|
||||
context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg"
|
||||
context.scene.BIMProperties.data_dir, "sheets", props.active_sheet.name + ".svg"
|
||||
),
|
||||
)
|
||||
return {"FINISHED"}
|
||||
@@ -581,9 +587,9 @@ class AddDrawingToSheet(bpy.types.Operator):
|
||||
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
|
||||
try:
|
||||
sheet_builder.add_drawing(
|
||||
props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name
|
||||
props.drawings.active_drawing.name, props.active_sheet.name
|
||||
)
|
||||
except:
|
||||
except FileNotFoundError:
|
||||
self.report({"ERROR"}, "Drawings need to be created before being added to a sheet")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -596,7 +602,7 @@ class CreateSheets(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
props = scene.DocProperties
|
||||
name = props.sheets[props.active_sheet_index].name
|
||||
name = props.active_sheet.name
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = scene.BIMProperties.data_dir
|
||||
sheet_builder.build(name)
|
||||
@@ -650,18 +656,23 @@ class OpenView(bpy.types.Operator):
|
||||
|
||||
|
||||
class OpenViewCamera(bpy.types.Operator):
|
||||
"""Select this drawing's camera object and expand its drawing properties"""
|
||||
bl_idname = "bim.open_view_camera"
|
||||
bl_label = "Open View Camera"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
view_name: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bpy.context.object.mode == "OBJECT"
|
||||
|
||||
def execute(self, context):
|
||||
new_drawing_index = context.scene.DocProperties.drawings.find(self.view_name)
|
||||
context.scene.DocProperties.active_drawing_index = new_drawing_index
|
||||
drawing = context.scene.DocProperties.drawings[new_drawing_index]
|
||||
context.view_layer.objects.active = drawing.camera
|
||||
doc_props = context.scene.DocProperties
|
||||
doc_props.active_drawing_index = doc_props.drawings.find(self.view_name)
|
||||
drawing = doc_props.active_drawing
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
drawing.camera.select_set(True)
|
||||
context.view_layer.objects.active = drawing.camera
|
||||
for area in context.screen.areas:
|
||||
if area.ui_type == "PROPERTIES":
|
||||
for space in area.spaces:
|
||||
@@ -712,6 +723,7 @@ class SelectDocIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_doc_ifc_file"
|
||||
bl_label = "Select Documentation IFC File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
@@ -1059,7 +1071,7 @@ class SelectScheduleFile(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props.schedules[props.active_schedule_index].file = self.filepath
|
||||
props.active_schedule.file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -1071,9 +1083,13 @@ class BuildSchedule(bpy.types.Operator):
|
||||
bl_idname = "bim.build_schedule"
|
||||
bl_label = "Build Schedule"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.DocProperties.active_schedule.file
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
schedule = props.schedules[props.active_schedule_index]
|
||||
schedule = props.active_schedule
|
||||
schedule_creator = scheduler.Scheduler()
|
||||
outfile = os.path.join(context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg")
|
||||
schedule_creator.schedule(schedule.file, outfile)
|
||||
@@ -1092,7 +1108,7 @@ class AddScheduleToSheet(bpy.types.Operator):
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
|
||||
sheet_builder.add_schedule(
|
||||
props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name
|
||||
props.active_schedule.name, props.active_sheet.name
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1143,13 +1159,12 @@ class CleanWireframes(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
objects = context.scene.objects
|
||||
if context.selected_objects:
|
||||
objects = context.selected_objects
|
||||
for obj in objects:
|
||||
if not isinstance(obj.data, bpy.types.Mesh):
|
||||
continue
|
||||
if "EDGE_SPLIT" not in [m.type for m in obj.modifiers]:
|
||||
else:
|
||||
objects = context.scene.objects
|
||||
for obj in (o for o in objects if o.type == "MESH"):
|
||||
if "EDGE_SPLIT" not in (m.type for m in obj.modifiers):
|
||||
obj.modifiers.new("EdgeSplit", "EDGE_SPLIT")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1159,11 +1174,13 @@ class CopyGrid(bpy.types.Operator):
|
||||
bl_label = "Add Grid"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return helper.get_active_drawing(context.scene)[0] is not None
|
||||
|
||||
def execute(self, context):
|
||||
proj_coll = helper.get_project_collection(context.scene)
|
||||
view_coll, camera = helper.get_active_drawing(context.scene)
|
||||
if view_coll is None:
|
||||
return {"CANCELLED"}
|
||||
is_ortho = camera.data.type == "ORTHO"
|
||||
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
|
||||
clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
@@ -1240,13 +1257,15 @@ class AddSectionsAnnotations(bpy.types.Operator):
|
||||
bl_label = "Add Sections"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
camera = helper.get_active_drawing(context.scene)[1]
|
||||
return camera and camera.data.type == "ORTHO"
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
view_coll, camera = helper.get_active_drawing(scene)
|
||||
is_ortho = camera.data.type == "ORTHO"
|
||||
if not is_ortho:
|
||||
return {"CANCELLED"}
|
||||
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
|
||||
bounds = helper.ortho_view_frame(camera.data)
|
||||
|
||||
drawings = [
|
||||
d
|
||||
|
||||
@@ -269,6 +269,17 @@ class DocProperties(PropertyGroup):
|
||||
name="Decorations Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
|
||||
)
|
||||
|
||||
@property
|
||||
def active_schedule(self):
|
||||
return self.schedules[self.active_schedule_index]
|
||||
|
||||
@property
|
||||
def active_drawing(self):
|
||||
return self.drawings[self.active_drawing_index]
|
||||
|
||||
@property
|
||||
def active_sheet(self):
|
||||
return self.sheets[self.active_sheet_index]
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
view_name: StringProperty(name="View Name")
|
||||
|
||||
@@ -170,9 +170,9 @@ class BIM_PT_drawings(Panel):
|
||||
if props.drawings:
|
||||
if props.active_drawing_index < len(props.drawings):
|
||||
op = row.operator("bim.open_view", icon="URL", text="")
|
||||
op.view = props.drawings[props.active_drawing_index].name
|
||||
op.view = props.active_drawing.name
|
||||
row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index
|
||||
layout.template_list("BIM_UL_generic", "", props, "drawings", props, "active_drawing_index")
|
||||
layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.add_ifc_file")
|
||||
@@ -206,7 +206,7 @@ class BIM_PT_schedules(Panel):
|
||||
layout.template_list("BIM_UL_generic", "", props, "schedules", props, "active_schedule_index")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props.schedules[props.active_schedule_index], "file")
|
||||
row.prop(props.active_schedule, "file")
|
||||
row.operator("bim.select_schedule_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ class BIM_PT_annotation_utilities(Panel):
|
||||
if props.drawings:
|
||||
if props.active_drawing_index < len(props.drawings):
|
||||
op = row.operator("bim.open_view", icon="URL", text="")
|
||||
op.view = props.drawings[props.active_drawing_index].name
|
||||
op.view = props.active_drawing.name
|
||||
row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index
|
||||
layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index")
|
||||
|
||||
|
||||
@@ -237,6 +237,12 @@ class ConvertLocalToGlobal(bpy.types.Operator):
|
||||
bl_label = "Convert Local To Global"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and props.coordinate_input.count(",") == 2
|
||||
|
||||
def execute(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load(IfcStore.get_file())
|
||||
@@ -284,6 +290,13 @@ class ConvertGlobalToLocal(bpy.types.Operator):
|
||||
bl_label = "Convert Global To Local"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and file.by_type("IfcUnitAssignment") \
|
||||
and props.coordinate_input.count(",") == 2
|
||||
|
||||
def execute(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load(IfcStore.get_file())
|
||||
@@ -330,6 +343,11 @@ class GetCursorLocation(bpy.types.Operator):
|
||||
bl_label = "Get Cursor Location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
return file and file.by_type("IfcUnitAssignment")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
||||
@@ -343,6 +361,12 @@ class SetCursorLocation(bpy.types.Operator):
|
||||
bl_label = "Set Cursor Location"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and file.by_type("IfcUnitAssignment") and props.coordinate_output.count(",") == 2
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
||||
|
||||
@@ -52,8 +52,8 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
||||
default="foot",
|
||||
)
|
||||
is_map_unit_null: BoolProperty(name="Is Map Unit Null")
|
||||
coordinate_input: StringProperty(name="Coordinate Input")
|
||||
coordinate_output: StringProperty(name="Coordinate Output")
|
||||
coordinate_input: StringProperty(name="Coordinate Input", description="Formatted \"x,y,z\" (without quotes)")
|
||||
coordinate_output: StringProperty(name="Coordinate Output", description="Formatted \"x,y,z\" (without quotes)")
|
||||
has_blender_offset: BoolProperty(name="Has Blender Offset")
|
||||
blender_eastings: StringProperty(name="Blender Eastings", default="0")
|
||||
blender_northings: StringProperty(name="Blender Northings", default="0")
|
||||
|
||||
@@ -129,6 +129,10 @@ class AddTypeInstance(bpy.types.Operator):
|
||||
else:
|
||||
if collection_obj and collection_obj.BIMObjectProperties.ifc_definition_id:
|
||||
obj.location[2] = collection_obj.location[2] - min([v[2] for v in obj.bound_box])
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
return {"FINISHED"}
|
||||
|
||||
def generate_layered_element(self, ifc_class, relating_type):
|
||||
@@ -205,8 +209,14 @@ class DynamicallyVoidProduct(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
if obj is None:
|
||||
return {"FINISHED"}
|
||||
product = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if not product.HasOpenings:
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# 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 blenderbim.bim.module.type.prop as type_prop
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -35,9 +36,21 @@ class BIM_PT_authoring(Panel):
|
||||
def draw(self, context):
|
||||
tprops = context.scene.BIMTypeProperties
|
||||
col = self.layout.column(align=True)
|
||||
col.prop(tprops, "ifc_class", text="", icon="FILE_VOLUME")
|
||||
col.prop(tprops, "relating_type", text="", icon="FILE_3D")
|
||||
col.operator("bim.add_type_instance", icon="ADD")
|
||||
enabled = True
|
||||
|
||||
if type_prop.getIfcTypes(tprops, context):
|
||||
col.prop(tprops, "ifc_class", text="", icon="FILE_VOLUME")
|
||||
else:
|
||||
col.label(text="No IFC Class", icon="FILE_VOLUME")
|
||||
enabled = False
|
||||
if type_prop.getAvailableTypes(tprops, context):
|
||||
col.prop(tprops, "relating_type", text="", icon="FILE_3D")
|
||||
else:
|
||||
col.label(text="No Relating Type", icon="FILE_3D")
|
||||
enabled = False
|
||||
row = col.row()
|
||||
row.operator("bim.add_type_instance", icon="ADD")
|
||||
row.enabled = enabled
|
||||
|
||||
|
||||
class BIM_PT_authoring_architectural(Panel):
|
||||
@@ -76,9 +89,8 @@ class BIM_PT_misc_utilities(Panel):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMProperties
|
||||
|
||||
row = layout.row()
|
||||
row = layout.split(factor=0.2, align=True)
|
||||
row.prop(props, "override_colour", text="")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.set_override_colour")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.set_viewport_shadow_from_sun")
|
||||
|
||||
@@ -74,12 +74,14 @@ class JoinWall(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
join_type: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
selected_objs = context.selected_objects
|
||||
selected_objs = [o for o in context.selected_objects if o.BIMObjectProperties.ifc_definition_id]
|
||||
for obj in selected_objs:
|
||||
bpy.ops.bim.dynamically_void_product(obj=obj.name)
|
||||
if len(selected_objs) == 0:
|
||||
return {"FINISHED"}
|
||||
if not self.join_type:
|
||||
for obj in selected_objs:
|
||||
DumbWallJoiner(obj, obj).unjoin()
|
||||
@@ -114,11 +116,14 @@ class AlignWall(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
align_type: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
selected_valid_objects = [o for o in context.selected_objects if o.data and hasattr(o.data, "transform")]
|
||||
return context.active_object and len(selected_valid_objects) > 1
|
||||
|
||||
def execute(self, context):
|
||||
selected_objs = context.selected_objects
|
||||
if len(selected_objs) < 2 or not context.active_object:
|
||||
return {"FINISHED"}
|
||||
for obj in selected_objs:
|
||||
selected_objects = [o for o in context.selected_objects if o.data and hasattr(o.data, "transform")]
|
||||
for obj in selected_objects:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
aligner = DumbWallAligner(obj, context.active_object)
|
||||
@@ -137,10 +142,12 @@ class FlipWall(bpy.types.Operator):
|
||||
bl_label = "Flip Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
selected_objs = context.selected_objects
|
||||
if len(selected_objs) == 0:
|
||||
return {"FINISHED"}
|
||||
selected_objs = [o for o in context.selected_objects if o.data and hasattr(o.data, "transform")]
|
||||
for obj in selected_objs:
|
||||
DumbWallFlipper(obj).flip()
|
||||
IfcStore.edited_objs.add(obj)
|
||||
@@ -152,12 +159,14 @@ class SplitWall(bpy.types.Operator):
|
||||
bl_label = "Split Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
selected_objs = context.selected_objects
|
||||
if len(selected_objs) == 0:
|
||||
return {"FINISHED"}
|
||||
selected_objs = [o for o in context.selected_objects if o.data and hasattr(o.data, "transform")]
|
||||
for obj in selected_objs:
|
||||
DumbWallSplitter(obj, bpy.context.scene.cursor.location).split()
|
||||
DumbWallSplitter(obj, context.scene.cursor.location).split()
|
||||
IfcStore.edited_objs.add(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import blenderbim.bim.module.type.prop as type_prop
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -52,9 +53,20 @@ class BimTool(WorkSpaceTool):
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
row = layout.row(align=True)
|
||||
if not IfcStore.get_file():
|
||||
row.label(text="No IFC Project", icon="ERROR")
|
||||
return
|
||||
props = context.scene.BIMTypeProperties
|
||||
row.prop(props, "ifc_class", text="")
|
||||
row.prop(props, "relating_type", text="")
|
||||
ifc_classes_is_empty = not bool(type_prop.getIfcTypes(props, context))
|
||||
if ifc_classes_is_empty:
|
||||
row.label(text="No IFC Class")
|
||||
else:
|
||||
row.prop(props, "ifc_class", text="")
|
||||
if type_prop.getAvailableTypes(props, context):
|
||||
row.prop(props, "relating_type", text="")
|
||||
else:
|
||||
row.label(text="No Relating Type")
|
||||
|
||||
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
@@ -62,34 +74,35 @@ class BimTool(WorkSpaceTool):
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Add Type Instance", icon="EVENT_A")
|
||||
|
||||
if props.ifc_class == "IfcWallType":
|
||||
row = layout.row()
|
||||
row.label(text="Join")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Butt", icon="EVENT_T")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Mitre", icon="EVENT_Y")
|
||||
if not ifc_classes_is_empty:
|
||||
if props.ifc_class == "IfcWallType":
|
||||
row = layout.row()
|
||||
row.label(text="Join")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Butt", icon="EVENT_T")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Mitre", icon="EVENT_Y")
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Wall Tools")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Flip", icon="EVENT_F")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Split", icon="EVENT_S")
|
||||
row = layout.row()
|
||||
row.label(text="Wall Tools")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Flip", icon="EVENT_F")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Split", icon="EVENT_S")
|
||||
|
||||
if props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
||||
row = layout.row()
|
||||
row.label(text="Join")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
if props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"):
|
||||
row = layout.row()
|
||||
row.label(text="Join")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
@@ -124,11 +137,16 @@ class Hotkey(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
hotkey: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.BIMTypeProperties
|
||||
self.ifc_classes_is_empty = not bool(type_prop.getIfcTypes(self.props, context))
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -136,26 +154,29 @@ class Hotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.add_type_instance()
|
||||
|
||||
def hotkey_S_C(self):
|
||||
if self.props.ifc_class == "IfcWallType":
|
||||
if not self.ifc_classes_is_empty and self.props.ifc_class == "IfcWallType":
|
||||
bpy.ops.bim.align_wall(align_type="CENTERLINE")
|
||||
else:
|
||||
bpy.ops.bim.align_product(align_type="CENTERLINE")
|
||||
|
||||
def hotkey_S_E(self):
|
||||
if self.ifc_classes_is_empty:
|
||||
return
|
||||
if self.props.ifc_class == "IfcWallType":
|
||||
bpy.ops.bim.join_wall(join_type="T")
|
||||
elif self.props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
||||
bpy.ops.bim.extend_profile()
|
||||
|
||||
def hotkey_S_V(self):
|
||||
if self.props.ifc_class == "IfcWallType":
|
||||
if not self.ifc_classes_is_empty and self.props.ifc_class == "IfcWallType":
|
||||
bpy.ops.bim.align_wall(align_type="INTERIOR")
|
||||
else:
|
||||
bpy.ops.bim.align_product(align_type="POSITIVE")
|
||||
|
||||
def hotkey_S_X(self):
|
||||
if self.props.ifc_class == "IfcWallType":
|
||||
bpy.ops.bim.align_wall(align_type="EXTERIOR")
|
||||
if not self.ifc_classes_is_empty and self.props.ifc_class == "IfcWallType":
|
||||
if bpy.ops.bim.align_wall.poll():
|
||||
bpy.ops.bim.align_wall(align_type="EXTERIOR")
|
||||
else:
|
||||
bpy.ops.bim.align_product(align_type="NEGATIVE")
|
||||
|
||||
|
||||
@@ -25,12 +25,38 @@ def calculate_height(obj):
|
||||
return obj.dimensions[2]
|
||||
|
||||
|
||||
def calculate_volume(obj):
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
result = bm.calc_volume()
|
||||
bm.free()
|
||||
return result
|
||||
def calculate_edges_lengths(objs, context):
|
||||
return calculate_mesh_quantity(objs, context, lambda bm: sum((e.calc_length() for e in bm.edges if e.select)))
|
||||
|
||||
|
||||
def calculate_faces_areas(objs, context):
|
||||
return calculate_mesh_quantity(objs, context, lambda bm: sum((f.calc_area() for f in bm.faces if f.select)))
|
||||
|
||||
|
||||
def calculate_volumes(objs, context):
|
||||
return calculate_mesh_quantity(objs, context, lambda bm: bm.calc_volume())
|
||||
|
||||
|
||||
def calculate_mesh_quantity(objs: bpy.types.Object, context, operation):
|
||||
"""Get the sum of the target quantity on all passed mesh objects
|
||||
|
||||
:param objs: iterable of mesh object
|
||||
:param context: current execution context
|
||||
:param operation: function which takes a single bmesh as an argument, returns a float value
|
||||
:returns float:
|
||||
"""
|
||||
result = 0
|
||||
edit_mode = context.active_object.mode == "EDIT"
|
||||
for obj in objs:
|
||||
if edit_mode:
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
result += operation(bm)
|
||||
else:
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
result += operation(bm)
|
||||
bm.free()
|
||||
return result
|
||||
|
||||
|
||||
def calculate_formwork_area(objs, context):
|
||||
@@ -49,10 +75,11 @@ def calculate_formwork_area(objs, context):
|
||||
context.collection.objects.link(new_obj)
|
||||
copied_objs.append(new_obj)
|
||||
|
||||
context_override = {}
|
||||
context_override["object"] = context_override["active_object"] = copied_objs[0]
|
||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = copied_objs
|
||||
bpy.ops.object.join(context_override)
|
||||
if len(objs) > 1:
|
||||
context_override = {}
|
||||
context_override["object"] = context_override["active_object"] = copied_objs[0]
|
||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = copied_objs
|
||||
bpy.ops.object.join(context_override)
|
||||
|
||||
copied_objs[0].name = "Formwork"
|
||||
copied_objs[0].BIMObjectProperties.ifc_definition_id = 0
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -31,14 +30,12 @@ class CalculateEdgeLengths(bpy.types.Operator):
|
||||
bl_label = "Calculate Edge Lengths"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects and context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
result = 0
|
||||
for obj in context.selected_objects:
|
||||
if not obj.data or not obj.data.edges:
|
||||
continue
|
||||
for edge in obj.data.edges:
|
||||
if edge.select:
|
||||
result += (obj.data.vertices[edge.vertices[1]].co - obj.data.vertices[edge.vertices[0]].co).length
|
||||
result = helper.calculate_edges_lengths([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -48,14 +45,12 @@ class CalculateFaceAreas(bpy.types.Operator):
|
||||
bl_label = "Calculate Face Areas"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects and context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
result = 0
|
||||
for obj in context.selected_objects:
|
||||
if not obj.data or not obj.data.polygons:
|
||||
continue
|
||||
for polygon in obj.data.polygons:
|
||||
if polygon.select:
|
||||
result += polygon.area
|
||||
result = helper.calculate_faces_areas([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -65,15 +60,12 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
||||
bl_label = "Calculate Object Volumes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects and context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
result = 0
|
||||
for obj in context.selected_objects:
|
||||
if not obj.data or not isinstance(obj.data, bpy.types.Mesh):
|
||||
continue
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
result += bm.calc_volume()
|
||||
bm.free()
|
||||
result = helper.calculate_volumes([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -83,17 +75,21 @@ class ExecuteQtoMethod(bpy.types.Operator):
|
||||
bl_label = "Execute Qto Method"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
selected_mesh_objects = [o for o in context.selected_objects if o.type == "MESH"]
|
||||
props = context.scene.BIMQtoProperties
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
for obj in context.selected_objects:
|
||||
for obj in selected_mesh_objects:
|
||||
result += helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
for obj in context.selected_objects:
|
||||
result += helper.calculate_volume(obj)
|
||||
result = helper.calculate_volumes(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area(context.selected_objects, context)
|
||||
result = helper.calculate_formwork_area(selected_mesh_objects, context)
|
||||
props.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -103,20 +99,24 @@ class QuantifyObjects(bpy.types.Operator):
|
||||
bl_label = "Quantify Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMQtoProperties
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
for obj in (o for o in context.selected_objects if o.type == "MESH"):
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
result = helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
result = helper.calculate_volume(obj)
|
||||
result = helper.calculate_volumes([obj], context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area([obj], context)
|
||||
if not result:
|
||||
|
||||
@@ -411,11 +411,13 @@ class RemoveSectionPlane(bpy.types.Operator):
|
||||
bl_label = "Remove Temporary Section Cutaway"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and bpy.data.node_groups.get("Section Override")
|
||||
|
||||
def execute(self, context):
|
||||
name = context.active_object.name
|
||||
section_override = bpy.data.node_groups.get("Section Override")
|
||||
if not section_override:
|
||||
return {"FINISHED"}
|
||||
for node in section_override.nodes:
|
||||
if node.type != "TEX_COORD" or node.object.name != name:
|
||||
continue
|
||||
@@ -497,8 +499,11 @@ class SetOverrideColour(bpy.types.Operator):
|
||||
bl_label = "Set Override Colour"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
result = 0
|
||||
for obj in context.selected_objects:
|
||||
obj.color = context.scene.BIMProperties.override_colour
|
||||
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
|
||||
@@ -511,6 +516,10 @@ class SetViewportShadowFromSun(bpy.types.Operator):
|
||||
bl_label = "Set Viewport Shadow from Sun"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
# The vector used for the light direction is a bit funny
|
||||
mat = Matrix(((-1.0, 0.0, 0.0, 0.0), (0.0, 0, 1.0, 0.0), (-0.0, -1.0, 0, 0.0), (0.0, 0.0, 0.0, 1.0)))
|
||||
@@ -553,17 +562,20 @@ class SnapSpacesTogether(bpy.types.Operator):
|
||||
bl_label = "Snap Spaces Together"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
threshold = 0.5
|
||||
processed_polygons = set()
|
||||
for obj in context.selected_objects:
|
||||
if obj.type != "MESH":
|
||||
continue
|
||||
selected_mesh_objects = [o for o in context.selected_objects if o.type == "MESH"]
|
||||
for obj in selected_mesh_objects:
|
||||
for polygon in obj.data.polygons:
|
||||
center = obj.matrix_world @ polygon.center
|
||||
distance = None
|
||||
for obj2 in context.selected_objects:
|
||||
if obj2 == obj or obj.type != "MESH":
|
||||
for obj2 in selected_mesh_objects:
|
||||
if obj2 == obj:
|
||||
continue
|
||||
result = obj2.ray_cast(obj2.matrix_world.inverted() @ center, polygon.normal, distance=threshold)
|
||||
if not result[0]:
|
||||
|
||||
@@ -63,8 +63,7 @@ def extract_docs(
|
||||
method_name: str="__init__",
|
||||
boilerplate_args : typing.Iterable[str]=None):
|
||||
"""Extract class docstrings and method arguments
|
||||
|
||||
:param module: Parent module from which to extract the submodule class
|
||||
|
||||
:param submodule_name: Submodule from which to extract the class
|
||||
:param cls_name: Class from which to extract the docstring and method arguments
|
||||
:param method_name: Class Method name from which to extract arguments
|
||||
|
||||
Reference in New Issue
Block a user