mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +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 .connect_element import connect_element
|
||||
from .connect_path import connect_path
|
||||
from .connect_wall import connect_wall
|
||||
from .create_2pt_wall import create_2pt_wall
|
||||
from .disconnect_element import disconnect_element
|
||||
from .disconnect_path import disconnect_path
|
||||
@@ -72,6 +73,7 @@ __all__ = [
|
||||
"assign_representation",
|
||||
"connect_element",
|
||||
"connect_path",
|
||||
"connect_wall",
|
||||
"create_2pt_wall",
|
||||
"disconnect_element",
|
||||
"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)
|
||||
if not layers:
|
||||
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.wall_vectors = self.get_wall_vectors(wall)
|
||||
axes = self.get_axes(wall, reference, layers, self.wall_vectors["a"])
|
||||
@@ -300,8 +300,8 @@ class Regenerator:
|
||||
print("joining", wall1, layers1, connection1)
|
||||
print("to", wall2, layers2, connection2)
|
||||
|
||||
reference1 = self.get_reference_line(wall1)
|
||||
reference2 = self.get_reference_line(wall2)
|
||||
reference1 = ifcopenshell.util.representation.get_reference_line(wall1, self.fallback_length)
|
||||
reference2 = ifcopenshell.util.representation.get_reference_line(wall2, self.fallback_length)
|
||||
wall_vectors2 = self.get_wall_vectors(wall2)
|
||||
axes1 = self.get_axes(wall1, reference1, layers1, self.wall_vectors["a"])
|
||||
axes2 = self.get_axes(wall2, reference2, layers2, wall_vectors2["a"])
|
||||
@@ -516,20 +516,6 @@ class Regenerator:
|
||||
results.append(layer)
|
||||
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):
|
||||
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
|
||||
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
|
||||
@@ -572,7 +558,7 @@ class Regenerator:
|
||||
axes = [[p.copy() for p in reference]]
|
||||
# Apply usage to convert the Reference line into MlsBase
|
||||
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]:
|
||||
point[1] += usage.OffsetFromReferenceLine
|
||||
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
||||
|
||||
@@ -461,3 +461,22 @@ def get_material_style(
|
||||
for style in item.Styles:
|
||||
if style.is_a(ifc_class):
|
||||
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