bluemira.geometry.tools ======================= .. py:module:: bluemira.geometry.tools .. autoapi-nested-parse:: Useful functions for bluemira geometries. Classes ------- .. autoapisummary:: bluemira.geometry.tools.HullPlane bluemira.geometry.tools.BluemiraGeoEncoder bluemira.geometry.tools.GeometryCreationIn bluemira.geometry.tools.GeometryCreationOut bluemira.geometry.tools.SweepShapeTransition bluemira.geometry.tools.CutLocation Functions --------- .. autoapisummary:: bluemira.geometry.tools.convert bluemira.geometry.tools._reconstruct_function_call bluemira.geometry.tools._make_debug_file bluemira.geometry.tools.log_geometry_on_failure bluemira.geometry.tools.fallback_to bluemira.geometry.tools._make_vertex bluemira.geometry.tools.closed_wire_wrapper bluemira.geometry.tools.make_polygon bluemira.geometry.tools.make_bezier bluemira.geometry.tools.make_bspline bluemira.geometry.tools.make_bsplinesurface bluemira.geometry.tools._make_polygon_fallback bluemira.geometry.tools.interpolate_bspline bluemira.geometry.tools.force_wire_to_spline bluemira.geometry.tools.make_circle bluemira.geometry.tools.make_circle_arc_3P bluemira.geometry.tools.make_ellipse bluemira.geometry.tools.wire_closure bluemira.geometry.tools._offset_wire_discretised bluemira.geometry.tools.offset_wire bluemira.geometry.tools.convex_hull_wires_2d bluemira.geometry.tools.polygon_revolve_signed_volume bluemira.geometry.tools.partial_diff_of_volume bluemira.geometry.tools.revolve_shape bluemira.geometry.tools.extrude_shape bluemira.geometry.tools.sweep_shape bluemira.geometry.tools.loft_shape bluemira.geometry.tools.fillet_chamfer_decorator bluemira.geometry.tools.fillet_wire_2D bluemira.geometry.tools.chamfer_wire_2D bluemira.geometry.tools.distance_to bluemira.geometry.tools.split_wire bluemira.geometry.tools.cut_wire_at_z_value bluemira.geometry.tools.slice_shape bluemira.geometry.tools.get_wire_plane_intersect bluemira.geometry.tools.circular_pattern bluemira.geometry.tools.mirror_shape bluemira.geometry.tools.is_convex bluemira.geometry.tools.save_as_STP bluemira.geometry.tools.save_cad bluemira.geometry.tools.import_cad bluemira.geometry.tools._nb_dot_2D bluemira.geometry.tools._nb_clip bluemira.geometry.tools._signed_distance_2D bluemira.geometry.tools.signed_distance_2D_polygon bluemira.geometry.tools.signed_distance bluemira.geometry.tools.raise_error_if_overlap bluemira.geometry.tools.connect_shapes bluemira.geometry.tools.make_compound bluemira.geometry.tools.boolean_fuse bluemira.geometry.tools.boolean_cut bluemira.geometry.tools.boolean_fragments bluemira.geometry.tools.point_inside_shape bluemira.geometry.tools.point_on_plane bluemira.geometry.tools.serialise_shape bluemira.geometry.tools.deserialise_shape bluemira.geometry.tools.get_shape_by_name bluemira.geometry.tools.find_clockwise_angle_2d Module Contents --------------- .. py:function:: convert(apiobj: bluemira.codes._freecadapi.apiShape, label: str = '') -> bluemira.geometry.base.BluemiraGeoT Convert a FreeCAD shape into the corresponding BluemiraGeo object. :returns: BluemiraGeo object of inputted FreeCAD shape. :raises TypeError: Cannot convert to BluemiraGeo .. py:class:: HullPlane(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.HullPlane :parts: 1 :private-bases: Enumeration of planes to perform a hull operation in. .. py:attribute:: XZ .. py:attribute:: XY .. py:attribute:: YZ .. py:method:: _missing_(value: str) :classmethod: .. py:class:: BluemiraGeoEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None) Bases: :py:obj:`json.JSONEncoder` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.BluemiraGeoEncoder :parts: 1 :private-bases: JSON Encoder for BluemiraGeo. .. py:method:: default(obj: bluemira.geometry.base.BluemiraGeoT | numpy.ndarray | Any) Override the JSONEncoder default object handling behaviour for BluemiraGeo. :returns: List from bluemira shape or numpy array. .. py:function:: _reconstruct_function_call(signature, *args, **kwargs) -> dict Reconstruct the call of a function with inputs arguments and defaults. :returns: Dictionary of data from a function with inputs arguments and defaults. .. py:function:: _make_debug_file(name: str) -> pathlib.Path Make a new file in the geometry debugging folder. :returns: Filename for new file in the geometry debugging folder. .. py:function:: log_geometry_on_failure(func) Decorator for debugging of failed geometry operations. :returns: Decorator for debugging of failed geometry operations. .. py:function:: fallback_to(fallback_func, exception) Decorator for a fallback to an alternative geometry operation. :returns: Decorator for fallback function. .. py:function:: _make_vertex(point: numpy.typing.ArrayLike | bluemira.geometry.coordinates.Coordinates) -> bluemira.codes._freecadapi.apiVertex Make a vertex. :param point: Coordinates of the point :rtype: Vertex at the point :raises GeometryError: Shape must a single point .. py:class:: GeometryCreationIn Bases: :py:obj:`Protocol` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.GeometryCreationIn :parts: 1 :private-bases: Typing for closed_wire_wrapper input .. py:method:: __call__(points: bluemira.geometry.coordinates.Coordinates, label: str = '', *, closed: bool = False) -> bluemira.geometry.wire.BluemiraWire Typing for coordinates wrapping .. py:class:: GeometryCreationOut Bases: :py:obj:`Protocol` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.GeometryCreationOut :parts: 1 :private-bases: Typing for closed_wire_wrapper output .. py:method:: __call__(points: bluemira.geometry.coordinates.Coordinates | numpy.typing.ArrayLike | dict[str, numpy.typing.ArrayLike], label: str = '', *, closed: bool = False) -> bluemira.geometry.wire.BluemiraWire Typing for coordinates wrapping .. py:function:: closed_wire_wrapper(*, drop_closure_point: bool) -> collections.abc.Callable[[GeometryCreationIn], GeometryCreationOut] Decorator for checking / enforcing closures on wire creation functions. :returns: Decorator on wire creation functions. .. py:function:: make_polygon(points: bluemira.geometry.coordinates.Coordinates, label: str = '', *, closed: bool = False) -> bluemira.geometry.wire.BluemiraWire Make a polygon from a set of points. :param points: list of points. It can be given as a list of 3D tuples, a 3D numpy array, or similar. :param label: Object's label :param closed: if True, the first and last points will be connected in order to form a closed polygon. Defaults to False. :rtype: BluemiraWire of the polygon .. rubric:: Notes If the input points are closed, but closed is False, the returned BluemiraWire will be closed. .. py:function:: make_bezier(points: bluemira.geometry.coordinates.Coordinates, label: str = '', *, closed: bool = False) -> bluemira.geometry.wire.BluemiraWire Make a bspline from a set of points. :param points: list of points. It can be given as a list of 3D tuples, a 3D numpy array, or similar. :param label: Object's label :param closed: if True, the first and last points will be connected in order to form a closed bspline. Defaults to False. :rtype: BluemiraWire that contains the bspline .. rubric:: Notes If the input points are closed, but closed is False, the returned BluemiraWire will be closed. Bezier curve of degree n by the control points `P_i` is: .. math:: {\textbf{C}}(t) = \sum_{i=0}^{n} B_{i,n}(t) \cdot {\textbf{P}}_i where :math:`B_{i,n}(t)` are the Bernstein polynomials: .. math:: B_{i,n}(t) = \binom{n}{i} (1 - t)^{n-i} t^i t is a parameter ranging from 0 to 1. .. py:function:: make_bspline(poles: numpy.typing.ArrayLike, mults: numpy.typing.ArrayLike, knots: numpy.typing.ArrayLike, *, periodic: bool, degree: int, weights: numpy.typing.ArrayLike, check_rational: bool, label: str = '') -> bluemira.geometry.wire.BluemiraWire Builds a B-Spline by a lists of Poles, Mults, Knots :param poles: list of poles. :param mults: list of integers for the multiplicity :param knots: list of knots :param periodic: Whether or not the spline is periodic (same curvature at start and end points) :param degree: bspline degree :type degree: int :param weights: sequence of float :param check_rational: Whether or not to check if the BSpline is rational :rtype: BluemiraWire of the spline .. rubric:: Notes The B-spline curve is defined by the control points `P_i`: .. math:: \textbf{C}(t) = \sum_{i=0}^{n} B_{i,p}(t) {\textbf{P}}_i, where the basis functions :math:`B_{i,p}(t)` are computed as: .. math:: B_{i,0}(t) = \begin{cases} 1 & \text{if } t_i \leq t < t_{i+1}, \\ 0 & \text{otherwise}. \end{cases} .. math:: B_{i,p}(t) = \frac{t - t_i}{t_{i+p} - t_i} B_{i,p-1}(t) + \frac{t_{i+p+1} - t}{t_{i+p+1} - t_{i+1}} B_{i+1,p-1}(t). Here, t is a parameter ranging from 0 to 1, representing the position along the curve. .. py:function:: make_bsplinesurface(poles: numpy.typing.ArrayLike, mults_u: numpy.typing.ArrayLike, mults_v: numpy.typing.ArrayLike, knot_vector_u: numpy.typing.ArrayLike, knot_vector_v: numpy.typing.ArrayLike, degree_u: int, degree_v: int, weights: numpy.typing.ArrayLike, *, periodic: bool, check_rational: bool, label: str = '') -> bluemira.geometry.wire.BluemiraWire Builds a B-SplineSurface by a lists of Poles, Mults, Knots :param poles: Array of poles (control points). :param mults_u: list of integers for the u-multiplicity :param mults_v: list of integers for the u-multiplicity :param knot_vector_u: list of u-knots :param knot_vector_v: list of v-knots :param degree_u: degree of NURBS in u-direction :param degree_v: degree of NURBS in v-direction :param weights: point weights. :param periodic: Whether or not the spline is periodic (same curvature at start and end points) :param check_rational: Whether or not to check if the BSpline is rational (not sure) :rtype: A FreeCAD object that contours the bsplinesurface .. rubric:: Notes This function wraps the FreeCAD function of bsplinesurface buildFromPolesMultsKnots. The B-spline surface is defined as: .. math:: {\textbf{S}}(u, v) = \sum_{i=0}^{n} \sum_{j=0}^{m} N_{i,p}(u) \\ M_{j,q}(v) {\textbf{P}}_{i,j} where :math:`N_{i,p}(u)` and :math:`M_{j,q}(v)` are the B-spline basis functions in the u and v dimensions, respectively. .. py:function:: _make_polygon_fallback(points, label='', *, closed=False, **kwargs) -> bluemira.geometry.wire.BluemiraWire Overloaded function signature for fallback option from interpolate_bspline :returns: Closed BluemiraWire from points. .. py:function:: interpolate_bspline(points: numpy.typing.ArrayLike, label: str = '', *, closed: bool = False, start_tangent: collections.abc.Iterable | None = None, end_tangent: collections.abc.Iterable | None = None, allow_fallback: bool = True) -> bluemira.geometry.wire.BluemiraWire Make a bspline from a set of points. :param points: list of points. It can be given as a list of 3D tuples, a 3D numpy array, or similar. :param label: Object's label :param closed: if True, the first and last points will be connected in order to form a closed bspline. Defaults to False. :param start_tangent: Tangency of the BSpline at the first pole. Must be specified with end_tangent :param end_tangent: Tangency of the BSpline at the last pole. Must be specified with start_tangent :rtype: Bluemira wire that contains the bspline .. py:function:: force_wire_to_spline(wire: bluemira.geometry.wire.BluemiraWire, n_edges_max: int = 200, l2_tolerance: float = 0.005) -> bluemira.geometry.wire.BluemiraWire Force a wire to be a spline wire. :param wire: The BluemiraWire to be turned into a splined wire :param n_edges_max: The maximum number of edges in the wire, below which this operation does nothing :param l2_tolerance: The L2-norm difference w.r.t. the original wire, above which this operation will warn that the desired tolerance was not achieved. :rtype: A new spline version of the wire .. rubric:: Notes This is intended for use with wires that consist of large polygons, often resulting from operations that failed with primitives and fallback methods making use of of polygons. This can be relatively stubborn to transform back to splines. .. py:function:: make_circle(radius: float = 1.0, center: numpy.typing.ArrayLike = (0.0, 0.0, 0.0), start_angle: float = 0.0, end_angle: float = 360.0, axis: numpy.typing.ArrayLike = (0.0, 0.0, 1.0), label: str = '') -> bluemira.geometry.wire.BluemiraWire Create a circle or arc of circle object with given parameters. :param radius: Radius of the circle :param center: Center of the circle :param start_angle: Start angle of the arc [degrees] :param end_angle: End angle of the arc [degrees]. If start_angle == end_angle, a circle is created, otherwise a circle arc is created :param axis: Normal vector to the circle plane. It defines the clockwise/anticlockwise circle orientation according to the right hand rule. :param label: object's label :rtype: Bluemira wire that contains the arc or circle .. py:function:: make_circle_arc_3P(p1: collections.abc.Iterable[float], p2: collections.abc.Iterable[float], p3: collections.abc.Iterable[float], axis: tuple[float, float, float] | None = None, label: str = '') -> bluemira.geometry.wire.BluemiraWire Create an arc of circle object given three points. :param p1: Starting point of the circle arc :param p2: Middle point of the circle arc :param p3: End point of the circle arc :rtype: Bluemira wire that contains the arc or circle :raises GeometryError: Raised if the three points are collinear. .. py:function:: make_ellipse(center: numpy.typing.ArrayLike = (0.0, 0.0, 0.0), major_radius: float = 2.0, minor_radius: float = 1.0, major_axis: numpy.typing.ArrayLike = (1, 0, 0), minor_axis: numpy.typing.ArrayLike = (0, 1, 0), start_angle: float = 0.0, end_angle: float = 360.0, label: str = '') -> bluemira.geometry.wire.BluemiraWire Create an ellipse or arc of ellipse object with given parameters. :param center: Center of the ellipse :param major_radius: Major radius of the ellipse :param minor_radius: Minor radius of the ellipse (float). Default to 2. :param major_axis: Major axis direction :param minor_axis: Minor axis direction :param start_angle: Start angle of the arc [degrees] :param end_angle: End angle of the arc [degrees]. if start_angle == end_angle, an ellipse is created, otherwise a ellipse arc is created :param label: Object's label :rtype: Bluemira wire that contains the arc or ellipse .. py:function:: wire_closure(bmwire: bluemira.geometry.wire.BluemiraWire, label='closure') -> bluemira.geometry.wire.BluemiraWire Close this wire with a line segment :param bmwire: supporting wire for the closure :param label: Object's label :rtype: Closure wire .. py:function:: _offset_wire_discretised(wire: bluemira.geometry.wire.BluemiraWire, thickness: float, /, join: str = 'intersect', *, open_wire: bool = True, label: str = '', fallback_method: str = 'square', fallback_force_spline: bool = False, byedges: bool = True, ndiscr: int = 200, **fallback_kwargs) -> bluemira.geometry.wire.BluemiraWire Fallback function for discretised offsetting :returns: The offset wire. :raises GeometryError: If the wire is not closed. This function cannot handle the offet of an open wire. .. py:function:: offset_wire(wire: bluemira.geometry.wire.BluemiraWire, thickness: float, /, join: str = 'intersect', *, open_wire: bool = True, label: str = '', fallback_method: str = 'square', byedges: bool = True, ndiscr: int = 400, allow_fallback: bool = True, **fallback_kwargs) -> bluemira.geometry.wire.BluemiraWire Make a planar offset from a planar wire. :param wire: Wire to offset from :param thickness: Offset distance. Positive values outwards, negative values inwards :param join: Offset method. "arc" gives rounded corners, and "intersect" gives sharp corners :param open_wire: For open wires (counter-clockwise default) whether or not to make an open offset wire, or a closed offset wire that encompasses the original wire. This is disabled for closed wires. :returns: Offset wire :param byedges: Whether or not to discretise the wire by edges :param ndiscr: Number of points to discretise the wire to :param fallback_method: Method to use in discretised offsetting, will default to `square` as `round` is know to be very slow .. rubric:: Notes If primitive offsetting failed, will fall back to a discretised offset implementation, where the fallback kwargs are used. Discretised offsetting is only supported for closed wires. .. py:function:: convex_hull_wires_2d(wires: collections.abc.Sequence[bluemira.geometry.wire.BluemiraWire], ndiscr: int, plane: str = 'xz') -> bluemira.geometry.wire.BluemiraWire Perform a convex hull around the given wires and return the hull as a new wire. The operation performs discretisations on the input wires. :param wires: The wires to draw a hull around. :param ndiscr: The number of points to discretise each wire into. :param plane: The plane to perform the hull in. One of: 'xz', 'xy', 'yz'. Default is 'xz'. :returns: * *A wire forming a convex hull around the input wires in the given* * *plane.* :raises ValueError: not enough wires .. py:function:: polygon_revolve_signed_volume(polygon: numpy.typing.ArrayLike) -> float Revolve a polygon along the z axis, and return the volume. A polygon placed in the RHS of the z-axis in the xz plane would have positive volume if it runs clockwise, and negative volume if it runs counter-clockwise. Similarly a polygon placed on the LHS of the z-axis in the xz plane would have negative volume if it runs clockwise, positive volume if it runs counter-clockwise. :param polygon: Stores the x-z coordinate pairs of the four coordinates. :returns: Volume of revolved polygon :raises ValueError: shape must be (2, N) .. rubric:: Notes Consider one edge of the polygon, which has two vertices, $p$ and $c$. When revolved around the z-axis, this trapezium forms a the frustum of a cone. The expression for the volume of this frustrum needs to be modified to avoid ZeroDivisionError, thus it is recast into the following (also the simplest) form: :math:`V = \frac{\pi}{3} (p_z - c_z) (p_x^2 + p_x c_x + c_x^2)`. Adding together the signed volume of all edges, the excess negative volume from one side would cancel out the excess positive volume from the other, such that abs(signed volume)= the volume of the polygon after being revolved around the z-axis. .. py:function:: partial_diff_of_volume(three_vertices: collections.abc.Sequence[collections.abc.Sequence[float]], normalised_direction_vector: collections.abc.Iterable[float]) -> float Gives the relationship between how the the solid volume varies with the position of one of its verticies. More precisely, it gives gives the the partial derivative of the volume of the solid revolved out of a polygon when one vertex of that polygon is moved in the direction specified by normalised_direction_vector. :param three_vertices: Contain (x, z) coordinates of the polygon. It extracts only the vertex being moved, and the two vertices around it. three_vertices[0] and three_vertices[2] are anchor vertices that cannot be adjusted. shape (3, 2) :param normalised_direction_vector: Direction that the point is allowed to move in. shape = (2,) :returns: Partial differential of volume .. rubric:: Notes Let there be 3 points, :math:`q`, :math:`r`, and :math:`s`, forming two edges of a polygon. When r is moved, the polygon's revolved solid volume changes. After a hefty amount of derivation, everything cancels out to give the expression .. math:: \frac{dV}{d r_z} = q_z q_x - r_z q_x + 2 q_z r_x - 2 s_z r_x + r_z s_x - s_z s_x \frac{dV}{d r_x} = (q_x + r_x + s_x) (s_x - q_x) The dot product between the direction of motion and the vector :math:`\frac{dV}{dr}` gives the required scalar derivative showing "how much does the volume change when r is moved in a certain direction by one unit length". .. py:function:: revolve_shape(shape: bluemira.geometry.base.BluemiraGeoT, base: numpy.typing.ArrayLike = (0.0, 0.0, 0.0), direction: numpy.typing.ArrayLike = (0.0, 0.0, 1.0), degree: float = 180, label: str = '') -> bluemira.geometry.base.BluemiraGeoT Apply the revolve (base, dir, degree) to this shape :param shape: The shape to be revolved :param base: Origin location of the revolution :param direction: The direction vector :param degree: revolution angle :returns: The revolved shape. :raises FreeCADError: Cannot revolve shape .. py:function:: extrude_shape(shape: bluemira.geometry.base.BluemiraGeo, vec: numpy.typing.ArrayLike, label: str = '') -> bluemira.geometry.solid.BluemiraSolid Apply the extrusion along vec to this shape :param shape: The shape to be extruded :param vec: The vector along which to extrude :param label: label of the output shape :rtype: The extruded shape. .. py:class:: SweepShapeTransition Bases: :py:obj:`enum.IntEnum` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.SweepShapeTransition :parts: 1 :private-bases: Sweep shape corner transition options .. py:attribute:: DEFAULT :value: 0 .. py:attribute:: RIGHT_CORNER :value: 1 .. py:attribute:: ROUND_CORNER :value: 2 .. py:function:: sweep_shape(profiles: bluemira.geometry.wire.BluemiraWire | collections.abc.Iterable[bluemira.geometry.wire.BluemiraWire], path: bluemira.geometry.wire.BluemiraWire, *, solid: bool = True, frenet: bool = True, transition: SweepShapeTransition | int = SweepShapeTransition.DEFAULT, label: str = '') -> bluemira.geometry.solid.BluemiraSolid | bluemira.geometry.shell.BluemiraShell Sweep a profile along a path. :param profiles: Profile(s) to sweep :param path: Path along which to sweep the profiles :param solid: Whether or not to create a Solid :param frenet: If true, the orientation of the profile(s) is calculated based on local curvature and tangency. For planar paths, should not make a difference. :param transition: transition type between sweep sections :rtype: Swept geometry object .. py:function:: loft_shape(profiles: collections.abc.Iterable[bluemira.geometry.wire.BluemiraWire], *, solid: bool = True, ruled: bool = False, closed: bool = False, label: str = '') -> bluemira.geometry.solid.BluemiraSolid | bluemira.geometry.shell.BluemiraShell Loft between a set of profiles. :param profiles: Profile(s) to loft between :param solid: Whether or not to create a Solid :param ruled: Create a ruled shape, see https://en.wikipedia.org/wiki/Ruled_surface for explanation. :rtype: Lofted geometry object .. py:function:: fillet_chamfer_decorator(*, chamfer: bool) Decorator for fillet and chamfer operations, checking for validity of wire and radius. :returns: Decorator for fillet and chamfer operations. :raises GeometryError: Number of edges >= 2, radius >= 0 and planar .. py:function:: fillet_wire_2D(wire: bluemira.geometry.wire.BluemiraWire, radius: float) -> bluemira.geometry.wire.BluemiraWire Fillet all edges of a wire :param wire: Wire to fillet :param radius: Radius of the fillet operation :rtype: The filleted wire .. py:function:: chamfer_wire_2D(wire: bluemira.geometry.wire.BluemiraWire, radius: float) -> bluemira.geometry.wire.BluemiraWire Chamfer all edges of a wire :param wire: Wire to chamfer :param radius: Radius of the chamfer operation :rtype: The chamfered wire .. py:function:: distance_to(geo1: numpy.typing.ArrayLike | bluemira.geometry.base.BluemiraGeo, geo2: numpy.typing.ArrayLike | bluemira.geometry.base.BluemiraGeo) -> tuple[float, list[tuple[float, float, float]]] Calculate the distance between two BluemiraGeos. :param geo1: Reference shape. If an iterable of length 3, converted to a point. :param geo2: Target shape. If an iterable of length 3, converted to a point. :returns: * *dist* -- Minimum distance * *vectors* -- List of tuples corresponding to the nearest points between geo1 and geo2. The distance between those points is the minimum distance given by dist. .. py:function:: split_wire(wire: bluemira.geometry.wire.BluemiraWire, vertex: numpy.typing.ArrayLike, tolerance: float = EPS * 10) -> tuple[bluemira.geometry.wire.BluemiraWire | None, bluemira.geometry.wire.BluemiraWire | None] Split a wire at a given vertex. :param wire: Wire to be split :param vertex: Vertex at which to split the wire :param tolerance: Tolerance within which to find the closest vertex on the wire :returns: * *wire_1* -- First half of the wire. Will be None if the vertex is the start point of the wire * *wire_2* -- Last half of the wire. Will be None if the vertex is the start point of the wire :raises GeometryError:: If the vertex is further away to the wire than the specified tolerance .. py:function:: cut_wire_at_z_value(wire: bluemira.geometry.wire.BluemiraWire, point_z: float, location: CutLocation, point_name: str = 'chosen point_z') -> bluemira.geometry.wire.BluemiraWire Remove the parts of the wire below or above a given z-value. :param wire: Wire shape that needs cutting. :param point_z: Z-value at point to make the cut. :param location: Choose to remove the lower or upper part of the wire after cutting. :param point_name: String printed in warning message, relevant when applied to an input of interest e.g. x-point location for an Equilibrium. :returns: The remaining section of wire after cutting. :rtype: cut_wire :raises ValueError: No parts of shape found .. py:class:: CutLocation(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: bluemira.geometry.tools.CutLocation :parts: 1 :private-bases: Used in cut_wire_at_z_value to choose which part of the shape to remove .. py:attribute:: UPPER Remove upper part of shape after cut .. py:attribute:: LOWER remove lower part of shape after cut .. py:function:: slice_shape(shape: bluemira.geometry.base.BluemiraGeo, plane: bluemira.geometry.plane.BluemiraPlane) -> list[numpy.ndarray] | list[bluemira.geometry.wire.BluemiraWire] | None Calculate the plane intersection points with an object :param shape: Shape to intersect with a plane :param plane: Plane to intersect with :returns: * **Wire** (*Union[List[np.ndarray], None]*) -- returns array of intersection points * **Face, Solid, Shell** (*Union[List[BluemiraWire], None]*) -- list of intersections lines .. rubric:: Notes Degenerate cases such as tangets to solid or faces do not return intersections if the shape and plane are acting at the Plane base. Further investigation needed. .. py:function:: get_wire_plane_intersect(convex_bm_wire: bluemira.geometry.wire.BluemiraWire, plane: bluemira.geometry.plane.BluemiraPlane, cut_direction: numpy.typing.NDArray[float]) -> numpy.typing.NDArray[float] Cut a wire using a plane. :param convex_bm_wire: The wire that we're interested in cutting. :param plane: Plane that is cutting the wire. :param cut_direction: np.ndarray with shape==(3,) :returns: np.ndarray with shape==(3,) :rtype: intersection point .. py:function:: circular_pattern(shape: bluemira.geometry.base.BluemiraGeo, origin: numpy.typing.ArrayLike = (0, 0, 0), direction: numpy.typing.ArrayLike = (0, 0, 1), degree: float = 360, n_shapes: int = 10) -> list[bluemira.geometry.base.BluemiraGeo] Make a equally spaced circular pattern of shapes. :param shape: Shape to pattern :param origin: Origin vector of the circular pattern :param direction: Direction vector of the circular pattern :param degree: Angle range of the patterning :param n_shapes: Number of shapes to pattern :rtype: List of patterned shapes, the first element is the original shape .. py:function:: mirror_shape(shape: bluemira.geometry.base.BluemiraGeo, base: numpy.typing.ArrayLike, direction: numpy.typing.ArrayLike, label='') -> bluemira.geometry.base.BluemiraGeo Get a mirrored copy of a shape about a plane. :param shape: Shape to mirror :param base: Mirror plane base :param direction: Mirror plane normal direction :rtype: The mirrored shape :raises GeometryError: if the norm of the direction tuple is <= 3*EPS .. py:function:: is_convex(points: numpy.typing.NDArray) Check that the the list of xz points are strictly convex, i.e. Not even collinear points are allowed. However, repeated points are allowed, as the repeated point would be ignored; and points are allowed to be entered in 3D (xyz), but the y component would be ignored as well. :param points: A list of points that we want to check the convexity for. Shape = (n, 2/3) :rtype: boolean .. py:function:: save_as_STP(shapes: bluemira.geometry.base.BluemiraGeoT | collections.abc.Iterable[bluemira.geometry.base.BluemiraGeoT], filename: str, **kwargs) Saves a series of Shape objects as a STEP assembly :param shapes: List of shape objects to be saved :param filename: Full path filename of the STP assembly .. py:function:: save_cad(shapes: bluemira.geometry.base.BluemiraGeoT | collections.abc.Iterable[bluemira.geometry.base.BluemiraGeoT], filename: str | pathlib.Path, cad_format: str | bluemira.codes._freecadapi.CADFileType = 'stp', names: str | list[str] | None = None, **kwargs) Save the CAD of a component (eg a reactor) or a list of components :param shapes: shapes to save :param filename: Full path filename for the CAD file :param cad_format: file format to save as :param names: Names of shapes to save :param kwargs: arguments passed to cadapi save function .. py:function:: import_cad(filename: str | pathlib.Path, cad_format: str | bluemira.codes._freecadapi.CADFileType | None = None, unit_scale: str = 'm', **kwargs) -> bluemira.geometry.base.BluemiraGeo | list[bluemira.geometry.base.BluemiraGeo] Import CAD from file :returns: The imported objects .. py:function:: _nb_dot_2D(v_1, v_2) :returns: Numba 2-D dot product .. py:function:: _nb_clip(val, a_min, a_max) :returns: Numba 1-D clip. .. py:function:: _signed_distance_2D(point: numpy.ndarray, polygon: numpy.ndarray) -> float 2-D function for the signed distance from a point to a polygon. The return value is negative if the point is outside the polygon, and positive if the point is inside the polygon. :param point: 2-D point :param polygon: 2-D set of points (closed) :rtype: Signed distance value of the point to the polygon .. rubric:: Notes Credit: Inigo Quilez (https://www.iquilezles.org/) .. py:function:: signed_distance_2D_polygon(subject_poly: numpy.ndarray, target_poly: numpy.ndarray) -> numpy.ndarray 2-D vector-valued signed distance function from a subject polygon to a target polygon. The return values are negative for points outside the target polygon, and positive for points inside the target polygon. :param subject_poly: Subject 2-D polygon :param target_poly: Target 2-D polygon (closed polygons only) :rtype: Signed distances from the vertices of the subject polygon to the target polygon .. rubric:: Notes This can used as a keep-out-zone constraint, in which the target polygon would be the keep-out-zone, and the subject polygon would be the shape which must be outsize of the keep-out-zone. The target polygon must be closed. .. py:function:: signed_distance(origin: bluemira.geometry.wire.BluemiraWire | bluemira.geometry.coordinates.Coordinates, target: bluemira.geometry.wire.BluemiraWire | bluemira.geometry.coordinates.Coordinates) -> float Single-valued signed "distance" function between two wires. Will return negative values if origin does not touch or intersect target, 0 if there is one intersection, and a positive estimate of the intersection length if there are overlaps. :param origin: a 0D/1D set of points :param target: a 0D/1D set of points :rtype: Closest distance between origin and target .. rubric:: Notes This is not a pure implementation of a distance function, as for overlapping wires a metric of the quantity of overlap is returned (a positive value). This nevertheless enables the use of such a function as a constraint in gradient-based optimisers. This function has been extended to allow the target wire to be a point (:class:`~bluemira.geometry.coordinates.Coordinates`) as well The signed "distance" function can be expressed as: .. math:: d (\textbf{p}_1, \textbf{p}_2) = \begin{cases} - \| \textbf{p}_1 - \textbf{p}_2 \| & \text{if no intersection} \\ 0 & \text{if one intersection} \\ \text{positive estimate of overlap length} & \text{if overlap exists} \end{cases} .. py:function:: raise_error_if_overlap(origin: bluemira.geometry.wire.BluemiraWire | bluemira.geometry.coordinates.Coordinates, target: bluemira.geometry.wire.BluemiraWire | bluemira.geometry.coordinates.Coordinates, origin_name: str = '', target_name: str = '') :raises GeometryError: if two wires/points intersects overlaps. .. py:function:: connect_shapes(shapes: collections.abc.Iterable[bluemira.geometry.base.BluemiraGeo], tolerance: float = 0.0, label: str = '') -> bluemira.geometry.base.BluemiraGeo Connect (imprint) a list of shapes together. This is similar to a boolean fuse operation, but connects the interior walls of solid or shells (removeing the excess). For wires it will fuse them, keeping the biggest joined piece. :param shapes: List of shape objects to be saved :param label: Label for the resulting shape :rtype: Result of the connect operation. :raises GeometryError: In case the boolean operation fails. :raises ValueError: All shapes (2 or more) must be the same type .. py:function:: make_compound(shapes: collections.abc.Iterable[bluemira.geometry.base.BluemiraGeo], label: str = '') -> bluemira.geometry.compound.BluemiraCompound Make a compound of the given shapes. The shapes must all be the same topologic type (all solids, shells etc.). No mixing. :param shapes: List of shape objects to be saved :param label: Label for the resulting shape :rtype: Result of the connect operation. :raises GeometryError: In case the boolean operation fails. :raises ValueError: All shapes (2 or more) must be the same type .. py:function:: boolean_fuse(shapes: collections.abc.Iterable[bluemira.geometry.base.BluemiraGeo], label: str = '') -> bluemira.geometry.base.BluemiraGeo Fuse two or more shapes together. Internal splitter are removed. :param shapes: List of shape objects to be saved :param label: Label for the resulting shape :rtype: Result of the boolean operation. :raises GeometryError: In case the boolean operation fails. :raises ValueError: All shapes (2 or more) must be the same type .. py:function:: boolean_cut(shape: bluemira.geometry.base.BluemiraGeoT, tools: bluemira.geometry.base.BluemiraGeoT | collections.abc.Iterable[bluemira.geometry.base.BluemiraGeoT]) -> bluemira.geometry.base.BluemiraGeoT | list[bluemira.geometry.base.BluemiraGeoT] Subtract from the base, a (list of) topo shapes cut (tools). :param shape: the reference object :param tools: List of BluemiraGeo shape objects to be used as tools. :rtype: Result of the boolean cut operation. :raises error: GeometryError: In case the boolean operation fails. .. rubric:: Notes Example: Before cutting: `shape` = a cube, `tools` = a sphere. .. figure:: /geometry/images/before_cut.png After cutting: a cube with a sphere subtracted out of it. .. figure:: /geometry/images/after_cut.png .. py:function:: boolean_fragments(shapes: collections.abc.Iterable[bluemira.geometry.solid.BluemiraSolid], tolerance: float = 0.0) -> tuple[bluemira.geometry.compound.BluemiraCompound, list[list[bluemira.geometry.solid.BluemiraSolid]]] Split a list of shapes into their Boolean fragments. :param shapes: List of BluemiraSolids to be split into Boolean fragments :param tolerance: Tolerance with which to perform the operation :returns: * *compound* -- A compound of the unique fragments * *fragment_map* -- An ordered list of groups of solid Boolean fragments (ordered in terms of input ordering) .. rubric:: Notes Labelling will be lost. This function is only tested on solids. .. py:function:: point_inside_shape(point: collections.abc.Iterable[float], shape: bluemira.geometry.base.BluemiraGeoT) -> bool Check whether or not a point is inside a shape. :param point: Coordinates of the point :param shape: Geometry to check with :rtype: Whether or not the point is inside the shape .. py:function:: point_on_plane(point: collections.abc.Iterable[float], plane: bluemira.geometry.plane.BluemiraPlane, tolerance: float = D_TOLERANCE) -> bool Check whether or not a point is on a plane. :param point: Coordinates of the point :param plane: Plane to check :param tolerance: Tolerance with which to check :rtype: Whether or not the point is on the plane .. py:function:: serialise_shape(shape: bluemira.geometry.base.BluemiraGeoT) Serialise a BluemiraGeo object. :returns: A serialised BluemiraGeo object. .. py:function:: deserialise_shape(buffer: dict) -> bluemira.geometry.base.BluemiraGeoT | None Deserialise a BluemiraGeo object obtained from serialise_shape. :param buffer: Object serialisation as stored by serialise_shape :rtype: The deserialised BluemiraGeo object. .. py:function:: get_shape_by_name(shape: bluemira.geometry.base.BluemiraGeoT, name: str) -> list[bluemira.geometry.base.BluemiraGeoT] Search through the boundary of the shape and get any shapes with a label corresponding to the provided name. Includes the shape itself if the name matches its label. :param shape: The shape to search for the provided name. :param name: The name to search for. :rtype: The shapes known to the provided shape that correspond to the provided name. .. py:function:: find_clockwise_angle_2d(base: numpy.ndarray, vector: numpy.ndarray) -> numpy.ndarray Find the clockwise angle between the 2D vectors ``base`` and ``vector`` in the range [0°, 360°). :param base: The vector to start the angle from. :param vector: The vector to end the angle at. :rtype: The clockwise angle between the two vectors in degrees. :raises TypeError: both base and vector must be arrays :raises ValueError: Array shapes must be (2, N)