This commit is contained in:
Andrej730
2025-07-28 11:45:12 +05:00
parent 23944d6060
commit cdeeab4b31
20 changed files with 161 additions and 134 deletions
+12 -11
View File
@@ -98,6 +98,7 @@ import time
from urllib.request import urlretrieve from urllib.request import urlretrieve
from collections.abc import Generator, Sequence from collections.abc import Generator, Sequence
from pathlib import Path from pathlib import Path
try: try:
from typing import Union, Literal from typing import Union, Literal
except: except:
@@ -241,7 +242,7 @@ cecho(
""" """
) )
dependency_tree: 'dict[str, tuple[str, ...]]' = { dependency_tree: "dict[str, tuple[str, ...]]" = {
"IfcParse": ("boost", "libxml2", "hdf5"), "IfcParse": ("boost", "libxml2", "hdf5"),
"IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen"), "IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen"),
"IfcConvert": ("IfcGeom",), "IfcConvert": ("IfcGeom",),
@@ -263,7 +264,7 @@ dependency_tree: 'dict[str, tuple[str, ...]]' = {
} }
def gather_dependencies(dep: str) -> 'Generator[str]': def gather_dependencies(dep: str) -> "Generator[str]":
yield dep yield dep
for d in dependency_tree[dep]: for d in dependency_tree[dep]:
for x in gather_dependencies(d): for x in gather_dependencies(d):
@@ -323,7 +324,7 @@ except:
pass pass
def run(cmds: 'Sequence[str]', cwd: 'Union[str, None]' = None, can_fail: bool = False) -> str: def run(cmds: "Sequence[str]", cwd: "Union[str, None]" = None, can_fail: bool = False) -> str:
""" """
Wraps `subprocess.Popen.communicate()` and logs the command being executed, Wraps `subprocess.Popen.communicate()` and logs the command being executed,
sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout
@@ -448,15 +449,15 @@ def build_dependency(
"ctest", "ctest",
"bjam", "bjam",
], ],
build_tool_args: 'list[str]', build_tool_args: "list[str]",
download_url: str, download_url: str,
download_name: str, download_name: str,
download_tool: Literal["py", "git"] = download_tool_default, download_tool: Literal["py", "git"] = download_tool_default,
revision: 'Union[str, None]' = None, revision: "Union[str, None]" = None,
patch: 'Union[str, list[str], None]' = None, patch: "Union[str, list[str], None]" = None,
shell=None, shell=None,
pre_compile_subs: 'Sequence[tuple[str, str, str]]' = (), pre_compile_subs: "Sequence[tuple[str, str, str]]" = (),
additional_files: 'Union[dict[str, str], None]' = None, additional_files: "Union[dict[str, str], None]" = None,
no_append_name=False, no_append_name=False,
**kwargs, **kwargs,
) -> None: ) -> None:
@@ -830,7 +831,7 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
# On OSX a dynamic python library is built or it would not be compatible # On OSX a dynamic python library is built or it would not be compatible
# with the system python because of some threading initialization # with the system python because of some threading initialization
PYTHON_CONFIGURE_ARGS: 'list[str]' = [] PYTHON_CONFIGURE_ARGS: "list[str]" = []
if platform.system() == "Darwin": if platform.system() == "Darwin":
PYTHON_CONFIGURE_ARGS = ["--enable-shared"] PYTHON_CONFIGURE_ARGS = ["--enable-shared"]
@@ -895,8 +896,8 @@ if "boost" in targets:
) )
if "cgal" in targets: if "cgal" in targets:
gmp_args: 'list[str]' = [] gmp_args: "list[str]" = []
mpfr_args: 'list[str]' = [] mpfr_args: "list[str]" = []
if "wasm" in flags: if "wasm" in flags:
gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx")) gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx"))
mpfr_args.extend(("--host", "none")) mpfr_args.extend(("--host", "none"))
+6 -3
View File
@@ -64,6 +64,7 @@ def find_bonsai_path() -> Union[Path, None]:
if path.exists(): if path.exists():
return path return path
# BONSAI_PATH: Path to 'bonsai' extension folder inside BLENDER_PATH. # BONSAI_PATH: Path to 'bonsai' extension folder inside BLENDER_PATH.
# Typically resolved automatically, paths priority can be found in `find_bonsai_path`. # Typically resolved automatically, paths priority can be found in `find_bonsai_path`.
# #
@@ -77,6 +78,7 @@ BONSAI_PATH = find_bonsai_path()
# Never changed by user. # Never changed by user.
PACKAGE_PATH = BLENDER_PATH / r"extensions/.local/lib/python3.11/site-packages" PACKAGE_PATH = BLENDER_PATH / r"extensions/.local/lib/python3.11/site-packages"
def main() -> None: def main() -> None:
global REPO_PATH global REPO_PATH
@@ -98,9 +100,10 @@ def main() -> None:
assert REPO_PATH.exists(), f"Path '{REPO_PATH=!s}' doesn't exist, ensure variable is set correctly." assert REPO_PATH.exists(), f"Path '{REPO_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert BLENDER_PATH.exists(), f"Path '{BLENDER_PATH=!s}' doesn't exist, ensure variable is set correctly." assert BLENDER_PATH.exists(), f"Path '{BLENDER_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert PACKAGE_PATH.exists(), f"Path '{PACKAGE_PATH=!s}' doesn't exist, ensure variable is set correctly." assert PACKAGE_PATH.exists(), f"Path '{PACKAGE_PATH=!s}' doesn't exist, ensure variable is set correctly."
assert BONSAI_PATH is not None, ( assert (
"Couldn't find BONSAI_PATH in any of the paths candidates. " BONSAI_PATH is not None
"Example paths: {}".format("\n".join(str(p) for p in BONSAI_PATH_CANDIDATES)) ), "Couldn't find BONSAI_PATH in any of the paths candidates. Example paths: {}".format(
"\n".join(str(p) for p in BONSAI_PATH_CANDIDATES)
) )
input("Confirm the settings above and press Enter to continue or Ctrl-C to cancel...") input("Confirm the settings above and press Enter to continue or Ctrl-C to cancel...")
@@ -47,7 +47,11 @@ def _add_curve_segment_to_composite_curve(
composite_curve.Segments += (curve_segment,) composite_curve.Segments += (curve_segment,)
assert len(curve_segment.UsingCurves) == 1 assert len(curve_segment.UsingCurves) == 1
else: else:
zero_length_segment = composite_curve.Segments[-1] if ifcopenshell.api.alignment.has_zero_length_segment(composite_curve) else None zero_length_segment = (
composite_curve.Segments[-1]
if ifcopenshell.api.alignment.has_zero_length_segment(composite_curve)
else None
)
prev_segment = None prev_segment = None
if zero_length_segment and 1 < len(composite_curve.Segments): if zero_length_segment and 1 < len(composite_curve.Segments):
@@ -59,7 +63,10 @@ def _add_curve_segment_to_composite_curve(
segments = composite_curve.Segments[0:-1] segments = composite_curve.Segments[0:-1]
if zero_length_segment: if zero_length_segment:
segments += (curve_segment,zero_length_segment,) segments += (
curve_segment,
zero_length_segment,
)
composite_curve.Segments = [] composite_curve.Segments = []
composite_curve.Segments += segments composite_curve.Segments += segments
else: else:
@@ -163,12 +163,11 @@ def _add_segment_to_layout(file: ifcopenshell.file, layout: entity_instance, seg
end_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az) end_referent.ObjectPlacement.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
end_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz) end_referent.ObjectPlacement.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
start_station = ifcopenshell.api.alignment.get_alignment_station(file,alignment) start_station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
end_referent_station = start_station + start_dist_along end_referent_station = start_station + start_dist_along
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=end_referent, name="Pset_Stationing") pset_stationing = ifcopenshell.api.pset.add_pset(file, product=end_referent, name="Pset_Stationing")
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": end_referent_station}) ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": end_referent_station})
# create the start of segment referent # create the start of segment referent
# get the previous segment. Working from the end of the basis curve, -1 is zero length segment # get the previous segment. Working from the end of the basis curve, -1 is zero length segment
@@ -22,6 +22,7 @@ from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._get_segment_start_point_label import _get_segment_start_point_label from ifcopenshell.api.alignment._get_segment_start_point_label import _get_segment_start_point_label
def _add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance) -> None: def _add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance) -> None:
""" """
Adds a zero length segment to the end of a layout. Also adds a zero length segment to the end of the corresponding geometric curve. Adds a zero length segment to the end of a layout. Also adds a zero length segment to the end of the corresponding geometric curve.
@@ -32,23 +33,23 @@ def _add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance) -
:param layout: An IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant :param layout: An IfcAlignmentHorizontal, IfcAlignmentVertical, or IfcAlignmentCant
:return: None :return: None
""" """
expected_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant"] expected_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant"]
if not layout.is_a() in expected_types: if not layout.is_a() in expected_types:
raise TypeError( raise TypeError(
f"Expected layout type to be one of {[_ for _ in expected_types]}, instead received {layout.is_a()}" f"Expected layout type to be one of {[_ for _ in expected_types]}, instead received {layout.is_a()}"
) )
if (not ifcopenshell.api.alignment.add_zero_length_segment(file,layout,include_referent=False)): if not ifcopenshell.api.alignment.add_zero_length_segment(file, layout, include_referent=False):
return # zero length segment not added, probably because it already exists return # zero length segment not added, probably because it already exists
curve = ifcopenshell.api.alignment.get_layout_curve(layout) curve = ifcopenshell.api.alignment.get_layout_curve(layout)
if curve: if curve:
ifcopenshell.api.alignment.add_zero_length_segment(file,curve) ifcopenshell.api.alignment.add_zero_length_segment(file, curve)
segment = layout.IsNestedBy[0].RelatedObjects[-1] segment = layout.IsNestedBy[0].RelatedObjects[-1]
alignment = ifcopenshell.api.alignment.get_alignment(layout) alignment = ifcopenshell.api.alignment.get_alignment(layout)
station = ifcopenshell.api.alignment.get_alignment_station(file,alignment) station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
name = f"{_get_segment_start_point_label(segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})" name = f"{_get_segment_start_point_label(segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
ifcopenshell.api.alignment.add_stationing_referent(file, segment, 0.0, station, name=name) ifcopenshell.api.alignment.add_stationing_referent(file, segment, 0.0, station, name=name)
@@ -37,7 +37,7 @@ def _map_line(file: ifcopenshell.file, design_parameters: entity_instance) -> Se
start_direction = design_parameters.StartDirection start_direction = design_parameters.StartDirection
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -80,7 +80,7 @@ def _map_circular_arc(file: ifcopenshell.file, design_parameters: entity_instanc
start_radius = design_parameters.StartRadiusOfCurvature start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -114,7 +114,7 @@ def _map_clothoid(file: ifcopenshell.file, design_parameters: entity_instance) -
end_radius = design_parameters.EndRadiusOfCurvature end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -156,7 +156,7 @@ def _map_cubic(file: ifcopenshell.file, design_parameters: entity_instance) -> S
end_radius = design_parameters.EndRadiusOfCurvature end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -209,7 +209,7 @@ def _map_helmert_curve(file: ifcopenshell.file, design_parameters: entity_instan
end_radius = design_parameters.EndRadiusOfCurvature end_radius = design_parameters.EndRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -315,7 +315,7 @@ def _map_bloss_curve(file: ifcopenshell.file, design_parameters: entity_instance
start_radius = design_parameters.StartRadiusOfCurvature start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -362,7 +362,7 @@ def _map_cosine_curve(file: ifcopenshell.file, design_parameters: entity_instanc
start_radius = design_parameters.StartRadiusOfCurvature start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -405,7 +405,7 @@ def _map_sine_curve(file: ifcopenshell.file, design_parameters: entity_instance)
start_radius = design_parameters.StartRadiusOfCurvature start_radius = design_parameters.StartRadiusOfCurvature
length = design_parameters.SegmentLength length = design_parameters.SegmentLength
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT') angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
start_direction *= angle_unit_scale start_direction *= angle_unit_scale
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
@@ -136,7 +136,7 @@ def _map_circular_arc(file: ifcopenshell.file, design_parameters: entity_instanc
start_height = design_parameters.StartHeight start_height = design_parameters.StartHeight
start_gradient = design_parameters.StartGradient start_gradient = design_parameters.StartGradient
end_gradient = design_parameters.EndGradient end_gradient = design_parameters.EndGradient
#radius = design_parameters.RadiusOfCurvature # radius = design_parameters.RadiusOfCurvature
transition = "DISCONTINUOUS" transition = "DISCONTINUOUS"
start_angle = math.atan(start_gradient) start_angle = math.atan(start_gradient)
@@ -30,7 +30,7 @@ from ifcopenshell.api.alignment._map_alignment_horizontal_segment import _map_al
from ifcopenshell.api.alignment._update_curve_segment_transition_code import _update_curve_segment_transition_code from ifcopenshell.api.alignment._update_curve_segment_transition_code import _update_curve_segment_transition_code
def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, include_referent : bool = True) -> bool: def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, include_referent: bool = True) -> bool:
""" """
Adds a zero length segment to the end of a layout. Adds a zero length segment to the end of a layout.
@@ -40,27 +40,33 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
:param include_referent: If True, an IfcReferent representing the ending point of the layout is included for IfcLinearElement layouts (i.e. business logic) :param include_referent: If True, an IfcReferent representing the ending point of the layout is included for IfcLinearElement layouts (i.e. business logic)
:return: True if segment is added :return: True if segment is added
""" """
# These are valid curve types for alignment, but don't have the zero-length segment # These are valid curve types for alignment, but don't have the zero-length segment
if layout.is_a("IfcOffsetCurveByDistances") or layout.is_a("IfcPolyline") or layout.is_a("IfcIndexedPolyCurve"): if layout.is_a("IfcOffsetCurveByDistances") or layout.is_a("IfcPolyline") or layout.is_a("IfcIndexedPolyCurve"):
return return
expected_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant","IfcCompositeCurve","IfcGradientCurve","IfcSegmentedReferenceCurve"] expected_types = [
"IfcAlignmentHorizontal",
"IfcAlignmentVertical",
"IfcAlignmentCant",
"IfcCompositeCurve",
"IfcGradientCurve",
"IfcSegmentedReferenceCurve",
]
if not layout.is_a() in expected_types: if not layout.is_a() in expected_types:
raise TypeError( raise TypeError(
f"Expected layout type to be one of {[_ for _ in expected_types]}, instead received {layout.is_a()}" f"Expected layout type to be one of {[_ for _ in expected_types]}, instead received {layout.is_a()}"
) )
if ifcopenshell.api.alignment.has_zero_length_segment(layout): if ifcopenshell.api.alignment.has_zero_length_segment(layout):
return False return False
if layout.is_a("IfcCompositeCurve") or layout.is_a("IfcGradientCurve") or layout.is_a("IfcSegmentedReferenceCurve"): if layout.is_a("IfcCompositeCurve") or layout.is_a("IfcGradientCurve") or layout.is_a("IfcSegmentedReferenceCurve"):
x = 0. x = 0.0
y = 0. y = 0.0
dx = 1. dx = 1.0
dy = 0. dy = 0.0
segment_start = 0. segment_start = 0.0
last_segment = None last_segment = None
if layout.Segments and 0 < len(layout.Segments): if layout.Segments and 0 < len(layout.Segments):
@@ -68,15 +74,15 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
# because this becomes of placement of the zero length segment # because this becomes of placement of the zero length segment
last_segment = layout.Segments[-1] last_segment = layout.Segments[-1]
settings = ifcopenshell.geom.settings() settings = ifcopenshell.geom.settings()
fn = wrapper.map_shape(settings,last_segment.wrapped_data) fn = wrapper.map_shape(settings, last_segment.wrapped_data)
eval = wrapper.function_item_evaluator(settings,fn) eval = wrapper.function_item_evaluator(settings, fn)
e = np.array(eval.evaluate(fn.end())) e = np.array(eval.evaluate(fn.end()))
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
e[:3,3] /= unit_scale e[:3, 3] /= unit_scale
x = float(e[0,3]) x = float(e[0, 3])
y = float(e[1,3]) y = float(e[1, 3])
dx = float(e[0,0]) dx = float(e[0, 0])
dy = float(e[1,0]) dy = float(e[1, 0])
parent_curve = file.createIfcLine( parent_curve = file.createIfcLine(
Pnt=file.createIfcCartesianPoint(Coordinates=((0.0, 0.0))), Pnt=file.createIfcCartesianPoint(Coordinates=((0.0, 0.0))),
@@ -99,21 +105,21 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
layout.Segments += (zero_length_curve_segment,) layout.Segments += (zero_length_curve_segment,)
if last_segment: if last_segment:
_update_curve_segment_transition_code(last_segment,zero_length_curve_segment) _update_curve_segment_transition_code(last_segment, zero_length_curve_segment)
# add zero length segments to base curves # add zero length segments to base curves
if layout.is_a("IfcSegmentedReferenceCurve"): if layout.is_a("IfcSegmentedReferenceCurve"):
ifcopenshell.api.alignment.add_zero_length_segment(file,layout.BaseCurve) ifcopenshell.api.alignment.add_zero_length_segment(file, layout.BaseCurve)
elif layout.is_a("IfcGradientCurve"): elif layout.is_a("IfcGradientCurve"):
ifcopenshell.api.alignment.add_zero_length_segment(file,layout.BaseCurve) ifcopenshell.api.alignment.add_zero_length_segment(file, layout.BaseCurve)
else: else:
zero_length_curve_segment = None zero_length_curve_segment = None
if layout.is_a("IfcAlignmentHorizontal"): if layout.is_a("IfcAlignmentHorizontal"):
x = 0. x = 0.0
y = 0. y = 0.0
dx = 1. dx = 1.0
dy = 0. dy = 0.0
last_segment = None last_segment = None
for rel in layout.IsNestedBy: for rel in layout.IsNestedBy:
if 0 < len(rel.RelatedObjects): if 0 < len(rel.RelatedObjects):
@@ -121,35 +127,34 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
break break
if last_segment: if last_segment:
file.begin_transaction() # use a transaction so we can discard any temporary IFC entities created file.begin_transaction() # use a transaction so we can discard any temporary IFC entities created
settings = ifcopenshell.geom.settings() settings = ifcopenshell.geom.settings()
mapped_segments = _map_alignment_horizontal_segment(file,last_segment) mapped_segments = _map_alignment_horizontal_segment(file, last_segment)
geometry_segment = mapped_segments[0] if mapped_segments[1] == None else mapped_segments[1] geometry_segment = mapped_segments[0] if mapped_segments[1] == None else mapped_segments[1]
fn = wrapper.map_shape(settings,geometry_segment.wrapped_data) fn = wrapper.map_shape(settings, geometry_segment.wrapped_data)
eval = wrapper.function_item_evaluator(settings,fn) eval = wrapper.function_item_evaluator(settings, fn)
e = np.array(eval.evaluate(fn.end())) e = np.array(eval.evaluate(fn.end()))
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
x = float(e[0,3]) / unit_scale x = float(e[0, 3]) / unit_scale
y = float(e[1,3]) / unit_scale y = float(e[1, 3]) / unit_scale
dx = float(e[0,0]) dx = float(e[0, 0])
dy = float(e[1,0]) dy = float(e[1, 0])
file.discard_transaction() file.discard_transaction()
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file, "PLANEANGLEUNIT")
angle_unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file,'PLANEANGLEUNIT')
design_parameters = file.createIfcAlignmentHorizontalSegment( design_parameters = file.createIfcAlignmentHorizontalSegment(
StartPoint=file.createIfcCartesianPoint( StartPoint=file.createIfcCartesianPoint((x, y)),
(x,y) StartDirection=math.atan2(dy, dx) / angle_unit_scale,
),
StartDirection=math.atan2(dy,dx) / angle_unit_scale,
StartRadiusOfCurvature=0.0, StartRadiusOfCurvature=0.0,
EndRadiusOfCurvature=0.0, EndRadiusOfCurvature=0.0,
SegmentLength=0.0, SegmentLength=0.0,
PredefinedType="LINE", PredefinedType="LINE",
) )
zero_length_curve_segment = file.createIfcAlignmentSegment(GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters) zero_length_curve_segment = file.createIfcAlignmentSegment(
GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters
)
elif layout.is_a("IfcAlignmentVertical"): elif layout.is_a("IfcAlignmentVertical"):
last_segment_dist_along = 0.0 last_segment_dist_along = 0.0
last_segment_end_gradient = 0.0 last_segment_end_gradient = 0.0
@@ -170,7 +175,9 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
EndGradient=last_segment_end_gradient, EndGradient=last_segment_end_gradient,
PredefinedType="CONSTANTGRADIENT", PredefinedType="CONSTANTGRADIENT",
) )
zero_length_curve_segment = file.createIfcAlignmentSegment(GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters) zero_length_curve_segment = file.createIfcAlignmentSegment(
GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters
)
elif layout.is_a("IfcAlignmentCant"): elif layout.is_a("IfcAlignmentCant"):
last_segment_dist_along = 0.0 last_segment_dist_along = 0.0
last_segment_cant_left = 0.0 last_segment_cant_left = 0.0
@@ -200,14 +207,16 @@ def add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance, in
StartCantRight=last_segment_cant_right, StartCantRight=last_segment_cant_right,
PredefinedType="CONSTANTCANT", PredefinedType="CONSTANTCANT",
) )
zero_length_curve_segment = file.createIfcAlignmentSegment(GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters) zero_length_curve_segment = file.createIfcAlignmentSegment(
GlobalId=ifcopenshell.guid.new(), DesignParameters=design_parameters
)
ifcopenshell.api.nest.assign_object(file, related_objects=[zero_length_curve_segment], relating_object=layout) ifcopenshell.api.nest.assign_object(file, related_objects=[zero_length_curve_segment], relating_object=layout)
if include_referent: if include_referent:
alignment = ifcopenshell.api.alignment.get_alignment(layout) alignment = ifcopenshell.api.alignment.get_alignment(layout)
station = ifcopenshell.api.alignment.get_alignment_station(file,alignment) station = ifcopenshell.api.alignment.get_alignment_station(file, alignment)
name = f"{_get_segment_start_point_label(zero_length_curve_segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})" name = f"{_get_segment_start_point_label(zero_length_curve_segment,None)} ({ifcopenshell.util.alignment.station_as_string(file,station)})"
ifcopenshell.api.alignment.add_stationing_referent(file, zero_length_curve_segment, 0.0, station, name=name) ifcopenshell.api.alignment.add_stationing_referent(file, zero_length_curve_segment, 0.0, station, name=name)
return True return True
@@ -54,9 +54,9 @@ def create_as_offset_curve(
_create_offset_curve_representation(file, alignment, offsets) _create_offset_curve_representation(file, alignment, offsets)
# define stationing # define stationing
#name = ifcopenshell.util.alignment.station_as_string(file, start_station) # name = ifcopenshell.util.alignment.station_as_string(file, start_station)
#referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, start_station, name) # referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, start_station, name)
#ifcopenshell.api.nest.reorder_nesting(file, referent, -1, 0) # ifcopenshell.api.nest.reorder_nesting(file, referent, -1, 0)
# IFC 4.1.4.1.1 Alignment Aggregation To Project # IFC 4.1.4.1.1 Alignment Aggregation To Project
project = file.by_type("IfcProject")[0] project = file.by_type("IfcProject")[0]
@@ -23,6 +23,7 @@ from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._create_geometric_representation import _create_geometric_representation from ifcopenshell.api.alignment._create_geometric_representation import _create_geometric_representation
from ifcopenshell.api.alignment._add_segment_to_curve import _add_segment_to_curve from ifcopenshell.api.alignment._add_segment_to_curve import _add_segment_to_curve
def create_representation( def create_representation(
file: ifcopenshell.file, file: ifcopenshell.file,
alignment: entity_instance, alignment: entity_instance,
@@ -43,11 +44,11 @@ def create_representation(
if alignment.Representation: if alignment.Representation:
return return
_create_geometric_representation(file,alignment) _create_geometric_representation(file, alignment)
layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment) layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment)
for layout in layouts: for layout in layouts:
curve = ifcopenshell.api.alignment.get_layout_curve(layout) curve = ifcopenshell.api.alignment.get_layout_curve(layout)
for segment in layout.IsNestedBy[0].RelatedObjects: for segment in layout.IsNestedBy[0].RelatedObjects:
_add_segment_to_curve(file, segment, curve) _add_segment_to_curve(file, segment, curve)
@@ -36,7 +36,7 @@ def get_alignment_station(file: ifcopenshell.file, alignment: entity_instance) -
parent_alignment = ifcopenshell.api.alignment.get_parent_alignment(alignment) parent_alignment = ifcopenshell.api.alignment.get_parent_alignment(alignment)
if parent_alignment: if parent_alignment:
start_station = ifcopenshell.api.alignment.get_alignment_station(file,parent_alignment) start_station = ifcopenshell.api.alignment.get_alignment_station(file, parent_alignment)
else: else:
components = ifcopenshell.util.element.get_components(alignment) components = ifcopenshell.util.element.get_components(alignment)
for c in components: for c in components:
@@ -44,5 +44,4 @@ def get_alignment_station(file: ifcopenshell.file, alignment: entity_instance) -
start_station = ifcopenshell.util.element.get_pset(c, name="Pset_Stationing", prop="Station") start_station = ifcopenshell.util.element.get_pset(c, name="Pset_Stationing", prop="Station")
break break
return start_station return start_station
@@ -29,7 +29,14 @@ def has_zero_length_segment(layout: entity_instance) -> bool:
:param layout: An IfcAlignmentHorizontal, IfcAlignmentVertical, IfcAlignmentCant, IfcCompositeCurve, IfcGradientCurve, or IfcSegmentedReferenceCurve :param layout: An IfcAlignmentHorizontal, IfcAlignmentVertical, IfcAlignmentCant, IfcCompositeCurve, IfcGradientCurve, or IfcSegmentedReferenceCurve
:return: True if the zero length segment is present :return: True if the zero length segment is present
""" """
expected_types = ["IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant","IfcCompositeCurve","IfcGradientCurve","IfcSegmentedReferenceCurve"] expected_types = [
"IfcAlignmentHorizontal",
"IfcAlignmentVertical",
"IfcAlignmentCant",
"IfcCompositeCurve",
"IfcGradientCurve",
"IfcSegmentedReferenceCurve",
]
if not layout.is_a() in expected_types: if not layout.is_a() in expected_types:
raise TypeError( raise TypeError(
f"Expected entity type to be one of {[_ for _ in expected_types]}, instead received '{layout.is_a()}" f"Expected entity type to be one of {[_ for _ in expected_types]}, instead received '{layout.is_a()}"
@@ -38,7 +45,11 @@ def has_zero_length_segment(layout: entity_instance) -> bool:
result = False result = False
if layout.is_a("IfcCompositeCurve") or layout.is_a("IfcGradientCurve") or layout.is_a("IfcSegmentedReferenceCurve"): if layout.is_a("IfcCompositeCurve") or layout.is_a("IfcGradientCurve") or layout.is_a("IfcSegmentedReferenceCurve"):
result = True if layout.Segments and 0 < len(layout.Segments) and layout.Segments[-1].SegmentLength.wrappedValue == 0.0 else False result = (
True
if layout.Segments and 0 < len(layout.Segments) and layout.Segments[-1].SegmentLength.wrappedValue == 0.0
else False
)
else: else:
for rel in layout.IsNestedBy: for rel in layout.IsNestedBy:
if 0 < len(rel.RelatedObjects): if 0 < len(rel.RelatedObjects):
@@ -33,9 +33,7 @@ def update_fallback_position(file: ifcopenshell.file, lp: entity_instance):
""" """
if not lp.CartesianPosition: if not lp.CartesianPosition:
lp.CartesianPosition = file.createIfcAxis2Placement3D( lp.CartesianPosition = file.createIfcAxis2Placement3D(Location=file.createIfcCartesianPoint((0.0, 0.0)))
Location = file.createIfcCartesianPoint((0.,0.))
)
p = np.array(ifcopenshell.util.placement.get_axis2placement(lp.RelativePlacement)) p = np.array(ifcopenshell.util.placement.get_axis2placement(lp.RelativePlacement))
@@ -51,13 +49,13 @@ def update_fallback_position(file: ifcopenshell.file, lp: entity_instance):
ay = float(p[1, 2]) ay = float(p[1, 2])
az = float(p[2, 2]) az = float(p[2, 2])
lp.CartesianPosition.Location.Coordinates = ((x,y,z)) lp.CartesianPosition.Location.Coordinates = (x, y, z)
if not lp.CartesianPosition.RefDirection: if not lp.CartesianPosition.RefDirection:
lp.CartesianPosition.RefDirection = file.createIfcDirection((1.,0.,0.)) lp.CartesianPosition.RefDirection = file.createIfcDirection((1.0, 0.0, 0.0))
if not lp.CartesianPosition.Axis: if not lp.CartesianPosition.Axis:
lp.CartesianPosition.Axis = file.createIfcDirection((0.,0.,1.)) lp.CartesianPosition.Axis = file.createIfcDirection((0.0, 0.0, 1.0))
lp.CartesianPosition.RefDirection.DirectionRatios = ((rx,ry,rz)) lp.CartesianPosition.RefDirection.DirectionRatios = (rx, ry, rz)
lp.CartesianPosition.Axis.DirectionRatios = ((ax,ay,az)) lp.CartesianPosition.Axis.DirectionRatios = (ax, ay, az)
@@ -20,43 +20,47 @@ import math
import ifcopenshell import ifcopenshell
import ifcopenshell.util.unit import ifcopenshell.util.unit
def add_linear_placement_fallback_position(file:ifcopenshell.file)->ifcopenshell.file:
def add_linear_placement_fallback_position(file: ifcopenshell.file) -> ifcopenshell.file:
import ifcopenshell.api.alignment import ifcopenshell.api.alignment
patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string()) patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string())
linear_placements = patched_file.by_type("IfcLinearPlacement") linear_placements = patched_file.by_type("IfcLinearPlacement")
for lp in linear_placements: for lp in linear_placements:
ifcopenshell.api.alignment.update_fallback_position(patched_file,lp) ifcopenshell.api.alignment.update_fallback_position(patched_file, lp)
return patched_file return patched_file
def create_alignment_geometry(file:ifcopenshell.file)->ifcopenshell.file: def create_alignment_geometry(file: ifcopenshell.file) -> ifcopenshell.file:
import ifcopenshell.api.alignment import ifcopenshell.api.alignment
patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string()) patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string())
alignments = patched_file.by_type("IfcAlignment") alignments = patched_file.by_type("IfcAlignment")
for alignment in alignments: for alignment in alignments:
ifcopenshell.api.alignment.create_representation(patched_file,alignment) ifcopenshell.api.alignment.create_representation(patched_file, alignment)
return patched_file return patched_file
def append_zero_length_segments(file:ifcopenshell.file)->ifcopenshell.file:
def append_zero_length_segments(file: ifcopenshell.file) -> ifcopenshell.file:
"""Appends zero length segments to all alignment layouts and layout geometry, if missing.""" """Appends zero length segments to all alignment layouts and layout geometry, if missing."""
import ifcopenshell.api.alignment import ifcopenshell.api.alignment
patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string()) patched_file = ifcopenshell.file.from_string(file.wrapped_data.to_string())
alignments = patched_file.by_type("IfcAlignment") alignments = patched_file.by_type("IfcAlignment")
for alignment in alignments: for alignment in alignments:
layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment) layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment)
for layout in layouts: for layout in layouts:
ifcopenshell.api.alignment.add_zero_length_segment(patched_file,layout,include_referent=False) ifcopenshell.api.alignment.add_zero_length_segment(patched_file, layout, include_referent=False)
curve = ifcopenshell.api.alignment.get_layout_curve(layout) curve = ifcopenshell.api.alignment.get_layout_curve(layout)
if curve: if curve:
ifcopenshell.api.alignment.add_zero_length_segment(patched_file,curve) ifcopenshell.api.alignment.add_zero_length_segment(patched_file, curve)
return patched_file
return patched_file
def station_as_string(file: ifcopenshell.file, sta: float): def station_as_string(file: ifcopenshell.file, sta: float):
@@ -76,4 +76,5 @@ def test_add_segment_to_layout():
alignment_segment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent") alignment_segment.IsNestedBy[0].RelatedObjects[0].is_a("IfcReferent")
) # a referent is automatically added at the start of the segment ) # a referent is automatically added at the start of the segment
test_add_segment_to_layout()
test_add_segment_to_layout()
@@ -82,4 +82,5 @@ def test_create():
for curve in curves: for curve in curves:
assert ifcopenshell.api.alignment.has_zero_length_segment(curve) assert ifcopenshell.api.alignment.has_zero_length_segment(curve)
test_create()
test_create()
@@ -45,24 +45,25 @@ def test_update_fallback_position():
basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment) basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment)
pde = file.createIfcPointByDistanceExpression( pde = file.createIfcPointByDistanceExpression(
DistanceAlong = file.createIfcLengthMeasure(4500.), DistanceAlong=file.createIfcLengthMeasure(4500.0), OffsetLateral=20.0, BasisCurve=basis_curve
OffsetLateral = 20.0,
BasisCurve = basis_curve
) )
placement = file.createIfcAxis2PlacementLinear( placement = file.createIfcAxis2PlacementLinear(
Location = pde, Location=pde,
) )
lp = file.createIfcLinearPlacement(RelativePlacement = placement) lp = file.createIfcLinearPlacement(RelativePlacement=placement)
assert lp.CartesianPosition == None assert lp.CartesianPosition == None
ifcopenshell.api.alignment.update_fallback_position(file,lp) ifcopenshell.api.alignment.update_fallback_position(file, lp)
assert lp.CartesianPosition != None assert lp.CartesianPosition != None
assert lp.CartesianPosition.Location.Coordinates == pytest.approx((3781.0625905626425,2663.2859940077856,0.0)) assert lp.CartesianPosition.Location.Coordinates == pytest.approx((3781.0625905626425, 2663.2859940077856, 0.0))
assert lp.CartesianPosition.RefDirection.DirectionRatios == pytest.approx((0.22453152656315067, 0.9744668252840736, 0.0)) assert lp.CartesianPosition.RefDirection.DirectionRatios == pytest.approx(
(0.22453152656315067, 0.9744668252840736, 0.0)
)
assert lp.CartesianPosition.Axis.DirectionRatios == pytest.approx((0.0, 0.0, 1.0)) assert lp.CartesianPosition.Axis.DirectionRatios == pytest.approx((0.0, 0.0, 1.0))
test_update_fallback_position() test_update_fallback_position()
@@ -24,12 +24,9 @@ from logging import Logger
import typing import typing
from typing import Union from typing import Union
class Patcher(ifcpatch.BasePatcher): class Patcher(ifcpatch.BasePatcher):
def __init__( def __init__(self, file: ifcopenshell.file, logger: Union[Logger, None] = None):
self,
file: ifcopenshell.file,
logger: Union[Logger, None] = None
):
"""Adds the geometric representation to a layout """Adds the geometric representation to a layout
Example: Example:
@@ -24,12 +24,9 @@ from logging import Logger
import typing import typing
from typing import Union from typing import Union
class Patcher(ifcpatch.BasePatcher): class Patcher(ifcpatch.BasePatcher):
def __init__( def __init__(self, file: ifcopenshell.file, logger: Union[Logger, None] = None):
self,
file: ifcopenshell.file,
logger: Union[Logger, None] = None
):
"""Adds the IfcLinearPlacement.CartesianPosition fallback position to all of the IfcLinearPlacement objects in the file """Adds the IfcLinearPlacement.CartesianPosition fallback position to all of the IfcLinearPlacement objects in the file
Example: Example:
@@ -24,12 +24,9 @@ from logging import Logger
import typing import typing
from typing import Union from typing import Union
class Patcher(ifcpatch.BasePatcher): class Patcher(ifcpatch.BasePatcher):
def __init__( def __init__(self, file: ifcopenshell.file, logger: Union[Logger, None] = None):
self,
file: ifcopenshell.file,
logger: Union[Logger, None] = None
):
"""Adds a zero length segments to alignment layouts """Adds a zero length segments to alignment layouts
Example: Example: