mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-03 05:22:06 +00:00
See #1227. New connect wall API function since this can be done generically.
This will supersede the butt/mitre join functions we used to have.
This commit is contained in:
@@ -43,6 +43,7 @@ from .add_window_representation import add_window_representation
|
|||||||
from .assign_representation import assign_representation
|
from .assign_representation import assign_representation
|
||||||
from .connect_element import connect_element
|
from .connect_element import connect_element
|
||||||
from .connect_path import connect_path
|
from .connect_path import connect_path
|
||||||
|
from .connect_wall import connect_wall
|
||||||
from .create_2pt_wall import create_2pt_wall
|
from .create_2pt_wall import create_2pt_wall
|
||||||
from .disconnect_element import disconnect_element
|
from .disconnect_element import disconnect_element
|
||||||
from .disconnect_path import disconnect_path
|
from .disconnect_path import disconnect_path
|
||||||
@@ -72,6 +73,7 @@ __all__ = [
|
|||||||
"assign_representation",
|
"assign_representation",
|
||||||
"connect_element",
|
"connect_element",
|
||||||
"connect_path",
|
"connect_path",
|
||||||
|
"connect_wall",
|
||||||
"create_2pt_wall",
|
"create_2pt_wall",
|
||||||
"disconnect_element",
|
"disconnect_element",
|
||||||
"disconnect_path",
|
"disconnect_path",
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# IfcOpenShell - IFC toolkit and geometry engine
|
||||||
|
# Copyright (C) 2025 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of IfcOpenShell.
|
||||||
|
#
|
||||||
|
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 numpy as np
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api.owner
|
||||||
|
import ifcopenshell.guid
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.util.placement
|
||||||
|
import ifcopenshell.util.representation
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def connect_wall(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
wall1: ifcopenshell.entity_instance,
|
||||||
|
wall2: ifcopenshell.entity_instance,
|
||||||
|
is_atpath: bool = False,
|
||||||
|
) -> Optional[ifcopenshell.entity_instance]:
|
||||||
|
matrix1i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(wall1.ObjectPlacement))
|
||||||
|
matrix2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement)
|
||||||
|
axis1 = ifcopenshell.util.representation.get_reference_line(wall1)
|
||||||
|
axis2 = ifcopenshell.util.representation.get_reference_line(wall2)
|
||||||
|
axis2[0] = (matrix1i @ matrix2 @ np.concatenate((axis2[0], (0, 1))))[:2]
|
||||||
|
axis2[1] = (matrix1i @ matrix2 @ np.concatenate((axis2[1], (0, 1))))[:2]
|
||||||
|
midx = (axis1[0][0] + axis1[1][0]) / 2
|
||||||
|
starty = axis2[0][1]
|
||||||
|
endy = axis2[1][1]
|
||||||
|
y = axis1[0][1]
|
||||||
|
|
||||||
|
if (x := ifcopenshell.util.shape_builder.intersect_x_axis_2d(*axis2, y=y)) is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
wall1_end = "ATEND" if x > midx else "ATSTART"
|
||||||
|
if is_atpath:
|
||||||
|
wall2_end = "ATPATH"
|
||||||
|
elif abs(y - starty) < abs(y - endy):
|
||||||
|
wall2_end = "ATSTART"
|
||||||
|
else:
|
||||||
|
wall2_end = "ATEND"
|
||||||
|
|
||||||
|
return ifcopenshell.api.geometry.connect_path(
|
||||||
|
file,
|
||||||
|
relating_element=wall1,
|
||||||
|
related_element=wall2,
|
||||||
|
relating_connection=wall1_end,
|
||||||
|
related_connection=wall2_end,
|
||||||
|
)
|
||||||
+4
-18
@@ -93,7 +93,7 @@ class Regenerator:
|
|||||||
layers = self.get_layers(wall)
|
layers = self.get_layers(wall)
|
||||||
if not layers:
|
if not layers:
|
||||||
return
|
return
|
||||||
reference = self.get_reference_line(wall)
|
reference = ifcopenshell.util.representation.get_reference_line(wall, self.fallback_length)
|
||||||
self.reference_p1, self.reference_p2 = reference
|
self.reference_p1, self.reference_p2 = reference
|
||||||
self.wall_vectors = self.get_wall_vectors(wall)
|
self.wall_vectors = self.get_wall_vectors(wall)
|
||||||
axes = self.get_axes(wall, reference, layers, self.wall_vectors["a"])
|
axes = self.get_axes(wall, reference, layers, self.wall_vectors["a"])
|
||||||
@@ -300,8 +300,8 @@ class Regenerator:
|
|||||||
print("joining", wall1, layers1, connection1)
|
print("joining", wall1, layers1, connection1)
|
||||||
print("to", wall2, layers2, connection2)
|
print("to", wall2, layers2, connection2)
|
||||||
|
|
||||||
reference1 = self.get_reference_line(wall1)
|
reference1 = ifcopenshell.util.representation.get_reference_line(wall1, self.fallback_length)
|
||||||
reference2 = self.get_reference_line(wall2)
|
reference2 = ifcopenshell.util.representation.get_reference_line(wall2, self.fallback_length)
|
||||||
wall_vectors2 = self.get_wall_vectors(wall2)
|
wall_vectors2 = self.get_wall_vectors(wall2)
|
||||||
axes1 = self.get_axes(wall1, reference1, layers1, self.wall_vectors["a"])
|
axes1 = self.get_axes(wall1, reference1, layers1, self.wall_vectors["a"])
|
||||||
axes2 = self.get_axes(wall2, reference2, layers2, wall_vectors2["a"])
|
axes2 = self.get_axes(wall2, reference2, layers2, wall_vectors2["a"])
|
||||||
@@ -516,20 +516,6 @@ class Regenerator:
|
|||||||
results.append(layer)
|
results.append(layer)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_reference_line(self, wall):
|
|
||||||
if axis := ifcopenshell.util.representation.get_representation(wall, "Plan", "Axis", "GRAPH_VIEW"):
|
|
||||||
for item in ifcopenshell.util.representation.resolve_representation(axis).Items:
|
|
||||||
if item.is_a("IfcPolyline"):
|
|
||||||
points = item.Points
|
|
||||||
elif item.is_a("IfcIndexedPolyCurve"):
|
|
||||||
points = item.Points.CoordList
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if points[0][0] < points[1][0]: # An axis always goes in the +X direction
|
|
||||||
return [np.array(points[0]), np.array(points[1])]
|
|
||||||
return [np.array(points[1]), np.array(points[0])]
|
|
||||||
return [np.array((0.0, 0.0)), np.array((self.fallback_length, 0.0))]
|
|
||||||
|
|
||||||
def get_wall_vectors(self, wall):
|
def get_wall_vectors(self, wall):
|
||||||
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
|
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
|
||||||
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
||||||
@@ -572,7 +558,7 @@ class Regenerator:
|
|||||||
axes = [[p.copy() for p in reference]]
|
axes = [[p.copy() for p in reference]]
|
||||||
# Apply usage to convert the Reference line into MlsBase
|
# Apply usage to convert the Reference line into MlsBase
|
||||||
sense_factor = 1
|
sense_factor = 1
|
||||||
if (usage := ifcopenshell.util.element.get_material(wall)) and usage.is_a("IfcMaterialLayerSetUage"):
|
if (usage := ifcopenshell.util.element.get_material(wall)) and usage.is_a("IfcMaterialLayerSetUsage"):
|
||||||
for point in axes[0]:
|
for point in axes[0]:
|
||||||
point[1] += usage.OffsetFromReferenceLine
|
point[1] += usage.OffsetFromReferenceLine
|
||||||
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
||||||
|
|||||||
@@ -461,3 +461,22 @@ def get_material_style(
|
|||||||
for style in item.Styles:
|
for style in item.Styles:
|
||||||
if style.is_a(ifc_class):
|
if style.is_a(ifc_class):
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
def get_reference_line(wall: ifcopenshell.entity_instance, fallback_length: float = 1.0):
|
||||||
|
"""Fetch the reference axis that goes in the +X direction
|
||||||
|
|
||||||
|
:param wall: ifcopenshell.entity_instance
|
||||||
|
"""
|
||||||
|
if axis := ifcopenshell.util.representation.get_representation(wall, "Plan", "Axis", "GRAPH_VIEW"):
|
||||||
|
for item in ifcopenshell.util.representation.resolve_representation(axis).Items:
|
||||||
|
if item.is_a("IfcPolyline"):
|
||||||
|
points = item.Points
|
||||||
|
elif item.is_a("IfcIndexedPolyCurve"):
|
||||||
|
points = item.Points.CoordList
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
if points[0][0] < points[1][0]: # An axis always goes in the +X direction
|
||||||
|
return [np.array(points[0]), np.array(points[1])]
|
||||||
|
return [np.array(points[1]), np.array(points[0])]
|
||||||
|
return [np.array((0.0, 0.0)), np.array((fallback_length, 0.0))]
|
||||||
|
|||||||
Reference in New Issue
Block a user