mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
clip_solid/clip_solid_bounded: add element param to register BBIM_Boolean
Add an optional element parameter to geometry.clip_solid and geometry.clip_solid_bounded. When provided, the resulting IfcBooleanClippingResult is registered in the element's BBIM_Boolean property set so that regenerate_wall_representation preserves it during regeneration. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -18,8 +18,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Sequence
|
||||
import json
|
||||
from typing import Optional, Sequence
|
||||
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.data import Clipping
|
||||
|
||||
@@ -61,9 +64,23 @@ def clip_solid(
|
||||
or ``IfcBooleanClippingResult``).
|
||||
:param location: A point on the clipping plane in the representation's
|
||||
local coordinate system.
|
||||
:param normal: Plane normal pointing toward the material to be removed.
|
||||
:param normal: Plane normal pointing toward the material to be removed
|
||||
(see warning above).
|
||||
:param element: If provided, the resulting ``IfcBooleanClippingResult`` is
|
||||
registered in the element's ``BBIM_Boolean`` property set so that
|
||||
:func:`regenerate_wall_representation` preserves it during regeneration.
|
||||
:return: The resulting ``IfcBooleanClippingResult``.
|
||||
"""
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
|
||||
clipping = Clipping(location=tuple(location), normal=tuple(normal))
|
||||
return clipping.apply(file, item, unit_scale)
|
||||
result = clipping.apply(file, item, unit_scale)
|
||||
if element is not None:
|
||||
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
|
||||
if pset_data:
|
||||
pset = file.by_id(pset_data["id"])
|
||||
data = list(set(json.loads(pset_data["Data"]) + [result.id()]))
|
||||
else:
|
||||
pset = ifcopenshell.api.pset.add_pset(file, product=element, name="BBIM_Boolean")
|
||||
data = [result.id()]
|
||||
ifcopenshell.api.pset.edit_pset(file, pset=pset, properties={"Data": json.dumps(data)})
|
||||
return result
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Sequence
|
||||
import json
|
||||
from typing import Optional, Sequence
|
||||
|
||||
import numpy as np
|
||||
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||
|
||||
@@ -33,6 +36,7 @@ def clip_solid_bounded(
|
||||
normal: Sequence[float],
|
||||
boundary_points: Sequence[Sequence[float]],
|
||||
boundary_position: Sequence[float] = (0.0, 0.0, 0.0),
|
||||
element: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Clip a solid with a polygonally bounded half-space, returning an IfcBooleanClippingResult.
|
||||
|
||||
@@ -67,6 +71,9 @@ def clip_solid_bounded(
|
||||
is automatically closed — do not repeat the first point.
|
||||
:param boundary_position: 3D origin of the boundary coordinate system
|
||||
(axes default to the global X/Y/Z directions). Defaults to the origin.
|
||||
:param element: If provided, the resulting ``IfcBooleanClippingResult`` is
|
||||
registered in the element's ``BBIM_Boolean`` property set so that
|
||||
:func:`regenerate_wall_representation` preserves it during regeneration.
|
||||
:return: The resulting ``IfcBooleanClippingResult``.
|
||||
"""
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
|
||||
@@ -96,4 +103,14 @@ def clip_solid_bounded(
|
||||
boundary = file.createIfcPolyline(ifc_pts)
|
||||
|
||||
half_space = file.create_entity("IfcPolygonalBoundedHalfSpace", plane, False, boundary_pos_entity, boundary)
|
||||
return file.create_entity("IfcBooleanClippingResult", "DIFFERENCE", item, half_space)
|
||||
result = file.create_entity("IfcBooleanClippingResult", "DIFFERENCE", item, half_space)
|
||||
if element is not None:
|
||||
pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
|
||||
if pset_data:
|
||||
pset = file.by_id(pset_data["id"])
|
||||
data = list(set(json.loads(pset_data["Data"]) + [result.id()]))
|
||||
else:
|
||||
pset = ifcopenshell.api.pset.add_pset(file, product=element, name="BBIM_Boolean")
|
||||
data = [result.id()]
|
||||
ifcopenshell.api.pset.edit_pset(file, pset=pset, properties={"Data": json.dumps(data)})
|
||||
return result
|
||||
|
||||
@@ -69,6 +69,12 @@ def regenerate_wall_representation(
|
||||
additional extrusions are generated for each connection that boolean
|
||||
difference the base extrusion.
|
||||
|
||||
Clippings applied via :func:`geometry.clip_solid` or
|
||||
:func:`geometry.clip_solid_bounded` are preserved only if the ``element``
|
||||
parameter was passed when creating them, which registers the result in the
|
||||
``BBIM_Boolean`` property set. Clippings created without that parameter
|
||||
are silently discarded during regeneration.
|
||||
|
||||
This will also update the axis line representation (e.g. trim the axis line
|
||||
to any connections).
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.shape_builder
|
||||
import test.bootstrap
|
||||
|
||||
@@ -100,5 +103,53 @@ class TestClipSolid(test.bootstrap.IFC4):
|
||||
assert result.SecondOperand.is_a("IfcHalfSpaceSolid")
|
||||
|
||||
|
||||
def test_element_registers_result_in_bbim_boolean(self):
|
||||
extrusion = self.make_extrusion()
|
||||
wall = self.file.createIfcWall()
|
||||
result = ifcopenshell.api.geometry.clip_solid(
|
||||
self.file,
|
||||
item=extrusion,
|
||||
location=[0.0, 0.0, 3.0],
|
||||
normal=[0.0, 0.0, 1.0],
|
||||
element=wall,
|
||||
)
|
||||
pset = ifcopenshell.util.element.get_pset(wall, "BBIM_Boolean")
|
||||
assert pset is not None
|
||||
assert result.id() in json.loads(pset["Data"])
|
||||
|
||||
def test_element_appends_to_existing_bbim_boolean(self):
|
||||
extrusion = self.make_extrusion()
|
||||
wall = self.file.createIfcWall()
|
||||
first = ifcopenshell.api.geometry.clip_solid(
|
||||
self.file,
|
||||
item=extrusion,
|
||||
location=[0.0, 0.0, 3.0],
|
||||
normal=[0.0, 0.0, 1.0],
|
||||
element=wall,
|
||||
)
|
||||
second = ifcopenshell.api.geometry.clip_solid(
|
||||
self.file,
|
||||
item=first,
|
||||
location=[0.0, 0.0, 1.0],
|
||||
normal=[0.0, 0.0, -1.0],
|
||||
element=wall,
|
||||
)
|
||||
pset = ifcopenshell.util.element.get_pset(wall, "BBIM_Boolean")
|
||||
ids = json.loads(pset["Data"])
|
||||
assert first.id() in ids
|
||||
assert second.id() in ids
|
||||
|
||||
def test_no_element_does_not_create_pset(self):
|
||||
extrusion = self.make_extrusion()
|
||||
wall = self.file.createIfcWall()
|
||||
ifcopenshell.api.geometry.clip_solid(
|
||||
self.file,
|
||||
item=extrusion,
|
||||
location=[0.0, 0.0, 3.0],
|
||||
normal=[0.0, 0.0, 1.0],
|
||||
)
|
||||
assert ifcopenshell.util.element.get_pset(wall, "BBIM_Boolean") is None
|
||||
|
||||
|
||||
class TestClipSolidIFC2X3(test.bootstrap.IFC2X3, TestClipSolid):
|
||||
pass
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.shape_builder
|
||||
import test.bootstrap
|
||||
|
||||
@@ -145,5 +148,33 @@ class TestClipSolidBounded(test.bootstrap.IFC4):
|
||||
assert first_clip.FirstOperand == extrusion
|
||||
|
||||
|
||||
def test_element_registers_result_in_bbim_boolean(self):
|
||||
extrusion = self.make_extrusion()
|
||||
wall = self.file.createIfcWall()
|
||||
result = ifcopenshell.api.geometry.clip_solid_bounded(
|
||||
self.file,
|
||||
item=extrusion,
|
||||
location=[2.5, 0.0, 2.0],
|
||||
normal=[0.6, 0.0, 0.8],
|
||||
boundary_points=[[2.0, 0.0], [3.0, 0.0], [3.0, 2.0], [2.0, 2.0]],
|
||||
element=wall,
|
||||
)
|
||||
pset = ifcopenshell.util.element.get_pset(wall, "BBIM_Boolean")
|
||||
assert pset is not None
|
||||
assert result.id() in json.loads(pset["Data"])
|
||||
|
||||
def test_no_element_does_not_create_pset(self):
|
||||
extrusion = self.make_extrusion()
|
||||
wall = self.file.createIfcWall()
|
||||
ifcopenshell.api.geometry.clip_solid_bounded(
|
||||
self.file,
|
||||
item=extrusion,
|
||||
location=[2.5, 0.0, 2.0],
|
||||
normal=[0.6, 0.0, 0.8],
|
||||
boundary_points=[[2.0, 0.0], [3.0, 0.0], [3.0, 2.0], [2.0, 2.0]],
|
||||
)
|
||||
assert ifcopenshell.util.element.get_pset(wall, "BBIM_Boolean") is None
|
||||
|
||||
|
||||
class TestClipSolidBoundedIFC2X3(test.bootstrap.IFC2X3, TestClipSolidBounded):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user