Ë
    Jÿi‹5  ã                  óæ  — d Z ddlmZ ddlmZmZmZmZ ddlm	Z	m
Z
 ddlmZ ddlmZ ddlmZmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZ ddlmZ ddl m!Z! ddl"m#Z# ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z- ddl.m/Z/m0Z0m1Z1m2Z2 ddl3m4Z4m5Z5m6Z6m7Z7m8Z8 e
rddl9m:Z: ddl;m<Z<m=Z= d#d„Z>	 d$	 	 	 	 	 	 	 d%d„Z?	 	 	 	 d&d„Z@	 d'	 	 	 	 	 d(d„ZA	 	 	 	 	 	 	 	 d)d„ZBd*d„ZCd+d„ZDd,d-d„ZEd.d„ZFd/d „ZGd0d!„ZHd1d"„ZIy)2a~  
build123d topology

name: utils.py
by:   Gumyr
date: January 07, 2025

desc:

This module provides utility functions and helper classes for the build123d CAD library, enabling
advanced geometric operations and facilitating the use of the OpenCascade CAD kernel. It complements
the core library by offering reusable and modular tools for manipulating shapes, performing Boolean
operations, and validating geometry.

Key Features:
- **Geometric Utilities**:
  - `polar`: Converts polar coordinates to Cartesian.
  - `tuplify`: Normalizes inputs into consistent tuples.
  - `find_max_dimension`: Computes the maximum bounding dimension of shapes.

- **Shape Creation**:
  - `_make_loft`: Creates lofted shapes from wires and vertices.
  - `_make_topods_compound_from_shapes`: Constructs compounds from multiple shapes.
  - `_make_topods_face_from_wires`: Generates planar faces with optional holes.

- **Boolean Operations**:
  - `_topods_bool_op`: Generic Boolean operations for TopoDS_Shapes.
  - `new_edges`: Identifies newly created edges from combined shapes.

- **Enhanced Math**:
  - `isclose_b`: Overrides `math.isclose` with a stricter absolute tolerance.

This module is a critical component of build123d, supporting complex CAD workflows and geometric
transformations while maintaining a clean, extensible API.

license:

    Copyright 2025 Gumyr

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

é    )Úannotations)ÚradiansÚsinÚcosÚisclose)ÚAnyÚTYPE_CHECKING)ÚIterable)Ú	BRep_Tool)ÚBRepAlgoAPI_BooleanOperationÚBRepAlgoAPI_CutÚBRepAlgoAPI_Splitter)ÚBRepBuilderAPI_MakeFace)ÚBRepLib_FindSurface)ÚBRepOffsetAPI_ThruSections)ÚBRepPrimAPI_MakePrism)ÚShapeFix_FaceÚShapeFix_Shape)ÚTopAbs_ShapeEnum)ÚTopExp_Explorer)ÚTopTools_ListOfShape)	ÚTopoDSÚTopoDS_BuilderÚTopoDS_CompoundÚTopoDS_FaceÚTopoDS_ShapeÚTopoDS_ShellÚTopoDS_VertexÚTopoDS_EdgeÚTopoDS_Wire)Ú	TOLERANCEÚBoundBoxÚVectorÚ
VectorLikeé   )ÚShapeÚ	ShapeListÚdowncastÚ	shapetypeÚunwrap_topods_compound)ÚVertex)ÚEdgeÚWirec                ó<  — t        |«      }| �%t        | t        t        t        t
        t        f«      st        dt        | «      › �«      ‚t        | |j                  «      }t        |j                  «       «      }|j                  «       }|t        j                  k(  r€g }t!        |t        j"                  «      }|j%                  «       rI|j'                  t        |j)                  «       «      «       |j+                  «        |j%                  «       rŒIt-        |«      }|S )a²  extrude

    Extrude a Shape in the provided direction.
    * Vertices generate Edges
    * Edges generate Faces
    * Wires generate Shells
    * Faces generate Solids
    * Shells generate Compounds

    Args:
        direction (VectorLike): direction and magnitude of extrusion

    Raises:
        ValueError: Unsupported class
        RuntimeError: Generated invalid result

    Returns:
        TopoDS_Shape: extruded shape
    zextrude not supported for )r#   Ú
