mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-13 06:53:36 +00:00
feat: Update the patch UI of ResetAbsoluteCoordinates
Instead of specifying parameters `a`, `b`, `c` and `d`, more meaningful parameters have been introduced (`threshold`, `automatic_offset_point`, `x`, `y` and `z`), which allow to do the same operations in a more understandable way.
This commit is contained in:
committed by
Dion Moult
parent
c31c34b7d2
commit
1abc3a1422
@@ -30,14 +30,15 @@ class Patcher:
|
|||||||
file: ifcopenshell.file,
|
file: ifcopenshell.file,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
mode: Literal[
|
mode: Literal[
|
||||||
"geometry",
|
"Geometry",
|
||||||
"placement",
|
"Placement",
|
||||||
"both",
|
"Both",
|
||||||
] = "geometry",
|
] = "Geometry",
|
||||||
a: Optional[float] = None,
|
automatic_offset_point: bool = True,
|
||||||
b: Optional[float] = None,
|
threshold: float = 1000000, # Arbitrary default threshold based on experience
|
||||||
c: Optional[float] = None,
|
x: Union[str, float] = "0",
|
||||||
d: Optional[float] = None,
|
y: Union[str, float] = "0",
|
||||||
|
z: Union[str, float] = "0",
|
||||||
):
|
):
|
||||||
"""Reset any large coordinates to smaller coordinates based on a threshold
|
"""Reset any large coordinates to smaller coordinates based on a threshold
|
||||||
|
|
||||||
@@ -53,47 +54,34 @@ class Patcher:
|
|||||||
ordinate is larger than a threshold) and offsets it back down to a small
|
ordinate is larger than a threshold) and offsets it back down to a small
|
||||||
number.
|
number.
|
||||||
|
|
||||||
You may either manually specify the offset to apply to any large
|
You may either let Blender BIM determine the offset to apply to any large
|
||||||
coordinate, or an offset will be automatically determined arbitrarily
|
coordinate automatically, arbitrarily based on the first large number we
|
||||||
based on the first large number we encounter.
|
encounter, or manually specify this offset.
|
||||||
|
|
||||||
Note that if your model inconsistently mixes coordinates between large
|
Note that if your model inconsistently mixes coordinates between large
|
||||||
and small (such as if your model mixes both local and map coordinates)
|
and small (such as if your model mixes both local and map coordinates)
|
||||||
then the results of this function may be poor. Provide a bug report back
|
then the results of this function may be poor. Provide a bug report back
|
||||||
to your BIM application to get it fixed.
|
to your BIM application to get it fixed.
|
||||||
|
|
||||||
You may specify up to 4 arguments, a, b, c, and d.
|
:param mode: Choose from "Geometry", "Placement", or "Both". Choosing
|
||||||
|
"Geometry" will only replace cartesian points used in shape
|
||||||
If you specify no arguments, then the threshold is set to 1000000. The
|
representations. Choosing "Placement" will only replace cartesian
|
||||||
offset is auto detected.
|
points used in object placements. Choosing "Both" will replace all
|
||||||
|
|
||||||
If you only specify 1 parameter (i.e. a), then this is treated as the
|
|
||||||
threshold beyond which an ordinate is considered to be large. The offset
|
|
||||||
is auto detected.
|
|
||||||
|
|
||||||
If you specify 3 parameters, (i.e. a, b, c) then your three numbers are
|
|
||||||
treated as the X, Y, Z offset to apply. Typically your numbers will be
|
|
||||||
negative to bring the numbers smaller. The threshold is set to 1000000.
|
|
||||||
|
|
||||||
If you specify 4 parameters (i.e. a, b, c, d), then the first three
|
|
||||||
numbers are treated as the X, Y, Z offset to apply (a, b, c). The fourth
|
|
||||||
(d) will be treated as the threshold.
|
|
||||||
|
|
||||||
:param mode: Choose from "geometry", "placement", or "both". Choosing
|
|
||||||
"geometry" will only replace cartesian points used in shape
|
|
||||||
representations. Choosing "placement" will only replace cartesian
|
|
||||||
points used in object placements. Choosing "both" will replace all
|
|
||||||
cartesian points regardless of use (useful if the model has both
|
cartesian points regardless of use (useful if the model has both
|
||||||
large placement offsets and large geometry offsets).
|
large placement offsets and large geometry offsets).
|
||||||
:type mode: str
|
:type mode: str
|
||||||
:param a: The first parameter
|
:param automatic_offset_point: Choose, whether the offset should be
|
||||||
:type a: float,optional
|
determined automatically or specified manually.
|
||||||
:param b: The second parameter
|
:type automatic_offset_point: bool
|
||||||
:type b: float,optional
|
:param threshold: The threshold for deciding, whether a coordinate is
|
||||||
:param c: The third parameter
|
treated as a large coordinate.
|
||||||
:type c: float,optional
|
:type threshold: float
|
||||||
:param d: The fourth parameter
|
:param x: The x-ordinate of the manually specified offset point.
|
||||||
:type d: float,optional
|
:type x: Union[str, float]
|
||||||
|
:param y: The y-ordinate of the manually specified offset point.
|
||||||
|
:type y: Union[str, float]
|
||||||
|
:param z: The z-ordinate of the manually specified offset point.
|
||||||
|
:type z: Union[str, float]
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -103,39 +91,33 @@ class Patcher:
|
|||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": []})
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": []})
|
||||||
|
|
||||||
# Reset all coordinates with an ordinate larger than 1000 arbitrarily
|
# Reset all coordinates with an ordinate larger than 1000 arbitrarily
|
||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [1000]})
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [True, 1000]})
|
||||||
|
|
||||||
# Reset all coordinates with an ordinate larger than 1000000 by -50000,-20000,0
|
# Reset all coordinates with an ordinate larger than 1000000 by -50000,-20000,0
|
||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [-50000,-20000,0]})
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [False, 1000000, -50000,-20000,0]})
|
||||||
|
|
||||||
# Reset all coordinates with an ordinate larger than 1000 by -500,-200,0
|
# Reset all coordinates with an ordinate larger than 1000 by -500,-200,0
|
||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [-500,-200,0,1000]})
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [False, 1000, -500,-200,0]})
|
||||||
"""
|
"""
|
||||||
self.src = src
|
self.src = src
|
||||||
self.file = file
|
self.file = file
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.mode = mode
|
self.mode = mode.lower()
|
||||||
self.args = [x for x in [a, b, c, d] if x is not None]
|
self.threshold = threshold
|
||||||
|
if automatic_offset_point:
|
||||||
|
self.offset_point = None
|
||||||
|
else:
|
||||||
|
self.offset_point = (float(x), float(y), float(z))
|
||||||
|
|
||||||
def patch(self):
|
def patch(self):
|
||||||
placement_coord_ids = set()
|
placement_coord_ids = set()
|
||||||
for placement in self.file.by_type("IfcObjectPlacement"):
|
for placement in self.file.by_type("IfcObjectPlacement"):
|
||||||
[placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a("IfcCartesianPoint")]
|
[placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a("IfcCartesianPoint")]
|
||||||
|
|
||||||
# Arbitrary threshold based on experience
|
|
||||||
self.threshold = 1000000
|
|
||||||
if self.args and len(self.args) == 1:
|
|
||||||
self.threshold = float(self.args[0])
|
|
||||||
elif self.args and len(self.args) == 4:
|
|
||||||
self.threshold = float(self.args[3])
|
|
||||||
|
|
||||||
# This method will not work all the time, but will catch most issues. It
|
# This method will not work all the time, but will catch most issues. It
|
||||||
# assumes that absolute coordinates are easily recognisable based on
|
# assumes that absolute coordinates are easily recognisable based on
|
||||||
# having a large absolute value above a threshold. This is not always
|
# having a large absolute value above a threshold. This is not always
|
||||||
# the case, but is very fast to run, and works for most cases.
|
# the case, but is very fast to run, and works for most cases.
|
||||||
offset_point = None
|
|
||||||
if self.args and len(self.args) >= 3:
|
|
||||||
offset_point = (float(self.args[0]), float(self.args[1]), float(self.args[2]))
|
|
||||||
try:
|
try:
|
||||||
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
||||||
except:
|
except:
|
||||||
@@ -147,10 +129,14 @@ class Patcher:
|
|||||||
if len(point) == 2 or not self.is_point_far_away(point):
|
if len(point) == 2 or not self.is_point_far_away(point):
|
||||||
coord_list[i] = point
|
coord_list[i] = point
|
||||||
continue
|
continue
|
||||||
if not offset_point:
|
if not self.offset_point:
|
||||||
offset_point = (-point[0], -point[1], -point[2])
|
self.offset_point = (-point[0], -point[1], -point[2])
|
||||||
self.logger.info(f"Resetting absolute coordinates by {point}")
|
self.logger.info(f"Resetting absolute coordinates by {point}")
|
||||||
point = (point[0] + offset_point[0], point[1] + offset_point[1], point[2] + offset_point[2])
|
point = (
|
||||||
|
point[0] + self.offset_point[0],
|
||||||
|
point[1] + self.offset_point[1],
|
||||||
|
point[2] + self.offset_point[2],
|
||||||
|
)
|
||||||
coord_list[i] = point
|
coord_list[i] = point
|
||||||
point_list.CoordList = coord_list
|
point_list.CoordList = coord_list
|
||||||
for point in self.file.by_type("IfcCartesianPoint"):
|
for point in self.file.by_type("IfcCartesianPoint"):
|
||||||
@@ -162,13 +148,13 @@ class Patcher:
|
|||||||
elif self.mode == "placement":
|
elif self.mode == "placement":
|
||||||
if point.id() not in placement_coord_ids:
|
if point.id() not in placement_coord_ids:
|
||||||
continue
|
continue
|
||||||
if not offset_point:
|
if not self.offset_point:
|
||||||
offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2])
|
self.offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2])
|
||||||
self.logger.info(f"Resetting absolute coordinates by {point}")
|
self.logger.info(f"Resetting absolute coordinates by {point}")
|
||||||
point.Coordinates = (
|
point.Coordinates = (
|
||||||
point.Coordinates[0] + offset_point[0],
|
point.Coordinates[0] + self.offset_point[0],
|
||||||
point.Coordinates[1] + offset_point[1],
|
point.Coordinates[1] + self.offset_point[1],
|
||||||
point.Coordinates[2] + offset_point[2],
|
point.Coordinates[2] + self.offset_point[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_point_far_away(self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]]) -> bool:
|
def is_point_far_away(self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]]) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user