mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Bonsai - extract elements from selection #4316
Demo - https://imgchest.com/p/xny8qvox5yb You can also now reuse 'bpy.data.texts['globalid-filter-3UvnD5Ml95zBDEcwAkAUIp']' kind of queries from Search in ExtractElements query.
This commit is contained in:
@@ -52,6 +52,7 @@ def draw_attributes(
|
||||
layout: bpy.types.UILayout,
|
||||
copy_operator: Optional[str] = None,
|
||||
popup_active_attribute: Optional[bonsai.bim.prop.Attribute] = None,
|
||||
callback: Optional[Callable[[bonsai.bim.prop.Attribute, bpy.types.UILayout], None]] = None,
|
||||
) -> None:
|
||||
"""you can set attribute active in popup with `active_attribute`
|
||||
meaning you will be able to type into attribute's field without having to click
|
||||
@@ -62,6 +63,8 @@ def draw_attributes(
|
||||
if attribute == popup_active_attribute:
|
||||
row.activate_init = True
|
||||
draw_attribute(attribute, row, copy_operator)
|
||||
if callback:
|
||||
callback(attribute, row)
|
||||
|
||||
|
||||
def draw_attribute(
|
||||
|
||||
@@ -21,6 +21,7 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ExecuteIfcPatch,
|
||||
operator.ExtractSelectedElements,
|
||||
operator.RunMigratePatch,
|
||||
operator.SelectIfcPatchInput,
|
||||
operator.SelectIfcPatchOutput,
|
||||
|
||||
@@ -89,6 +89,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
|
||||
value = value[0]
|
||||
arguments.append(value)
|
||||
|
||||
arguments = tool.Patch.post_process_patch_arguments(recipe_name, arguments)
|
||||
args = ifcpatch.ArgumentsDict(
|
||||
recipe=props.ifc_patch_recipes,
|
||||
arguments=arguments,
|
||||
@@ -208,3 +209,21 @@ class RunMigratePatch(bpy.types.Operator):
|
||||
pass # Probably running in headless mode
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExtractSelectedElements(bpy.types.Operator):
|
||||
bl_idname = "bim.patch_query_from_selected"
|
||||
bl_label = "Fill patch query based on the selected elements"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMPatchProperties
|
||||
recipe_name = props.ifc_patch_recipes
|
||||
|
||||
if recipe_name != "ExtractElements":
|
||||
self.report({"ERROR"}, "Only supported for the 'ExtractElements' recipe.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
query = tool.Search.get_query_for_selected_elements()
|
||||
props.ifc_patch_args_attr[0].string_value = query
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -55,6 +55,12 @@ class BIM_PT_patch(bpy.types.Panel):
|
||||
row.prop(props, "ifc_patch_output")
|
||||
row.operator("bim.select_ifc_patch_output", icon="FILE_FOLDER", text="")
|
||||
|
||||
def draw_callback_(_, row: bpy.types.UILayout) -> None:
|
||||
if props.ifc_patch_recipes == "ExtractElements":
|
||||
row.operator("bim.patch_query_from_selected", text="", icon="EYEDROPPER")
|
||||
|
||||
if props.ifc_patch_args_attr:
|
||||
draw_attributes(props.ifc_patch_args_attr, layout)
|
||||
draw_callback = draw_callback_ if props.ifc_patch_recipes == "ExtractElements" else None
|
||||
draw_attributes(props.ifc_patch_args_attr, layout, callback=draw_callback)
|
||||
|
||||
op = layout.operator("bim.execute_ifc_patch")
|
||||
|
||||
@@ -101,20 +101,10 @@ class SelectFilterElements(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
filter_groups = tool.Search.get_filter_groups(self.module)
|
||||
global_ids = []
|
||||
for obj in context.selected_objects:
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
if global_id := getattr(element, "GlobalId", None):
|
||||
global_ids.append(global_id)
|
||||
if len(global_ids) > 50:
|
||||
# Too much to store in a string property
|
||||
name = f"globalid-filter-{ifcopenshell.guid.new()}"
|
||||
text_data = bpy.data.texts.new(name)
|
||||
text_data.from_string(",".join(global_ids))
|
||||
filter_groups[self.group_index].filters[self.index].value = f"bpy.data.texts['{name}']"
|
||||
query = tool.Search.get_query_for_selected_elements()
|
||||
filter_groups[self.group_index].filters[self.index].value = query
|
||||
if query.startswith("bpy.data.texts['"):
|
||||
self.report({"INFO"}, f'List of Global Ids was saved to the text file "{name}" in the current .blend file')
|
||||
else:
|
||||
filter_groups[self.group_index].filters[self.index].value = ",".join(global_ids)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcpatch
|
||||
import bonsai.core.tool
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Patch(bonsai.core.tool.Patch):
|
||||
@@ -40,3 +41,13 @@ class Patch(bonsai.core.tool.Patch):
|
||||
@classmethod
|
||||
def does_patch_has_output(cls, recipe: str) -> bool:
|
||||
return recipe != "SplitByBuildingStorey"
|
||||
|
||||
@classmethod
|
||||
def post_process_patch_arguments(cls, recipe: str, args: list[Any]) -> list[Any]:
|
||||
if recipe == "ExtractElements":
|
||||
query = args[0]
|
||||
assert isinstance(query, str)
|
||||
if "bpy.data.texts" in query:
|
||||
text_name = query.split("bpy.data.texts")[1][2:-2]
|
||||
args[0] = bpy.data.texts[text_name].as_string()
|
||||
return args
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# Bonsai 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.
|
||||
#
|
||||
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import bpy
|
||||
import json
|
||||
import lark
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.tool
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.selector
|
||||
@@ -267,6 +287,23 @@ class Search(bonsai.core.tool.Search):
|
||||
return palette[-1]
|
||||
return cls.interpolate_color(palette[index], palette[index + 1], fraction)
|
||||
|
||||
@classmethod
|
||||
def get_query_for_selected_elements(cls) -> str:
|
||||
global_ids = []
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
if global_id := getattr(element, "GlobalId", None):
|
||||
global_ids.append(global_id)
|
||||
|
||||
query = ",".join(global_ids)
|
||||
if len(global_ids) > 50:
|
||||
# Too much to store in a string property.
|
||||
name = f"globalid-filter-{ifcopenshell.guid.new()}"
|
||||
text_data = bpy.data.texts.new(name)
|
||||
text_data.from_string(query)
|
||||
query = f"bpy.data.texts['{name}']"
|
||||
return query
|
||||
|
||||
|
||||
class ImportFilterQueryTransformer(lark.Transformer):
|
||||
def __init__(self, filter_groups):
|
||||
|
||||
Reference in New Issue
Block a user