isinstancer   r   r    r   r   Ú
ValueErrorÚtyper   Úwrappedr(   r&   Ú	ShapeTyper   ÚTopAbs_COMPSOLIDr   ÚTopAbs_SOLIDÚMoreÚappendÚCurrentÚNextÚ!_make_topods_compound_from_shapes)ÚobjÚ	directionÚprism_builderÚ	extrusionÚ
shape_typeÚsolidsÚexplorers          úi/workspace/moltex-experiments/.pixi/envs/default/lib/python3.12/site-packages/build123d/topology/utils.pyÚ_extrude_topods_shaperC   `   sç   € ô( �yÓ!€Ià
€{œ*ØÜ	œ¤[´+¼|ÐLôô Ð5´d¸3³i°[ÐAÓBÐBä)¨#¨y×/@Ñ/@ÓA€MÜ˜×,Ñ,Ó.Ó/€IØ×$Ñ$Ó&€JØÔ%×6Ñ6Ò6ØˆÜ" 9Ô.>×.KÑ.KÓLˆØ�m‰mŒoØ�M‰Mœ( 8×#3Ñ#3Ó#5Ó6Ô7Ø�M‰MŒOð �m‰m�oô 6°fÓ=ˆ	ØÐó    c                óv  — t        | «      } t        | «      dk  rt        d«      ‚| D �cg c]  }t        |j                  t
        «      sŒ|‘Œ! }}t        |«      }|dkD  rt        d«      ‚|dk(  rEt        | d   j                  t
        «      s(t        | d   j                  t
        «      st        d«      ‚|dk(  r^t        | «      dk(  rt        d«      ‚t        | d   j                  t
        «      rt        | d   j                  t
        «      st        d	«      ‚t        ||«      }| D ]n  }t        |j                  t
        «      r|j                  |j                  «       Œ9t        |j                  t        «      sŒT|j                  |j                  «       Œp |j                  «        |j                  «       S c c}w )
a¶  make loft

    Makes a loft from a list of wires and vertices. Vertices can appear only at the
    beginning or end of the list, but cannot appear consecutively within the list
    nor between wires.

    Args:
        wires (list[Wire]): section perimeters
        ruled (bool, optional): stepped or smooth. Defaults to False (smooth).

    Raises:
        ValueError: Too few wires

    Returns:
        TopoDS_Shape: Lofted object
    é   zMore than one wire is requiredzOnly two vertices are allowedr%   r   éÿÿÿÿz=The vertex must be either at the beginning or end of the listz=You can't have only 2 vertices to loft; try adding some wiresz9The vertices must be at the beginning and end of the list)ÚlistÚlenr0   r/   r2   r   r   Ú	AddVertexr    ÚAddWireÚBuildr&   )ÚobjsÚfilledÚruledr;   ÚverticesÚvertex_countÚloft_builders          rB   Ú
_make_loftrS   ‰   sy  € ô* �‹:€DÜ
ˆ4ƒy�1‚}ÜÐ9Ó:Ð:Ù#ÓN™t˜¤z°#·+±+¼}Õ'M’˜t€HÐNÜ�x“=€Là�aÒÜÐ8Ó9Ð9à�qÒÜ�4˜‘7—?‘?¤MÔ2Ü�d˜2‘h×&Ñ&¬Ô6äØKó
ð 	
ð �qÒÜˆt‹9˜Š>ÜØOóð ô �t˜A‘w—‘¬Ô6Ü˜4 ™8×+Ñ+¬]Ô;äØKóð ô .¨f°eÓ<€LãˆÜ�c—k‘k¤=Ô1Ø×"Ñ" 3§;¡;Õ/Ü˜Ÿ™¤[Õ1Ø× Ñ  §¡Õ-ð	 ð ×ÑÔà×ÑÓÐùòK Os   ©F6Á	F6c                óˆ   — t        «       }t        «       }|j                  |«       | D ]  }|€Œ|j                  ||«       Œ |S )zñCreate an OCCT TopoDS_Compound

    Create an OCCT TopoDS_Compound object from an iterable of TopoDS_Shape objects

    Args:
        occt_shapes (Iterable[TopoDS_Shape]): OCCT shapes

    Returns:
        TopoDS_Compound: OCCT compound
    )r   r   ÚMakeCompoundÚAdd)Úocct_shapesÚcompÚcomp_builderÚshapes       rB   r:   r:   É   sI   € ô Ó€DÜ!Ó#€LØ×Ñ˜dÔ#ãˆØÑØ×Ñ˜T 5Õ)ð ð €KrD   Nc                ó  — |r t        j                  | «      st        d«      ‚|rt        |«      ng }t	        | g|z   «      }t        |d¬«      j                  «       st        d«      ‚t        | «      }|j                  «        t        j                  |j                  «       «      }t        |d«      }|D ]3  }t        j                  |«      st        d«      ‚|j                  |«       Œ5 |j                  «        |j                  «       st        d|j!                  «       › �«      ‚|j#                  «       }t%        |«      }|j'                  «        |j                  «        t        j(                  |j+                  «       «      S )aÓ  _make_topods_face_from_wires

    Makes a planar face from one or more wires

    Args:
        outer_wire (TopoDS_Wire): closed perimeter wire
        inner_wires (Iterable[TopoDS_Wire], optional): holes. Defaults to None.

    Raises:
        ValueError: outer wire not closed
        ValueError: wires not planar
        ValueError: inner wire not closed
        ValueError: internal error

    Returns:
        TopoDS_Face: planar face potentially with holes
    z.Cannot build face(s): outer wire is not closedT)Ú	OnlyPlanez&Cannot build face(s): wires not planarz.Cannot build face(s): inner wire is not closedzCannot build face(s): )r   Ú
IsClosed_sr0   rH   r:   r   ÚFoundr   ÚPerformr   ÚWire_sr&   r   rV   rL   ÚIsDoneÚErrorÚFacer   ÚFixOrientationÚFace_sÚResult)	Ú
outer_wireÚinner_wiresÚverification_compoundÚsf_sÚ	topo_wireÚface_builderÚ
inner_wireÚfaceÚsf_fs	            rB   Ú_make_topods_face_from_wiresrp   á   sG  € ñ( œ9×/Ñ/°
Ô;ÜÐIÓJÐJÙ'2”$�{Ô#¸€Kô >Ø	ˆ�{Ñ"óÐô Ð4ÀÔE×KÑKÔMÜÐAÓBÐBô ˜*Ó%€DØ‡L�L„NÜ—‘˜dŸj™j›lÓ+€Iä*¨9°dÓ;€Lã!ˆ
Ü×#Ñ# JÔ/ÜÐMÓNÐNØ×Ñ˜Õ$ð "ð
 ×ÑÔà×ÑÔ ÜÐ1°,×2DÑ2DÓ2FÐ1GÐHÓIÐIà×ÑÓ€Dä˜Ó€DØ×ÑÔØ‡L�L„Nä�=‰=˜Ÿ™›Ó'Ð'rD   c                óª  — t        | «      } t        |«      }t        «       }| D ]  }|j                  |«       Œ t        «       }|D ]  }|j                  |«       Œ |j                  |«       |j	                  |«       |j                  d«       |j                  «        t        |j                  «       «      }t        |t        «      rt        |d«      }|S )zìGeneric boolean operation for TopoDS_Shapes

    Args:
        args: Iterable[TopoDS_Shape]:
        tools: Iterable[TopoDS_Shape]:
        operation: BRepAlgoAPI_BooleanOperation | BRepAlgoAPI_Splitter:

    Returns: TopoDS_Shape

    T)rH   r   ÚAppendÚSetArgumentsÚSetToolsÚSetRunParallelrL   r(   r&   r/   r   r*   )ÚargsÚtoolsÚ	operationÚargr;   ÚtoolÚresults          rB   Ú_topods_bool_opr|     sµ   € ô �‹:€DÜ�‹K€EÜ
Ó
 €CÛˆØ�
‰
�3�ð ô  Ó!€DÛˆØ�‰�CÕð ð ×Ñ˜3ÔØ×Ñ�tÔà×Ñ˜TÔ"Ø‡O�OÔä�i—o‘oÓ'Ó(€Fä�&œ/Ô*Ü'¨°Ó5ˆà€MrD   c                ól  — t        | «      } t        |«      }| D �ch c]  }|j                  ’Œ }}|D �ch c]  }|j                  ’Œ }}t        ||z
  «      }g }| |fD ]'  }|j                  t        |t         «      r|ng |¢«       Œ) |D �cg c]  }|j                  |v sŒ|‘Œ }}|S c c}w c c}w c c}w )z@Compare the OCCT objects of each list and return the differences)rH   r2   Úextendr/   )	Ú
shapes_oneÚ
shapes_tworZ   Úocct_oneÚocct_twoÚ
occt_deltaÚ
all_shapesÚshapesÚshape_deltas	            rB   Údeltar‡   A  s¹   € ä�jÓ!€JÜ�jÓ!€JÙ+5Ó6©: %�—“¨:€HÐ6Ù+5Ó6©: %�—“¨:€HÐ6Ü�h Ñ)Ó*€Jà€JØ˜zÓ*ˆØ×Ñ¤J¨v´tÔ$<™&À)ÀFÀ)ÕLð +á&0ÓP¡j˜U°E·M±MÀZÒ4O’5 j€KÐPØÐùò 7ùÚ6ùò Qs   ›B'´B,ÂB1ÂB1c                óÌ   — t        | t        «      r| n| g} t        | D �cg c]  }|j                  ‘Œ c}«      }t	        j
                  |t        d¬«      }|j                  S c c}w )z2Return the maximum dimension of one or more shapesT)Ú	toleranceÚoptimal)r/   r
   r:   r2   r"   Úfrom_topo_dsr!   Údiagonal)r…   ÚsÚ	compositeÚbboxs       rB   Úfind_max_dimensionr�   P  sU   € ä! &¬(Ô3‰V¸&¸€FÜ1ÁfÓ2MÁfÀ°1·9³9ÀfÑ2MÓN€IÜ× Ñ  ´iÈÔN€DØ�=‰=Ðùò 3Ns   ŸA!c                ó    — t        | |||¬«      S )a¦  Determine whether two floating point numbers are close in value.
    Overridden abs_tol default for the math.isclose function.

    Args:
        x (float): First value to compare
        y (float): Second value to compare
        rel_tol (float, optional): Maximum difference for being considered "close",
            relative to the magnitude of the input values. Defaults to 1e-9.
        abs_tol (float, optional): Maximum difference for being considered "close",
            regardless of the magnitude of the input values. Defaults to 1e-14
            (unlike math.isclose which defaults to zero).

    Returns: True if a is close in value to b, and False otherwise.
    )Úrel_tolÚabs_tol)r   )ÚxÚyr’   r“   s       rB   Ú	isclose_br–   X  s   € ô �1�a °'Ô:Ð:rD   c                óR  — t        «       }| j                  «       D ]*  }|j                  €Œ|j                  |j                  «       Œ, t        «       }|D ��cg c]  }|j                  «       D ]  }|‘Œ Œ c}}D ]*  }|j                  €Œ|j                  |j                  «       Œ, t	        «       }|j                  |«       |j                  |«       |j                  d«       |j                  «        g }t        |j                  «       t        j                  «      }	|	j                  «       rk| j                  j                  t!        |	j#                  «       «      «      }
| |
_        |j'                  |
«       |	j)                  «        |	j                  «       rŒkt+        |«      S c c}}w )a  new_edges

    Given a sequence of shapes and the combination of those shapes, find the newly added edges

    Args:
        objects (Shape): sequence of shapes
        combined (Shape): result of the combination of objects

    Returns:
        ShapeList[Edge]: new edges
    T)r   Úedgesr2   rr   r   rs   rt   ru   rL   r   r&   r   ÚTopAbs_EDGEr6   Ú	__class__Úcastr(   r8   Útopo_parentr7   r9   r'   )ÚcombinedÚobjectsÚcombined_topo_edgesÚedgeÚoriginal_topo_edgesr;   Úerx   r˜   rA   Ú
found_edges              rB   Ú	new_edgesr¤   j  sN  € ô /Ó0ÐØ—‘Ö ˆØ�<‰<Ñ#Ø×&Ñ& t§|¡|Õ4ð !ô
 /Ó0ÐÙ%Ô;™g�s¨s¯y©y®{¨!’¨{�˜gÕ;ˆØ�<‰<Ñ#Ø×&Ñ& t§|¡|Õ4ð <ô
  Ó!€IØ×ÑÐ.Ô/Ø×ÑÐ*Ô+Ø×Ñ˜TÔ"Ø‡O�OÔà€EÜ˜yŸ™Ó0Ô2B×2NÑ2NÓO€HØ
�-‰-Œ/Ø×'Ñ'×,Ñ,¬X°h×6FÑ6FÓ6HÓ-IÓJˆ
Ø!)ˆ
ÔØ�‰�ZÔ Ø�‰Œð	 �-‰-�/ô �UÓÐùó' <s   ÁF#c                ó^   — | t        t        |«      «      z  | t        t        |«      «      z  fS )z4Convert polar coordinates into cartesian coordinates)r   r   r   )ÚlengthÚangles     rB   Úpolarr¨   ”  s)   € à”Sœ ›Ó(Ñ(¨&´3´w¸u³~Ó3FÑ*FÐGÐGrD   c                óv   — | €d}|S t        | t        t        f«      rt        | «      }|S t        | g|z  «      }|S )zCreate a size tupleN)r/   ÚtuplerH   )r;   Údimr{   s      rB   Útuplifyr¬   ™  sJ   € à
€{Øˆð
 €Mô	 
�Cœ%¤˜Ô	'Ü�s“ˆð €Mô ˜�u˜s‘{Ó#ˆØ€MrD   c                ó   — t        | j                  t        «      rY| D �ch c]  }t        |j                  «      ’Œ }}t	        |«      dk(  r|j                  «       }|S t        | j                  «      }|S t        | j                  «      }|S c c}w )zReturn Shape's TopAbs_ShapeEnumr%   )r/   r2   r   r)   rI   Úpop)r;   ÚoÚ
shapetypesr{   s       rB   Úunwrapped_shapetyper±   ¤  s}   € ä�#—+‘+œÔ/Ù47Ó8±C¨q”i §	¡	Õ*°Cˆ
Ð8Üˆz‹?˜aÒØ—^‘^Ó%ˆFð
 €Mô ˜sŸ{™{Ó+ˆFð €Mô ˜3Ÿ;™;Ó'ˆØ€Mùò 9s   ŸB)r;   r   r<   r$   Úreturnr   )F)rM   zIterable[Vertex | Wire]rN   ÚboolrO   r³   r²   r   )rW   zIterable[TopoDS_Shape | None]r²   r   )N)rg   r    rh   zIterable[TopoDS_Wire] | Noner²   r   )rv   úIterable[TopoDS_Shape]rw   r´   rx   z3BRepAlgoAPI_BooleanOperation | BRepAlgoAPI_Splitterr²   r   )r   úIterable[Shape]r€   rµ   r²   zlist[Shape])r…   zShape | Iterable[Shape]r²   Úfloat)g•Ö&è.>g›+¡†›„=)r”   r¶   r•   r¶   r²   r³   )rž   r&   r�   r&   r²   zShapeList[Edge])r¦   r¶   r§   r¶   r²   ztuple[float, float])r;   r   r«   Úintr²   ztuple | None)r;   r&   r²   r   )JÚ__doc__Ú
__future__r   Úmathr   r   r   r   Útypingr   r	   Úcollections.abcr
   ÚOCP.BRepr   ÚOCP.BRepAlgoAPIr   r   r   ÚOCP.BRepBuilderAPIr   ÚOCP.BRepLibr   ÚOCP.BRepOffsetAPIr   ÚOCP.BRepPrimAPIr   ÚOCP.ShapeFixr   r   Ú
OCP.TopAbsr   Ú
OCP.TopExpr   ÚOCP.TopToolsr   Ú
OCP.TopoDSr   r   r   r   r   r   r   r   r    Úbuild123d.geometryr!   r"   r#   r$   Ú
shape_corer&   r'   r(   r)   r*   Úzero_dr+   Úone_dr,   r-   rC   rS   r:   rp   r|   r‡   r�   r–   r¤   r¨   r¬   r±   © rD   rB   Ú<module>rÍ      s+  ðñ3õj #ç +Ó +ß %å $å ÷ñ õ
 7Ý +Ý 8Ý 1ß 6Ý 'Ý &Ý -÷
÷ 
õ 
÷ GÓ Fç UÕ Uñ Ýß!ó&ðX ð= Ø
!ð= àð= ð ð= ð ó	= ð@Ø.ðàóð2 JNð6(Øð6(Ø*Fð6(àó6(ðr$Ø
 ð$à!ð$ð Cð$ð ó	$óNóô;ó$'óTHó
ô
rD   