
    ^j                   0   d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
mZ ddlmZ ddlmZ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mZ ddlmZ ddlmZ ddl m!Z!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/m0Z0 ddl1m2Z2m3Z3 ddl4m5Z5 ddl6m7Z7m8Z8 ddl9m:Z: ddl;m<Z<m=Z=m>Z> ddl?m@Z@ ddlAmBZBmCZCmDZDmEZEmFZFmGZG ddlHmIZImJZJmKZKmLZL ddlMmNZN ddlOmPZPmQZQmRZR ddlSmTZT ddlUmVZV dd lWmXZXmYZYmZZZm[Z[m\Z\ dd!l]m^Z^ dd"l_m`Z` dd#lambZb dd$lcmdZdmeZe dd%lfmgZgmhZhmiZimjZj dd&lkmlZl dd'lmmnZnmoZo dd(lpmqZqmrZrmsZs dd)lmtZt dd*lumvZv dd+lwmxZxmyZymzZzm{Z{m|Z| dd,l}m~Z~mZmZ dd-lmZ dd.lmZmZ dd/lmZmZmZmZmZmZ dd0lmZmZmZmZmZmZmZmZmZ d1d2lmZmZmZmZ d1d3lmZmZmZmZmZmZmZmZmZmZmZ d1d4lmZmZmZmZ d1d5lmZ erd1d6lmZmZ d1d7lmZ  ed8eed9      Z G d: d;eee         Z G d< d9eey         Z G d= d>ee{         Zd@d?Zy)Au  
build123d topology

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

desc:

This module provides classes and methods for two-dimensional geometric entities in the build123d CAD
library, focusing on the `Face` and `Shell` classes. These entities form the building blocks for
creating and manipulating complex 2D surfaces and 3D shells, enabling precise modeling for CAD
applications.

Key Features:
- **Mixin2D**:
  - Adds shared functionality to `Face` and `Shell` classes, such as splitting, extrusion, and
    projection operations.

- **Face Class**:
  - Represents a 3D bounded surface with advanced features like trimming, offsetting, and Boolean
    operations.
  - Provides utilities for creating faces from wires, arrays of points, Bézier surfaces, and ruled
    surfaces.
  - Enables geometry queries like normal vectors, surface centers, and planarity checks.

- **Shell Class**:
  - Represents a collection of connected faces forming a closed surface.
  - Supports operations like lofting and sweeping profiles along paths.

- **Utilities**:
  - Includes methods for sorting wires into buildable faces and creating holes within faces
    efficiently.

The module integrates deeply with OpenCascade to leverage its powerful CAD kernel, offering robust
and extensible tools for surface and shell creation, manipulation, and analysis.

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.

    )annotationsN)ABCabstractmethod)IterableSequence)degrees)TYPE_CHECKINGAnyLiteralTypeVar)cast)overload)BRep_Builder	BRep_Tool)BRepAdaptor_Curve)BRepAlgo)BRepAlgoAPI_CommonBRepAlgoAPI_Section)BRepBuilderAPI_MakeEdgeBRepBuilderAPI_MakeFaceBRepBuilderAPI_MakeWire)BRepClass3d_SolidClassifier)BRepExtrema_DistShapeShape)BRepFeat_SplitShape)BRepFill)BRepFilletAPI_MakeFillet2d)	BRepGPropBRepGProp_Face)BRepIntCurveSurface_Inter)BRepOffsetAPI_MakeFillingBRepOffsetAPI_MakePipeShell)BRepPrimAPI_MakeRevol)	BRepToolsBRepTools_ReShapeBRepTools_WireExplorer)gce_MakeLin)Geom_BezierSurfaceGeom_BSplineCurveGeom_OffsetSurfaceGeom_RectangularTrimmedSurfaceGeom_SurfaceGeom_TrimmedCurve)
GeomAbs_C0GeomAbs_CurveType
GeomAbs_G1
GeomAbs_G2)GeomAdaptor_Surface)GeomAPI_ExtremaCurveCurveGeomAPI_PointsToBSplineSurfaceGeomAPI_ProjectPointOnSurf)GeomLib_IsPlanarSurface)GeomProjLib)gp_Ax1gp_Ax3gp_Plngp_Pntgp_Vec)GProp_GProps)	Precision)ShapeAnalysis_Edge)ShapeFix_SolidShapeFix_Wire)Standard_ConstructionErrorStandard_FailureStandard_NoSuchObjectStandard_TypeMismatch)StdFail_NotDone)TColgp_Array1OfPntTColgp_HArray2OfPnt)TColStd_Array1OfIntegerTColStd_Array1OfRealTColStd_HArray2OfReal)TopAbs_Orientation)TopExp)TopoDSTopoDS_FaceTopoDS_ShapeTopoDS_ShellTopoDS_Solid))TopTools_IndexedDataMapOfShapeListOfShapeTopTools_ListOfShapeTopTools_SequenceOfShape)interpolate_curve_network)Self
deprecated)CenterOfContinuityLevelGeomTypeKeepSortBy
Transition)	DEG2RAD	TOLERANCEAxisColorLocationOrientedBoundBoxPlaneVector
VectorLike   )EdgeMixin1DWire_split_edge_at_vertex)TOPODSShape	ShapeList	SkipClean_sew_topods_faces_topods_bool_op_topods_entities_topods_face_normal_atdowncastget_top_level_topods_shapes	shapetype)_extrude_topods_shape
_make_loft_make_topods_face_from_wiresfind_max_dimension)Vertex)CompoundCurve)SolidTFacec                     e Zd ZdZedd       Zedd       Ze	 	 	 	 	 	 dd       ZddZ	e
	 	 	 	 	 	 dd       Ze
	 	 	 	 	 	 dd       Ze
ej                  f	 	 	 	 	 dd       Zej                  fdd	Zef	 	 	 	 	 dd
Z	 	 d	 	 	 	 	 	 	 ddZ	 	 	 d	 	 	 	 	 	 	 	 	 ddZedd       Zd dZ	 	 	 d!	 	 	 	 	 	 	 	 	 d"dZ	 	 d#	 	 	 	 	 	 	 	 	 d$dZy)%Mixin2Dz1Additional methods to add to Face and Shell classc                     y)zDimension of Faces and Shells    selfs    Q/opt/ringagent/.cad-venv/lib/python3.12/site-packages/build123d/topology/two_d.py_dimzMixin2D._dim   s         c           
        t         j                  t        t         j                  t        t         j
                  t        t         j                  t        t         j                  t        i}t        |      } ||   t        |            S )z6Returns the right type of wrapper, given a OCCT object)taTopAbs_VERTEXr{   TopAbs_EDGErh   TopAbs_WIRErj   TopAbs_FACEr   TopAbs_SHELLShellrv   rt   )clsobjconstructor_lut
shape_types       r   r   zMixin2D.cast   s[     fNNDNNDNNDOOU
 s^
*z*8C=99r   c                    t         S )z9Unused - only here because Mixin1D is a subclass of Shape)NotImplementedr   r   	directions      r   extrudezMixin2D.extrude   s
    
 r   c                    | j                   t        d      t        j                  |       }t	        t
        t        | j                  j                                     |_        d|_	        |S )zReverse normal operator -NzInvalid Shape)
_wrapped
ValueErrorcopydeepcopytcastrl   rt   wrappedComplementedtopo_parent)r   new_surfaces     r   __neg__zMixin2D.__neg__   sV    == _--mmD)#FHT\\5N5N5P,QR #'r   c                     y)z-split_by_perimeter and keep inside or outsideNr   r   	perimeterkeeps      r   split_by_perimeterzMixin2D.split_by_perimeter       r   c                     y)z.split_by_perimeter and keep inside and outsideNr   r   s      r   r   zMixin2D.split_by_perimeter   r   r   c                     y)z,split_by_perimeter and keep inside (default)Nr   r   s      r   r   zMixin2D.split_by_perimeter   r   r   c                    d fd} fd}dd}dd}|t         j                  t         j                  t         j                  hvrt	        d      |j
                  st	        d      t               } j                         D 	cg c]  }|j                  D ]  }	|	  }
}}	|j                         D ]  }|sg }|
D ]W  }	|j                  |	      }||j                         D ].  t        fd|j                         D              s& ||       0 Y  |||      D ]  }|j                  |j                           t         j                        }|j!                  |       |j#                           ||j%                               } ||j'                               } ||      } ||      }|j(                  }|r t+        d |j                         D              nd	}|r t+        d
 |j                         D              nd	}t-        ||z
        t-        ||z
        k  }|t         j                  k(  r
|r||fS ||fS |t         j                  k(  r|r|S |S |r|S |S c c}	}w )aa  split_by_perimeter

        Divide the faces of this object into those within the perimeter
        and those outside the perimeter.

        Note: this method may fail if the perimeter intersects shape edges.

        Args:
            perimeter (Union[Edge,Wire]): closed perimeter
            keep (Keep, optional): which object(s) to return. Defaults to Keep.INSIDE.

        Raises:
            ValueError: perimeter must be closed
            ValueError: keep must be one of Keep.INSIDE|OUTSIDE|BOTH

        Returns:
            Union[Face | Shell | ShapeList[Face] | None,
            Tuple[Face | Shell | ShapeList[Face] | None]: The result of the split operation.

            - **Keep.INSIDE**: Returns the inside part as a `Shell` or `Face`, or `None`
              if no inside part is found.
            - **Keep.OUTSIDE**: Returns the outside part as a `Shell` or `Face`, or `None`
              if no outside part is found.
            - **Keep.BOTH**: Returns a tuple `(inside, outside)` where each element is
              either a `Shell`, `Face`, or `None` if no corresponding part is found.

        c                    g }t        | j                               D ]\  }| j                         }|j                         s*|j	                  j
                  j                  |             | j                          ^ |S )z0Return objects from TopTools_ListOfShape as list)rangeSizeFirstIsNullappend	__class__r   RemoveFirst)losshapes_firstr   s       r   getz'Mixin2D.split_by_perimeter.<locals>.get  sb    F388:& "		||~MM$.."5"5e"<=!	"
 Mr   c                    | syt        |       dk(  r| d   S t        | D cg c]  }|j                   c}      }t        |t              rj
                  j                  |      S t        |       S c c}w )zgProcess sides to determine if it should be None, a single element,
            a Shell, or a ShapeList.Nrg   r   )lenrp   r   
isinstancerP   r   r   rn   )sidesspotential_shellr   s      r   process_sidesz1Mixin2D.split_by_perimeter.<locals>.process_sides&  sh     5zQQx/E0Jq0JKO/<8~~**?;;U## 1Ks   A1c                   | g}|D ]}  g }|D ]r  }|j                        t        kD  s"t        fd|j                         D              r|j	                  |       Ot        |      }|j                  d |D               t |} |S )z-Split an edge at all given interior vertices.c              3  N   K   | ]  }j                  |      t        k    y wNdistance_tor_   .0edge_vertexvertexs     r   	<genexpr>zMMixin2D.split_by_perimeter.<locals>.split_edge_at_vertices.<locals>.<genexpr>9  s+      F' **;79DF   "%c              3  2   K   | ]  }t        |        y wr   )rh   )r   
split_edges     r   r   zMMixin2D.split_by_perimeter.<locals>.split_edge_at_vertices.<locals>.<genexpr>@  s     (Xjj)9(X   )r   r_   anyverticesr   rk   extend)edger   segmentsnext_segmentssegmentsplit_edgesr   s         @r   split_edge_at_verticesz:Mixin2D.split_by_perimeter.<locals>.split_edge_at_vertices3  s    vH" ) "' YG**62Y># F+2+;+;+=F C &,,W5 "7"HK!(((XK(XXY )) Or   c                R    t        fd| D              r| j                         yy)z7Add vertex if it isn't already represented in the list.c              3  N   K   | ]  }j                  |      t        kD    y wr   r   )r   existingr   s     r   r   zHMixin2D.split_by_perimeter.<locals>.add_unique_vertex.<locals>.<genexpr>F  s!     U6%%h/);Ur   N)allr   )r   r   s    `r   add_unique_vertexz5Mixin2D.split_by_perimeter.<locals>.add_unique_vertexD  s#    UHUU' Vr   z;keep must be one of Keep.INSIDE, Keep.OUTSIDE, or Keep.BOTHz'perimeter must be a closed Wire or Edgec              3  N   K   | ]  }j                  |      t        kD    y wr   r   r   s     r   r   z-Mixin2D.split_by_perimeter.<locals>.<genexpr>\  s)      ' **;7)Cr   c              3  4   K   | ]  }|j                     y wr   lengthr   es     r   r   z-Mixin2D.split_by_perimeter.<locals>.<genexpr>p  s     #CAHH#C   r   c              3  4   K   | ]  }|j                     y wr   r   r   s     r   r   z-Mixin2D.split_by_perimeter.<locals>.<genexpr>q  s     $E!QXX$Er   )r   rS   returnlist)r   rh   r   list[Vertex]r   z
list[Edge])r   r   r   r{   r   None)r[   INSIDEOUTSIDEBOTHr   	is_closedrT   facesseamsedges	intersectr   r   Appendr   r   AddBuildLeftRightr   sumabs)r   r   r   r   r   r   r   perimeter_edgesfaceseamr   perimeter_edgeseam_verticesseam_intersectionr   constructorleftsrightsleftrightperimeter_lengthleft_perimeter_lengthright_perimeter_lengthleft_insider   s   `                       @r   r   zMixin2D.split_by_perimeter   sg   :		$	"	(
 T\\499==M 
 ""FGG24"&**,F$4::F4FFF'oo/ 	;N!*,M 	A$2$<$<T$B!$,/88: AF +9+B+B+D  *-@A		A 5^]S ;
&&z'9'9:;	;$ *$,,7($'(8(8(:$;%():):)<%=U#f% %++GK#Cdjjl#C CQRIN$Eu{{}$E!ETU*-BBCc55G
 
 499$/D%=BeT]B4;;&41E1#u--Q Gs   I!c           	     <   | j                   g S t        |j                        j                         }t	               }|j                  | j                  ||       g }|j                         r|j                         }t        |      j                  t        |            j                  }|j                  |j                         t        |      |f       |j                          |j                         r|j                  d        |D cg c]  }|d   	 }	}|D cg c]  }|d   	 }
}t!        |	      D cg c]"  \  }}t#        ||
|   j%                               $ }}}g }t'        |
|      D ]  \  }}|j                  ||f        |S c c}w c c}w c c}}w )a4  Find point and normal at intersection

        Return both the point(s) and normal(s) of the intersection of the axis and the shape

        Args:
            axis (Axis): axis defining the intersection line

        Returns:
            list[tuple[Vector, Vector]]: Point and normal of intersection
        c                    | d   S )Nr   r   )xs    r   <lambda>z2Mixin2D.find_intersection_points.<locals>.<lambda>  s
    1 r   keyr   rg   )r   r&   r   Valuer   InitMorePntrd   to_local_coordsre   Zr   r   Nextsort	enumeraters   to_pntzip)r   other	toleranceintersection_lineintersect_makerintersectionsinter_ptdistanceiintersecting_facesintersecting_pointsfintersecting_normalsresultpntnormals                   r   find_intersection_pointsz Mixin2D.find_intersection_points  s    == I'6<<>35T\\+<iH""$&**,HU|33F84DEGGH  #((*8$   " ""$ 	~.,9:qad::-:;qt;; ""45 
1 #1&9!&<&C&C&EF 
  
 24HI 	)KCMM3-(	)  ;; 
s   FF9'Fc           	     .   t        |t              rt        |      }n]t        |t              rt        |j                        }n7t        |t
              rt        |      }nt        |t              rt        |      }	 	 	 	 	 	 dfd}t               }t        |t              re|j                  rY| j                  d      }|j                  |j                  |j                         |j                         z
  j                  z         }t        |t        t         f      r| j#                  | f|ft%                     }|j'                         }|j)                  |       | j#                  | f|ft+                     }	t        |	D 
cg c]  }
t        |
t              s|
 c}
      j'                         }|s|j)                  |       nt               }|D ]!  }|j)                  |j-                                # |j)                   |||             nt        |t        t.        f      r/| j#                  | f|ft+                     }	|j)                  |	       n\t        |t              r&|j1                  |       k  r8|j3                  |       n&|j5                  | |      }|r|j)                  |       |r]t        |t        t         f      rGt        d |D              }t        d |D              }|j)                  | j7                  |||             |r|S dS c c}
w )u>  Single-object intersection for Face/Shell.

        Returns same-dimension overlap or crossing geometry:
        - 2D + 2D → Face (coplanar overlap) + Edge (crossing curves)
        - 2D + Edge → Edge (on surface) + Vertex (piercing)
        - 2D + Solid/Compound → delegates to other._intersect(self)

        Args:
            other: Shape or geometry object to intersect with
            tolerance: tolerance for intersection detection
            include_touched: if True, include boundary contacts
                (only relevant when Solids are involved)
        c                   	 | D cg c]  }||j                  d      f }}|D cg c]  }||j                  d      f }}t               }|D ]/  \  	t        	
fd|D              }|r|j                         1 |S c c}w c c}w )zGFilter section edges, keeping only edges not on common face boundaries.Foptimalc              3  t   K   | ]/  \  }}j                  |      xr j                  |      k   1 y wr   )overlapsr   )r   cece_bboxr   	edge_bboxr  s      r   r   z;Mixin2D._intersect.<locals>.filter_edges.<locals>.<genexpr>  sI        $G &&w	: :((,	9: s   58)bounding_boxrn   r   r   )section_edgescommon_edgesr   section_bboxesr1  common_bboxesfiltered	is_commonr   r3  r  s           @@r   filter_edgesz(Mixin2D._intersect.<locals>.filter_edges  s    
 KXXQq!..."?@XNX?K9;R__U_34M 
 #,+H#1 *i   (5  	
 !OOD)* O Ys
   BBFr-  c              3  B   K   | ]  }t        |t              s|  y wr   )r   r   r   rs     r   r   z%Mixin2D._intersect.<locals>.<genexpr>       #N!*Q:MA#N   c              3  B   K   | ]  }t        |t              s|  y wr   r   rh   r=  s     r   r   z%Mixin2D._intersect.<locals>.<genexpr>  r?  r@  N)r5  ShapeList[Edge]r6  rC  r   rC  )r   re   r{   rb   positionr`   rh   rd   r   rn   is_infiniter4  trim_infinitediagonalcenterr   r   _bool_op_listr   expandr   r   r   rj   r   r   
_intersecttouch)r   r  r  include_touchedr;  resultsbboxcommoncommon_facessectionr   r5  r6  r   r'  found_facesfound_edgess     `              r   rK  zMixin2D._intersect  s   ( eV$5MEx(5>>*Et$KEu%KE	*	:I		, '[ eT"u'8'8$$U$3D''$++-!? G GGE
 edE]+''%;M;OPF!==?LNN<( (($5(<O<QRG%#;qz!T':;fh   }- 1:( 6D ''

56|M<HI d|,(($5(<O<QRGNN7# v&  &)3u% %%dIGFv& z%$?##Nw#NNK##Nw#NNKNN4::eY[QR!w+t+I <s    LLNc                <   dfd}d	fd}d
d}t               }t        |t        t        f      rH|vt               }t               }| j	                  |d      }	|	rX|	D ]F  }
t        |
t              r|j                  |
       %t        |
t              s6|j                  |
       H n|
t               }t               }t               }|j                  dz         |j                  | j                         |j                  |j                         |j                          |j                         rC|j                         k  r/t        d|j!                         dz         D ]  }|j#                  |      }|j%                  |      }|j'                  |      kD  r;t)        |j+                         |j-                         |j/                               } |||      r| ||| j1                               rF |||j1                               r/ ||| j3                               s |||j3                               s |||      r |||      r|j                  |       |j                  |        |S |j5                  |j7                  |              |S )u  Find boundary contacts between this 2D shape and another shape.

        Returns the highest-dimensional contact at each location, filtered to
        avoid returning lower-dimensional boundaries of higher-dimensional contacts.

        For Face/Shell:
        - Face + Face → Vertex (shared corner or crossing point without edge/face overlap)
        - Face + Edge/Vertex → no touch (intersect already returns dim 0)

        Args:
            other: Shape to find contacts with
            tolerance: tolerance for contact detection
            found_faces: pre-found faces to filter against (from Mixin3D.touch)
            found_edges: pre-found edges to filter against (from Mixin3D.touch)

        Returns:
            ShapeList of contact shapes (Vertex only for 2D+2D)
        c                0     t         fd|D              S )Nc              3  F   K   | ]  }j                  |      k    y wr   r   )r   r   r  vs     r   r   z9Mixin2D.touch.<locals>.vertex_on_edges.<locals>.<genexpr><       Dq}}Q'94D   !r   )rY  r   r  s   ` r   vertex_on_edgesz&Mixin2D.touch.<locals>.vertex_on_edges;      DeDDDr   c                0     t         fd|D              S )Nc              3  F   K   | ]  }j                  |      k    y wr   rX  )r   r%  r  rY  s     r   r   z9Mixin2D.touch.<locals>.vertex_on_faces.<locals>.<genexpr>?  rZ  r[  r\  )rY  r   r  s   ` r   vertex_on_facesz&Mixin2D.touch.<locals>.vertex_on_faces>  r^  r   c                B    t        |       t        fd|D              S )Nc              3  :   K   | ]  }t        |      k(    y wr   )re   )r   ovvecs     r   r   z6Mixin2D.touch.<locals>.is_duplicate.<locals>.<genexpr>C  s     <RsfRj(<s   )re   r   )rY  r   re  s     @r   is_duplicatez#Mixin2D.touch.<locals>.is_duplicateA  s    )C<8<<<r   F)rM  MbP?rg   )rY  r{   r   zIterable[Edge]r   bool)rY  r{   r   Iterable[Face]r   rh  )rY  r{   r   Iterable[Vertex]r   rh  )rn   r   r   r   rK  r   rh   r   SetDeflectionLoadS1r   LoadS2PerformIsDoner  r   
NbSolutionPointOnShape1PointOnShape2Distancer{   XYr  r   r   r   rL  )r   r  r  rS  rT  r]  ra  rf  rN  intersect_resultsr>  found_verticesextremar"  pnt1pnt2
new_vertexs     `              r   rL  zMixin2D.touch   s=   6	E	E	= '[edE]+"'k'k$(OO9e %4 %! %. 2%a.'..q1'40'..q1	2
 $'k )2N02G!!D  NN4<<(NN5==)OO~~GMMOy$@q'"4"4"6":; :A"003D"003D}}T*Y6 !'$&&(DFFH!EJ $J?  (
DJJLA+JF ,Z I ,Z9I J  +"K-j+Fz2&--j97:D  NN5;;tY78r   c                     y)zA location from a face or shellNr   )r   argskwargss      r   location_atzMixin2D.location_at  r   r   c                ~    t        j                  |       j                  t        | j	                         |z              S )z6Return a copy of self moved along the normal by amount)r   r   movedrb   	normal_at)r   amounts     r   offsetzMixin2D.offset  s-    }}T"(($..2BV2K)LMMr   c                4    t        j                  | ||||      S )a  project_to_viewport

        Project a shape onto a viewport returning visible and hidden Edges.

        Args:
            viewport_origin (VectorLike): location of viewport
            viewport_up (VectorLike, optional): direction of the viewport y axis.
                Defaults to (0, 0, 1).
            look_at (VectorLike, optional): point to look at.
                Defaults to None (center of shape).
            focus (float, optional): the focal length for perspective projection
                Defaults to None (orthographic projection)

        Returns:
            tuple[ShapeList[Edge],ShapeList[Edge]]: visible & hidden Edges
        )ri   project_to_viewport)r   viewport_originviewport_uplook_atfocuss        r   r  zMixin2D.project_to_viewport  s"    . **/;
 	
r   c                     	 	 	 	 	 	 d fd	 	 	 	 	 	 	 	 d fd} j                   t        d       j                  t        j                         |j
                  j                  |j                  }d}d}d}	t        j                  j                  }
|j                  d      j                  |kD  rCt        j                  d|dz        } j                  ||d|	      }|d
z  } || z
        \  }}n"|j                  }|j                   j                  }|
|kD  r|	|k  rg }||}}|j#                  |       t%        d
|t'        |j(                         z         D ]P  }|j                  |d
z
  |z        }|j                  ||z        }||z
  } ||||      \  }}|j#                  |       R t        j*                  ||j(                        }t-        ||j                  z
        }
|dz  }|	d
z  }	|
|kD  r|	|k  r|
|kD  rt/        d|
dd|       r|j0                  st/        d      |s|S t3        j4                   j6                        }|j9                  d      }|j9                  d
      }t3        j:                  |j6                  ||      }t=        j>                  ||      }|t/        d      t        tA        |      j                               }|S )a  _wrap_edge

        Helper method of wrap that handles wrapping edges on surfaces (Face or Shell).

        Args:
            planar_edge (Edge): edge to wrap around surface
            surface_loc (Location): location on surface to wrap
            snap_to_face (bool,optional): ensure wrapped edge is tight against surface.
                Defaults to True.
            tolerance (float, optional): maximum allowed length error during initial wrapping
                operation. Defaults to 0.001

        Raises:
            RuntimeError: wrapping over surface boundary, try difference surface_loc
        Returns:
            Edge: wrapped edge
        c                     t         |      }j                  |      j                   fd      }|d   }|j                  |      }|st	        d      t        | fd      S )z`Return the intersection point and normal of the closest surface face
            along directionc                &    | j                        S r   rX  )r%  points    r   r  zGMixin2D._wrap_edge.<locals>._intersect_surface_normal.<locals>.<lambda>  s    !--. r   r   z:wrapping over surface boundary, try difference surface_locc                &    t        | d   z
        S )Nr   )r   )pairr  s    r   r  zGMixin2D._wrap_edge.<locals>._intersect_surface_normal.<locals>.<lambda>  s    s47U?/C r   r  )r`   faces_intersected_by_axissort_byr*  RuntimeErrormin)r  r   axisr   r   interr   s   `     r   _intersect_surface_normalz5Mixin2D._wrap_edge.<locals>._intersect_surface_normal  sm    
 y)D2248@@.E 8D11$7E"P  u"CDDr   c                Z    t        | |      }|j                  |      } ||z
        S )zBProject a 2D offset from a local surface frame onto the 3D surfaceoriginx_dirz_dir)rd   from_local_coords)current_pointr)  relative_positionlocal_planeworld_pointr  surface_x_directiontarget_object_centers        r   _find_point_on_surfacez2Mixin2D._wrap_edge.<locals>._find_point_on_surface  sC      $)K
 &778IJK,[+?? r   zCan't wrap around an empty face   
   r   )r   r   T)snap_to_facer  rg   )periodicr   zLength error of z.6fz exceeds tolerance zWrapped edge is invalidz7Projection failed, try setting `snap_to_face` to False.)r  re   r   re   r   tuple[Vector, Vector])r  re   r)  re   r  re   r   r  )!r   r   rH  rX   BOUNDING_BOXx_axisr   r   sys
float_infomaxposition_atrh   	make_line
_wrap_edgerD  z_axisr   r   intr   make_spliner   r  is_validr   	Surface_sr   param_atCurve_sr6   	Project_sr   )!r   planar_edgesurface_locr  r  r  planar_edge_lengthsubdivisions	max_loops
loop_countlength_errorto_start_edgewrapped_to_start_edge	start_pntr   start_normalwrapped_edge_pointsr  current_normaldivprevcurrr  wrapped_edgesurface_handlefirst_param
last_paramcurve_handleproj_curve_handleprojected_edger  r  r  s!   `                             @@@r   r  zMixin2D._wrap_edge  s:   2	E	E&,	E"	E"	!	+1	FL	"	 == >??  ${{8+@+@A)00::(// 	
~~)) ""1%,,y8 NN6;?CM$(OO{ %4 %! .1I7I(<<OA|
 $,,I&--77LY&:	+A46,5|>M&&}5 Qs{7L7L3L/M MN :"..a</GH"..s\/AB0F!>61-~ $**=9:  ++#k.C.CL 1L4G4GGHLAL!OJ/ Y&:	+A2 )#"<"44G	{S  <#8#8899 #,,T\\:)2215(11!4
 (()=)={JW'11,O$I 
 56GHMMOPr   )r   r  )r   rO   r   z#Vertex | Edge | Wire | Face | Shell)r   rm   r   rf   r   z&Edge | Face | Shell | Solid | Compound)r   rV   )r   Edge | Wirer   z"Literal[Keep.INSIDE, Keep.OUTSIDE]r   %Face | Shell | ShapeList[Face] | None)r   r  r   zLiteral[Keep.BOTH]r   zStuple[Face | Shell | ShapeList[Face] | None, Face | Shell | ShapeList[Face] | None])r   r  r   zLiteral[Keep.INSIDE]r   r  )r   r  r   r[   )r  r`   r  floatr   zlist[tuple[Vector, Vector]])ư>F)r  z(Shape | Vector | Location | Axis | Planer  r  rM  rh  r   ShapeList | None)r  NN)
r  rm   r  r  rS  r  rT  r  r   rn   )r}  r
   r~  r
   r   rb   )r  r  r   rV   ))r   r   rg   NN)
r  rf   r  rf   r  VectorLike | Noner  zfloat | Noner   z'tuple[ShapeList[Edge], ShapeList[Edge]])Trg  )
r  rh   r  rb   r  rh  r  r  r   rh   )__name__
__module____qualname____doc__propertyr   classmethodr   r   r   r   r   r[   r   r_   r*  rK  rL  r   r  r  r  r  r   r   r   r   r      s=   ;   : :  $.	/ 
 <$<,N<	.< <
 =$=,>=
= = CG;;;$;,@;	.; ;
 GKkk {.L /8--&+-	$-d   %	k,7k, k, 	k,
 
k,`  (,(,ee e &	e
 &e 
eN . .N #,%)"
#
  
 #	

 
 
1
> " II I 	I
 I 
Ir   r   c                  \    e Zd ZdZdZe	 	 	 dH	 	 	 	 	 	 	 dId       Ze	 	 	 	 dJ	 	 	 	 	 	 	 	 	 dKd       ZdL fdZedMd       ZedNd       Z	edOd	       Z
edPd
       ZedQd       ZedMd       ZedRd       ZedRd       ZedSd       ZedTd       ZedUd       ZedTd       ZedVd       ZedTd       ZedWd       ZedMd       ZedTd       ZedXd       Ze	 dY	 	 	 	 	 dZd       Ze	 d[	 	 	 	 	 	 	 d\d       Ze ed      ej>                  f	 	 	 d]d              Z eej>                  fd^d       Z!e"d_d       Z#e	 	 	 	 	 	 d`d       Z$e	 	 	 	 	 	 dad        Z%e	 	 db	 	 	 	 	 	 	 dcd!       Z&e	 	 	 	 dd	 	 	 	 	 	 	 	 	 	 	 ded"       Z'ee	 	 	 	 	 	 dfd#              Z(ee	 	 	 	 	 	 dgd$              Z(edWd%       Z(e	 	 	 dh	 	 	 	 	 	 	 did&       Z)e	 	 	 	 	 	 	 	 djd'       Z*edkd(       Z+ee,jZ                  f	 	 	 	 	 dld)       Z.e/j`                  fdmd*Z1	 dY	 	 	 	 	 	 	 	 	 dnd+Z2dod,Z3dpd-Z4dqd.Z5drd/Z6dsdtd0Z7e	 dYdd1	 	 	 	 	 dud2       Z8edd1	 	 	 	 	 	 	 dvd3       Z8dPd4Z8dwd5Z9edYdxd6       Z:edyd7       Z:dzd8Z:d{d9Z;dyd:Z<	 	 	 	 	 	 d|d;Z= ed<      d}d~d=       Z>dWd>Z?d{d?Z@e	 	 d	 	 	 	 	 	 	 	 	 dd@       ZAe	 	 d	 	 	 	 	 	 	 	 	 ddA       ZAe	 	 d	 	 	 	 	 	 	 	 	 ddB       ZA	 	 d	 	 	 	 	 	 	 	 	 ddCZA	 d	 	 	 	 	 	 	 ddDZBddEZC	 	 d	 	 	 	 	 	 	 	 	 	 	 ddFZD	 	 d	 	 	 	 	 	 	 	 	 	 	 ddGZE xZFS )r   a  A Face in build123d represents a 3D bounded surface within the topological data
    structure. It encapsulates geometric information, defining a face of a 3D shape.
    These faces are integral components of complex structures, such as solids and
    shells. Face enables precise modeling and manipulation of surfaces, supporting
    operations like trimming, filleting, and Boolean operations.g       @Nc                     y)aF  Build a Face from an OCCT TopoDS_Shape/TopoDS_Face

        Args:
            obj (TopoDS_Shape | Plane, optional): OCCT Face or Plane.
            label (str, optional): Defaults to ''.
            color (Color, optional): Defaults to None.
            parent (Compound, optional): assembly parent. Defaults to None.
        Nr   )r   r   labelcolorparents        r   __init__zFace.__init__C  r   r   c                     y)a  Build a planar Face from a boundary Wire with optional hole Wires.

        Args:
            outer_wire (Wire): closed perimeter wire
            inner_wires (Iterable[Wire], optional): holes. Defaults to None.
            label (str, optional): Defaults to ''.
            color (Color, optional): Defaults to None.
            parent (Compound, optional): assembly parent. Defaults to None.
        Nr   )r   
outer_wireinner_wiresr  r  r  s         r   r  zFace.__init__T  r   r   c                   d\  }}}}}}|rrt        |      }	t        |d   t              r|d   }nNt        |d   t              r|d d dd|	z
  z  z   \  }}}}n't        |d   t              r|d d dd|	z
  z  z   \  }}}}}dj                  t        |j                               j                  g d            }
|
rt        d|
       |j                  d	|      }|j                  d
|      }|j                  d|      }|j                  d|      }|j                  d|      }|j                  d|      }t        |t              r#t        |j                        j                         }|_||D cg c]  }|j                   c}ng }t        d |j                  g|z   D              rt        d      t        |j                  |      }t         | E  ||dn|||       d | _        y c c}w )N)NNNNNNr      r      , )r  r  r   r  r  r  Unexpected argument(s) r   r  r  r  r  r  c              3  H   K   | ]  }t        j                  |         y wr   )r   
IsClosed_s)r   ws     r   r   z Face.__init__.<locals>.<genexpr>  s&       ((++s    "z*Face can only be created with closed wires r   r  r  r  )r   r   rd   rO   rj   joinsetkeys
differencer   r   r   r   r   r   ry   superr  
created_on)r   r}  r~  r  r  r   r  r  r  l_aunknown_argsr  inner_topods_wiresr   s                r   r  zFace.__init__g  s   =H:
KeUFd)C$q'5)1gDG\2,0!Hw!c'7J,J)UE6DGT*@DRa7GL A=
Kv yy))	
 6|nEFFjj$ZZj9
jj<

7E*

7E*Hf-c5!)#++6;;=C!4?4KK0q0QS   $,,-0BB  !!MNN.z/A/ACUVC"5	 	 	
 )-! 1s   /G'c                P    | j                   y| j                         j                  S )a`  
        Calculate the total surface area of the face, including the areas of any holes.

        This property returns the overall area of the face as if the inner boundaries (holes)
        were filled in.

        Returns:
            float: The total surface area, including the area of holes. Returns 0.0 if
            the face is empty.
                )r   without_holesarear   s    r   area_without_holeszFace.area_without_holes  s&     == !!#(((r   c                X   | j                         }t        |t        t        f      r'|j	                         }t        |t        t        f      r't
        j                  t        |      j                            }|xt        j                  k(  r( t        |j                         j                               S xt        j                  k(  r( t        |j                         j                               S xt        j                  k(  rB |j!                         }t        t#        |j%                         |j'                                     S xt        j(                  k(  r( t        |j+                         j                               S t        j,                  k(  rt        |j                               S 	 y)z.Get the rotational axis of a cylinder or torusN)geom_adaptorr   r*   r)   BasisSurfacerm   geom_LUT_FACEr1   GetTyperZ   CONEr`   ConeCYLINDERCylinderSPHEREPositionr7   rb   	DirectionTORUSTorus
REVOLUTION)r   surf	geom_typeax3s       r   axis_of_rotationzFace.axis_of_rotation  s)   
 "..0  >@RST$$&D  >@RST ''(;D(A(I(I(KL	 DIIK,,.//"""DMMO00233 mmoF3<<>3==?CDDDJJL--/00$$DIIK((r   c                  !" | j                   t        d      | j                  st        d      | j                         }| j	                         }| j                         }|r|D cg c]  }t        |       }}t        j                  |      }t        ||z
        t        kD  r||z
  j                         g}n@|D cg c]#  }|j                         |z
  j                         % }}n| j                         j                         j                  t        j                  d      }	| j                         j                         }
|	rxt!        |       }|j"                  }t%        t'        d      D cg c]$  }t)        j*                  ||   ||dz   dz           & c}      }|
|z   D cg c]  }dD ]  }||z  	  }}}n|
D cg c]  }dD ]  }||z  	  }}}|D cg c]  }||z
  j                          }}t-               }|D ]  !t/        |!!j1                  |            "| j3                  "t4        j6                  	      \  }}t9        |      t9        |      k7  r]||bt%        t;        |t<              r|n|g      }t%        t;        |t<              r|n|g      }t?        |      t?        |      k7  r|jA                  tC        |!            }t%        "fd
|D              jA                  tC        |!            }tE        d |D              }tG        ||      D ]<  \  }}|jI                  |      }|d} n"tE        d |jK                         D              }> dk(  rbt        ||z
        t        k  sy|s|jM                  !       tO        !fd|D              }|r|jM                  !        |D cg c]  }tC        ||       } }| S c c}w c c}w c c}w c c}}w c c}}w c c}w c c}w )u  Computes and returns the axes of symmetry for a planar face.

        The method determines potential symmetry axes by analyzing the face’s
        geometry:

        - It first validates that the face is non-empty and planar.

        - For faces with inner wires (holes), it computes the centroid of the
          holes and the face's overall center (COG).

            - If the holes' centroid significantly deviates from the COG (beyond
              a specified tolerance), the symmetry axis is taken along the line
              connecting these points; otherwise, each hole’s center is used to
              generate a candidate axis.

        - For faces without holes, candidate directions are derived by sampling
          midpoints along the outer wire's edges.

            - If curved edges are present, additional candidate directions are
              obtained from an oriented bounding box (OBB) constructed around the
              face.

        For each candidate direction, the face is split by a plane (defined
        using the candidate direction and the face’s normal).  The top half of the face
        is then mirrored across this plane, and if the area of the intersection between
        the mirrored half and the bottom half matches the bottom half’s area within a
        small tolerance, the direction is accepted as an axis of symmetry.

        Returns:
            list[Axis]: A list of Axis objects, each defined by the face's
                center and a direction vector, representing the symmetry axes of
                the face.

        Raises:
            ValueError: If the face or its underlying representation is empty.
            ValueError: If the face is not planar.
        z.Can't determine axes_of_symmetry of empty facez/axes_of_symmetry only supports for planar facesT)reverser  rg   )r        ?      ?r  )r   c              3  @   K   | ]  }|j                          y wr   )mirror)r   r%  split_planes     r   r   z(Face.axes_of_symmetry.<locals>.<genexpr>9  s      )*+%)s   c              3  4   K   | ]  }|j                     y wr   r  r   r%  s     r   r   z(Face.axes_of_symmetry.<locals>.<genexpr>=  s     :aff:r         c              3  4   K   | ]  }|j                     y wr   r  r  s     r   r   z(Face.axes_of_symmetry.<locals>.<genexpr>C  s     $JQVV$Jr   c              3  T   K   | ]  }|j                        d t        z   k   ! yw)N)dotr_   )r   d	cross_dirs     r   r   z(Face.axes_of_symmetry.<locals>.<genexpr>M  s(      #>?i(2	>9#s   %()(r   r   	is_planarrH  r  r  r   combined_centerr   r_   
normalizedr  r   	filter_byrZ   LINErc   cornersrn   r   rh   r  r  rd   crosssplitr[   r   typer   r   r   r  r`   r   r  r   r   addr   )#r   cogr)  shape_inner_wiresr  
hole_facesholes_centroid
cross_dirsr%  curved_edgesshape_edgesobbr'  r"  	obb_edgesr   p
mid_points	mid_pointsymmetry_dirstopbottomtop_listbottom_listtop_flipped_listbottom_areaflipped_facebottom_faceintersectionintersect_areaoppositer   symmetry_axesr!  r  s#                                    @@r   axes_of_symmetryzFace.axes_of_symmetry  s   N == MNN~~NOOkkm! ,,.+<=a$q'=J=!11*=N >C'(94-3??AB
 HRR!qxxzC/;;=R
R !'')33HMM43P  //+113K&t,++%OTUVxX!T^^GAJQ!0DEX	 $/#:O?@AE
  .9R/RQa!eReR
RJTUY9s?668UJU%(U# /	5Ioof-K **[tyy*AKCCyDL({fn 
3(=C5IH#j.FFVHUK8}K 00%--d3	.BCK( )/7)  gd3	*+  :k::K-01A;-O K)k+55kB'%)N!$$J\5G5G5I$J!JK % >K/09<$!%%i0" #CP#  H $%)))4_/	5b 0==!c1==] > S Y SUh >s*   'O?(O!1)O&)O+O1O7O<c                r    | j                  dd      }t        || j                  |            j                  S )zLocation at the center of facer  )r  )r  rd   r  location)r   r  s     r   center_locationzFace.center_locationV  s2     !!#s+V4>>&#9:CCCr   c                P   d}| j                   rt        |       j                  |       }|j                         }t	        d |D              r|j                         }d}t        |      dk(  rg }|D ]b  }|j                  |D cg c]  }||j                         v s| c}       |D 	cg c]!  }|D 	cg c]  }	|	j                  d       c}	# }
}}	d t	        d 
D              r/d}t        |j                  t        j                              dk(  rd	}|S c c}w c c}	w c c}	}w )
zgeometry of planar faceNc              3  V   K   | ]!  }|j                   t        j                  k(   # y wr   )r  rZ   r&  r   s     r   r   z Face.geometry.<locals>.<genexpr>c  s     IA1;;(--/Is   ')POLYGONr  r   c              3  P   K   | ]  }|d    j                  |d         dk(     yw)r   rg   Z   N)	get_angle)r   edge_directionss     r   r   z Face.geometry.<locals>.<genexpr>o  s2      + (*44_Q5GHBNs   $&	RECTANGLErg   SQUARE)r"  rd   r  r   r   r   r   r   
tangent_atgroup_byr\   LENGTH)r   r'  	flat_faceflat_face_edgesflat_face_vertices
edge_pairsr   r   r  r   edge_pair_directionss              r   geometryzFace.geometry\  s$    >>#Dk99$?I'oo/OIII%.%7%7%9""'1,35J"4 "))(7R16QZZ\;QQR OY0FJDADT__Q/A0, 0	  /C  "-77FG1L%-F S B0s$   D
D
*	D"3DD"D"c                   | j                   t        j                  k(  rqt        | j	                         t
              sS| j                  }|t        d      | j                         j                  | j                         |j                  z
        S | j                   t        j                  k(  rS| j                  }|t        d      | j                         j                  | j                         |j                  z
        S | j                   t        j                  k(  r| j                  }|| j                  t        d      t!        t#        |            }t%        j&                  | j                  d         j)                  |      }|j+                  | j                               \  }}}| j                         j                  | j                         |z
        S y)a  
        Compute the signed dot product between the face normal and the vector from the
        underlying geometry's reference point to the face center.

        For a cylinder, the reference is the cylinder's axis position.
        For a sphere, it is the sphere's center.
        For a torus, we derive a reference point on the central circle.

        Returns:
            float: The signed value; positive indicates convexity, negative indicates concavity.
                Returns 0 if the geometry type is unsupported.
        z$Can't find curvature of empty objectr   r  )r  rZ   r  r   r  r*   r  r   r  r  rH  rD  r  rG  r
  radiirb   rd   rh   make_circlelocatedistance_to_with_closest_points)r   r  locaxis_circler   pnt_on_axis_circles         r   _curvature_signzFace._curvature_signy  st    >>X...z!?8
 ((D| !GHH>>#''(EFF>>X__,--C{ !GHH>>#''(DEE>>X^^+ ((D|tzz1 !GHH5;'C**4::a=9@@EK'2'R'R($A!1 >>#''8J(JKKr   c                (    | j                   t        kD  S )z
        Determine whether a given face is convex relative to its underlying geometry
        for supported geometries: cylinder, sphere, torus.

        Returns:
            bool: True if convex; otherwise, False.
        rc  r_   r   s    r   is_circular_convexzFace.is_circular_convex  s     ##i//r   c                *    | j                   t         k  S )z
        Determine whether a given face is concave relative to its underlying geometry
        for supported geometries: cylinder, sphere, torus.

        Returns:
            bool: True if concave; otherwise, False.
        re  r   s    r   is_circular_concavezFace.is_circular_concave  s     ##yj00r   c                T   t        j                  | j                        }t        |t              }|j                         sy|j                         }|j                         j                         s0t        t        |j                         j                                     }t        |      S )zRIs the face planar even though its geom_type may not be PLANE - if so return PlaneN)r   r  r   r5   r_   IsPlanarPlanr  Directr9   r8   Ax2rd   )r   surfaceplanar_searcherplns       r   r"  zFace.is_planar  s{     %%dll31'9E'')""$||~$$& 2 2 456CSzr   c                    d}| j                   rft        |       j                  |       }|j                         j	                  t
        j                        }|d   j                  |d   j                  z
  }|S )zlength of planar faceNr  r   )r"  rd   r  r   r  r`   rt  r   r'  rU  face_verticess       r   r   zFace.length  f     >>d33D9I%..088@M"2&((=+;+=+==Fr   c                    | j                   t        j                  k(  r<| j                         j	                         | j                         j                         fS y)z:Return the major and minor radii of a torus otherwise NoneN)r  rZ   r
  r  MajorRadiusMinorRadiusr   s    r   r\  z
Face.radii  sM     >>X^^+!!#//1!!#//1 
 r   c                    | j                   t        j                  t        j                  fv r<t	        | j                         t              s| j                         j                         S y)z9Return the radius of a cylinder or sphere, otherwise NoneN)r  rZ   r  r  r   r  r*   Radiusr   s    r   radiuszFace.radius  sS     >>h//AA*!?K
 $$&--//r   c                `     t                j                         j                   fd      S )z+Return the seams contained within this Facec                P    j                  | j                  j                        S r   )IsSeamr   )r   saer   s    r   r  zFace.seams.<locals>.<lambda>  s    

199dll0S r   )r>   r   r%  )r   r~  s   `@r   r   z
Face.seams  s%     !"zz|%%&STTr   c                    | j                   t        j                  k(  rEt        | j	                         t
              s't        | j	                         j                               S y)z/Return the semi angle of a cone, otherwise NoneN)r  rZ   r  r   r  r*   r   	SemiAngler   s    r   
semi_anglezFace.semi_angle  sL     >>X]]*:!?4
 4,,.88:;;r   c                B    t        t        j                  j                        j	                         }t        j                  |      d fddfd} | j                               } j                         D cg c]
  } ||       }}t	        ||      S c c}w )a  Create a planar face from a face's parametric-space boundary.

        Each boundary edge's pcurve on ``self`` is converted to a normal
        build123d ``Edge`` on the XY plane, where X is the surface U parameter and Y
        is the surface V parameter. The original outer/inner wire structure is kept
        so the result can be displayed with normal build123d/ocp-vscode tooling.

        Args:
            source_face: Planar or non-planar face to inspect.

        Returns:
            A planar ``Face`` in UV parameter space.
        c                   t        j                  | j                        \  }}t        j                  | j                  ||      }t	        |||      }|j                         st        d      |j                         }| j                         t        j                  k(  r#t        j                  |j                               }t        |      S )Nz)Unable to convert pcurve to a planar edge)r   Range_sr   CurveOnSurface_sr   ro  r   rh   OrientationrK   TopAbs_REVERSEDrM   Reversed)native_edger   lastpcurveedge_buildertopods_edger   
xy_surfaces         r   uv_edgezFace.uv_face.<locals>.uv_edge  s    #++KFKE4//T\\5RVWF26:udSL&&( !LMM&++-K&&(,>,N,NN$kk+*>*>*@A$$r   c                   t        | j                        }g }|j                         rY|j                   t	        j
                  |j                                            |j                          |j                         rYt        |      S r   )	r%   r   r  r   rM   rh   Currentr  rj   )source_wirewire_exploreruv_edgesr  s      r   uv_wirezFace.uv_face.<locals>.uv_wire  sm    2;3F3FGMH$$&M4I4I4K(L MN""$  $$& >!r   )r   rh   )r  rj   r   rj   )	r   rd   XYr   r   r   r  r  r  )r   xy_facer  r  wirer  r  r  s   `     @@r   uv_facezFace.uv_face  s     *%((*:*:;@@B((1

	%	" T__./
151A1A1CDwt}DDJ,, Es   ?Bc                     y)z6volume - the volume of this Face, which is always zeror  r   r   s    r   volumezFace.volume  s     r   c                    d}| j                   rft        |       j                  |       }|j                         j	                  t
        j                        }|d   j                  |d   j                  z
  }|S )zwidth of planar faceNr  r   )r"  rd   r  r   r  r`   ru  rr  s       r   widthz
Face.width#  rt  r   c                    |st        d      t        t        j                  t        |j                  |                  S )a/  extrude

        Extrude an Edge into a Face.

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

        Raises:
            ValueError: Unsupported class
            RuntimeError: Generated invalid result

        Returns:
            Face: extruded shape
        zCan't extrude empty object)r   r   rM   rw   r   r   s      r   r   zFace.extrude0  s3      9::FKK 5ckk9 MNOOr   c           	        t        |      dk  st        |d         dk  rt        d      t        |      dkD  st        |d         dkD  rt        d      |r?t        |      t        |      k7  st        |d         t        |d         k7  rt        d      t        dt        |      dt        |d               }t        |      D ]H  \  }}t        |      D ]5  \  }}|j	                  |dz   |dz   t        |      j                                7 J |rxt        dt        |      dt        |d               }t        |      D ]:  \  }}	t        |	      D ]'  \  }}
|j	                  |dz   |dz   t        |
             ) < t        ||      }nt        |      } | t        |t        j                               j                               S )u  make_bezier_surface

        Construct a Bézier surface from the provided 2d array of points.

        Args:
            points (list[list[VectorLike]]): a 2D list of control points
            weights (list[list[float]], optional): control point weights. Defaults to None.

        Raises:
            ValueError: Too few control points
            ValueError: Too many control points
            ValueError: A weight is required for each control point

        Returns:
            Face: a potentially non-planar face
        r   r   z9At least two control points must be provided (start, end)   z*The maximum number of control points is 25z0A weight must be provided for each control pointrg   )r   r   rG   r  SetValuere   r  rJ   r  r'   r   r=   Confusion_sr   )r   pointsweightspoints_r"  
row_pointsjr  weights_row_weightsweightbeziers               r   make_bezier_surfacezFace.make_bezier_surfaceD  s   , v;?c&)nq0K  v;s6!9~2IJJK3w<'3vay>S_+LOPP%aVaVAYH&v. 	GMAz%j1 G5  QAve}/C/C/EFG	G ,QGaWQZQH"+G"4 C;!*;!7 CIAv%%a!eQUE&MBCC (:F'0F*693H3H3JKPPRSSr   c                  	 	 d	 	 	 	 	 dd	d	fd}|D cg c]
  } ||       }}|D cg c]
  } ||       }}t        |||      } | t        |t        j                               j	                               S c c}w c c}w )a  
        Constructs a Gordon surface from a network of profile and guide curves.

        Requirements:
        1. Profiles and guides may be defined as points or curves.
        2. Only the first or last profile or guide may be a point.
        3. At least one profile and one guide must be a non-point curve.
        4. Each profile must intersect with every guide.
        5. Both ends of every profile must lie on a guide.
        6. Both ends of every guide must lie on a profile.

        Args:
            profiles (Iterable[VectorLike | Edge]): Profiles defined as points or edges.
            guides (Iterable[VectorLike | Edge]): Guides defined as points or edges.
            tolerance (float, optional): Tolerance used for surface construction and
                intersection calculations.

        Raises:
            ValueError: input Edge cannot be empty.

        Returns:
            Face: the interpolated Gordon surface
        c                N   t        dd      }|j                  d|        |j                  d|        t        dd      }|j                  dd       |j                  dd       t        dd      }|j                  d|dz          |j                  d|dz          t	        ||||      }|S )Nrg   r   r  r  )rF   r  rI   rH   r(   )r  degreecontrol_pointsknotsmultiplicitiescurves         r    create_zero_length_bspline_curvezBFace.make_gordon_surface.<locals>.create_zero_length_bspline_curve  s     015N##Au-##Au-(A.ENN1c"NN1c"4Q:N##Avz2##Avz2%ne^VTELr   c                F   t        | t        t        t        f      r6t        |       } t	        |j
                  j                                     }|S | st        d      t        | j
                        }t        j                  | j
                  dd      }|j                         r|j                         sk|j                         t        j                  k(  sJ|j                         t        j                   k(  s)t#        ||j%                         |j'                               }|S )Nzinput Edge cannot be emptyr   rg   )r   re   tupler   r:   r   XYZr   r   r   r  
IsPeriodicIsClosedr  r.   GeomAbs_BSplineCurveGeomAbs_BezierCurver,   FirstParameterLastParameter)shape_shapesingle_point_curveadaptorr  r  s        r   to_geom_curvez/Face.make_gordon_surface.<locals>.to_geom_curve  s    %&%!:;%E6>>--/0&" *) !=>>'6G%%emmQ:E##%'*:*:*<??$(9(N(NN??$(9(M(MM)7113W5J5J5L Lr   )r  )rg   )r  r:   r  r  r   r(   )r  zVectorLike | Edge)rU   r   r=   r  r   )
r   profilesguidesr  r  r  ocp_profiles
ocp_guidesgordon_bspline_surfacer  s
            @r   make_gordon_surfacezFace.make_gordon_surfaceu  s    @ *+		#&		$	. ;CCe,CC8>?umE*?
?!:*	"
 #&	(=(=(?df
 	
 D?s
   A=BzNThe 'make_plane' method is deprecated and will be removed in a future version.c                X    t        |j                        j                         } | |      S )z/Create a unlimited size Face aligned with planer   r   r   )r   plane	pln_shapes      r   
make_planezFace.make_plane  s%     ,EMM:??A	9~r   c                |    t        |j                  | dz  |dz  | dz  |dz        j                         } | |      S )aT  make_rect

        Make a Rectangle centered on center with the given normal

        Args:
            width (float, optional): width (local x).
            height (float, optional): height (local y).
            plane (Plane, optional): base plane. Defaults to Plane.XY.

        Returns:
            Face: The centered rectangle
        r  r  )r   r  heightr  r  s        r   	make_rectzFace.make_rect  sG     ,MME6C<vgmVc\

$& 	 9~r   c                R   t        | t              r| nt        | t              rt        |       n| }t        |t              r|j	                         }n9t        |t              rt        d |D              rt        |      }nt        d      t        d |D              rt        d      |S )z>Normalize and validate the exterior boundary for make_surface.c              3  <   K   | ]  }t        |t                y wr   rB  )r   os     r   r   z/Face._surface_exterior_edges.<locals>.<genexpr>  s      ?
$%Jq$?
s   z(exterior must be a Wire or list of Edgesc              3  "   K   | ]  }|  	 y wr   r   )r   r   s     r   r   z/Face._surface_exterior_edges.<locals>.<genexpr>  s     2D4x2s   zexterior contains empty edges)	r   rj   r   r   r   r   rn   r   r   )exteriornormalized_exterioroutside_edgess      r   _surface_exterior_edgeszFace._surface_exterior_edges  s    
 (D)  (H- h 	 )40/557M+X63 ?
)<?
 <
 &&9:MGHH2M22<==r   c                    	 |j                           | |j                               S # |$ r}t        |      |d}~ww xY w)z3Build a filling surface and convert it into a Face.N)r   rm   r  )r   rn  error_messagefailure_exceptionserrs        r   _build_surface_facezFace._build_surface_face  s>    	7MMOw}}''! 	7}-36	7s   %( >9>c                    t        |j                        }|D ]*  }|st        d      |j                  |j                         , 	  | |j	                               S # t
        $ r}t        d      |d}~ww xY w)z.Add interior wires as holes to a surface face.z$interior_wires contain an empty wireJError adding interior hole in non-planar face with provided interior_wiresN)r   r   r   r   r   rE   r  )r   surface_faceinterior_wiresmakeface_objectr  r  s         r   _add_surface_holeszFace._add_surface_holes  s    
 2,2F2FG" 	.D !GHH-	.	++-.. 	\	s   A 	A6%A11A6c                2   |r|D cg c]  }t        |       }}nd}t        dddddddd	d
d
      }| j                  |      D ]"  }|j                  |j                  t
               $ | j                  |dt        t        t        t        f      }|rE|D ]  }	|j                  t        |	         | j                  |dt        t        t        t        f      }|r| j                  ||      }|j                         }|j                  st        d      |S c c}w )aP  Create Non-Planar Face

        Create a potentially non-planar face bounded by exterior (wire or edges),
        optionally refined by surface_points with optional holes defined by
        interior_wires.

        Args:
            exterior (Union[Wire, list[Edge]]): Perimeter of face
            surface_points (list[VectorLike], optional): Points on the surface that
                refine the shape. Defaults to None.
            interior_wires (list[Wire], optional): Hole(s) in the face. Defaults to None.

        Raises:
            RuntimeError: Internal error building face
            RuntimeError: Error building non-planar face with provided surface_points
            RuntimeError: Error adding interior hole
            RuntimeError: Generated face is invalid

        Returns:
            Face: Potentially non-planar face
        Nr     r   Fgh㈵>g-C6?{Gz?皙?   	   )
Degree
NbPtsOnCurNbIterAnisotropieTol2dTol3dTolAngTolCurvMaxDegMaxSegmentsz5Error building non-planar face with provided exteriorz;Error building non-planar face with provided surface_pointsznon planar face is invalid)re   r    r  r   r   r-   r  rB   rE   rC   rA   r:   r  fixr  r  )
r   r  surface_pointsr  r5  surface_point_vectorsrn  r   r  r  s
             r   make_surfacezFace.make_surface!  s8   8 8F$G1VAY$G!$G$(! ,%
* //9 	2DKKj1	2 ..C %*		
 !. ,FEN+,22M$#).		L 11,OL#'')$$;<<{ %Hs   Dc           	        t        dt        |      dt        |d               }t        |      D ]H  \  }}t        |      D ]5  \  }	}
|j                  |dz   |	dz   t	        |
      j                                7 J |rt        |g|||d}nt        ||||      }|j                         st        d      |j                         } | t        |t        j                               j                               S )a  make_surface_from_array_of_points

        Approximate a spline surface through the provided 2d array of points.
        The first dimension correspond to points on the vertical direction in the parameter
        space of the face. The second dimension correspond to points on the horizontal
        direction in the parameter space of the face. The 2 dimensions are U,V dimensions
        of the parameter space of the face.

        Args:
            points (list[list[VectorLike]]): a 2D list of points, first dimension is V
                parameters second is U parameters.
            tol (float, optional): tolerance of the algorithm. Defaults to 1e-2.
            smoothing (Tuple[float, float, float], optional): optional tuple of
                3 weights use for variational smoothing. Defaults to None.
            min_deg (int, optional): minimum spline degree. Enforced only when
                smoothing is None. Defaults to 1.
            max_deg (int, optional): maximum spline degree. Defaults to 3.

        Raises:
            ValueError: B-spline approximation failed

        Returns:
            Face: a potentially non-planar face defined by points
        rg   r   )DegMaxTol3D)DegMinr  r  zB-spline approximation failed)rG   r   r  r  re   r  r3   ro  r   Surfacer   r=   r  r   )r   r  tol	smoothingmin_degmax_degr  r"  	point_rowr  r  spline_builderspline_geoms                r   !make_surface_from_array_of_pointsz&Face.make_surface_from_array_of_points}  s   B &aVaVAYH%f- 	GLAy%i0 G5  QAve}/C/C/EFG	G ;#,33N <sN $$&<==$,,.*;	8M8M8OPUUWXXr   c                     y r   r   )r   edge1edge2s      r   make_surface_from_curveszFace.make_surface_from_curves      
 	r   c                     y r   r   )r   wire1wire2s      r   r  zFace.make_surface_from_curves  r	  r   c                   d\  }}|r:t        |      dk7  st        |d         t        |d         urt        d      |\  }}|j                  d|      }|j                  d|      }|j                  d|      }|j                  d	|      }|r+t	        d
dj                  |j                                      t        |t        t        f      rt        |t        t        f      st        d      t        |t              r;| j                  t        j                  |j                  |j                              }|S | j                  t        j                  |j                  |j                              }|S )ar  make_surface_from_curves

        Create a ruled surface out of two edges or two wires. If wires are used then
        these must have the same number of edges.

        Args:
            curve1 (Union[Edge,Wire]): side of surface
            curve2 (Union[Edge,Wire]): opposite side of surface

        Returns:
            Face: potentially non planar surface
        NNr   r   rg   z>Both curves must be of the same type (both Edge or both Wire).r  r  r  r  zUnexpected argument(s): r  )r   r*  	TypeErrorpopr   r  r  r   rh   rj   r   r   Shell_sr   Face_s)r   r}  r~  curve1curve2return_values         r   r  zFace.make_surface_from_curves  sA    $4yA~d1gd47m!CT  "NFFGV,GV,GV,GV, 7		&++-8P7QRSS&4,/z&4QU,7WP  fd#88H$4$4V^^V^^$TUL  88HOOFNNFNN$STLr   c                   t         j                  t        t         j                  t        t         j
                  t        i}t               }|r:|D ]5  }|j                  |d   j                  |d   j                  ||d             7 |r4|D ]/  }|j                  |j                  |t         j                            1 |r|D ]  }|j                  t        |         	 |j                           | t        j                  |j                                     }	|	j)                         }	|	j*                  r|	st'        d      |	S # t        t         t"        t$        f$ r}
t'        d      |
d}
~
ww xY w)a  make_surface_patch

        Create a potentially non-planar face patch bounded by exterior edges which can
        be optionally refined using support faces to ensure e.g. tangent surface
        continuity. Also can optionally refine the surface using surface points.

        Args:
            edge_face_constraints (list[tuple[Edge, Face, ContinuityLevel]], optional):
                Edges defining perimeter of face with adjacent support faces subject to
                ContinuityLevel. Defaults to None.
            edge_constraints (list[Edge], optional): Edges defining perimeter of face
                without adjacent support faces. Defaults to None.
            point_constraints (list[VectorLike], optional): Points on the surface that
                refine the shape. Defaults to None.

        Raises:
            RuntimeError: Error building non-planar face with provided constraints
            RuntimeError: Generated face is invalid

        Returns:
            Face: Potentially non-planar face
        r   rg   r   z8Error building non-planar face with provided constraintsNzNon planar face is invalid)rY   C0r-   C1r/   C2r0   r    r   r   r:   r   rM   r   rm   rB   rE   rC   rA   r  r  r  )r   edge_face_constraintsedge_constraintspoint_constraintscontinuity_dictpatch
constraintr   r  r'  r  s              r   make_surface_patchzFace.make_surface_patch  sU   @ 




 *+ 3 
		qM))qM))#JqM2 ( M		$,,8J8J(KLM * *		&%.)*	KKMU[[]34F f;<< !&	
 	 J	s   9E   E*E%%E*c                    t        |j                  |j                  |t        z  d      } | |j                               S )a  sweep

        Revolve an Edge around an axis.

        Args:
            profile (Edge): the object to sweep
            angle (float): the angle to revolve through
            axis (Axis): rotation Axis

        Returns:
            Face: resulting face
        T)r"   r   r^   rm   r   profileangler  revol_builders        r   revolvezFace.revolve4  s<    & .OOLLGO	
 =&&())r   c           
         t        |D cg c]  }|j                   c}      }t        |      }g }|D ]  }t        |t              r%|j                  t        t        |      g             8t        |t              r)|j                  t        |      j                                qt        |t              r,|j                  t        d t        |d      D                     t        dt        |       d       |S c c}w )aG  sew faces

        Group contiguous faces and return them in a list of ShapeList

        Args:
            faces (Iterable[Face]): Faces to sew together

        Raises:
            RuntimeError: OCCT SewedShape generated unexpected output

        Returns:
            list[ShapeList[Face]]: grouped contiguous faces
        c              3  2   K   | ]  }t        |        y wr   )r   r  s     r   r   z!Face.sew_faces.<locals>.<genexpr>l  s       Qr   r   zSewedShape returned a z which was unexpected)rp   r   ru   r   rN   r   rn   r   rP   r   r   rQ   rr   r  r*  )r   r   r%  sewed_shapetop_level_shapes
sewn_facestop_level_shapes          r   	sew_faceszFace.sew_facesP  s      (E(Bq(BC6{C&(
  0 	O/;7!!)T/-B,C"DEO\:!!%"8">">"@AO\:!! !1/6!J  #,T/-B,CCXY 	" - )Cs   C;c                F   t        |j                               dk7  st        |j                               dk7  rt        d      |j                         }|j                         }|J |J t	        |g      }t	        |g      }t        |j                        }|j                  |j                  dd       |j                  t        j                  |          |j                          t        |j                               }t        j                  r|j                         }|S )aO  sweep

        Sweep a 1D profile along a 1D path. Both the profile and path must be composed
        of only 1 Edge.

        Args:
            profile (Union[Curve,Edge,Wire]): the object to sweep
            path (Union[Curve,Edge,Wire]): the path to follow when sweeping
            transition (Transition, optional): handling of profile orientation at C1 path
                discontinuities. Defaults to Transition.TRANSFORMED.

        Raises:
            ValueError: Only 1 Edge allowed in profile & path

        Returns:
            Face: resulting face, may be non-planar
        rg   z&Use Shell.sweep for multi Edge objectsF)r   r   r   r   rj   r!   r   r   SetTransitionModerm   _transModeDictr   r   ro   clean)r   r#  path
transitionprofile_edge	path_edgebuilderr'  s           r   sweepz
Face.sweepx  s    : w}}1$DJJL(9Q(>EFF||~IIK	'''$$$~&YK -dll;GOOUE2!!%"6"6z"BCgmmo&??\\^Fr   c                d   |t         j                  k(  s|t         j                  k(  rQ| j                  rEt	               }t        j                  | j                  |       |j                         }t#        |      S |t         j                  k(  r)| j                         j                         }t#        |      S |t         j                  k(  r`| j                         \  }}}}d||z   z  }d||z   z  }	t               }t               }
t        | j                        j!                  ||	||
       t#              S )zCenter of Face

        Return the center based on center_of

        Args:
            center_of (CenterOf, optional): centering option. Defaults to CenterOf.GEOMETRY.

        Returns:
            Vector: center
        r  )rX   MASSGEOMETRYr"  r<   r   SurfaceProperties_sr   CentreOfMassr  r4  rH  
_uv_boundsr:   r;   r   Normalre   )r   	center_of
propertiescenter_pointu_val0u_val1v_val0v_val1u_valv_valr)  s              r   rH  zFace.center  s    &***t~~%J))$,,
C%224L l## (///,,.557L l## (+++-1__->*FFFF6F?+E6F?+E!8LXF4<<(//ulFSl##r   c                   |}t        | j                        }t               }t        j                  | j                  t
        j                  t
        j                  |       |D ]  }|j                  |j                        }	t        t        j                  |	j                                     t        t        j                  |	j                                     f}
t        j                  ||
      \  }}|j                  t        j                  |j                        t        j                  |j                        ||        |j!                          | j"                  j%                  |j'                               j)                         S )a  Apply 2D chamfer to a face

        Args:
            distance (float): chamfer length
            distance2 (float): chamfer length
            vertices (Iterable[Vertex]): vertices to chamfer
            edge (Edge): identifies the side where length is measured. The vertices must be
                part of the edge

        Raises:
            ValueError: Cannot chamfer at this location
            ValueError: One or more vertices are not part of edge

        Returns:
            Face: face with a chamfered corner(s)

        )r   r   rR   rL   MapShapesAndAncestors_sr   r   r   FindFromKeyrh   rM   r   Lastrj   order_chamfer_edges
AddChamferr   r   r   rm   r  )r   r!  	distance2r   r   reference_edgechamfer_buildervertex_edge_maprY  	edge_listr   r  r  s                r   
chamfer_2dzFace.chamfer_2d  s#   0 4T\\BCE&&LL"**BNNO	
  	A'33AII>I
 V[[!234V[[!123E
  33NEJLE5&&EMM*EMM*		& 	~~""?#8#8#:;??AAr   c           	        |D cg c]  }|j                   | }}|s| S | j                         }| j                         }g }|g|D ]Y  }|D cg c]&  t        fd|j	                         D              r( }}|j                  |r|j                  ||      n|       [ | j                  |d   |dd       }	| j                         |	j                         k7  r|	 }	|	S c c}w c c}w )zApply 2D fillet to a face

        Args:
          radius: float:
          vertices: Iterable[Vertex]:

        Returns:

        Nc              3  h   K   | ])  }|j                   j                  j                          + y wr   )r   IsSame)r   wire_vertexr   s     r   r   z!Face.fillet_2d.<locals>.<genexpr>  s.      #  ''..v~~>s   /2r   rg   )	r   r  r  r   r   r   	fillet_2dr   r  )
r   rz  r   r   r  r  filleted_wiresr  vertices_in_wirefilleted_faces
      `      r   rX  zFace.fillet_2d  s    *2PvV^^5OFPPK__&
&&(%'.+. 	D '  '+}}      !!<Lv'78RV	 ~a'8.:LM>>}6688*NM3 Q s   C#C#+C(c                @    t        j                  | j                        S )z%Return the Geom Surface for this Face)r   r  r   r   s    r   r  zFace.geom_adaptor&  s    ""4<<00r   c                    | j                         }| j                         D cg c]  }|j                  |      r| }}|D ]!  }| j                  | n| j                  |_        # t	        |      S c c}w )z.Extract the inner or hole wires from this Face)r  wiresis_samer   rn   )r   outerr  innerss       r   r  zFace.inner_wires*  sn    !!ZZ\B51A!BB 	SA$($4$4$<D$BRBRAM	S   Cs
   A1A1c                H   | j                         \  }}}}t               }t               }t        | j                        j                  ||||       |j                  t        |            xr7 dt        |j                  j                  t        |                  z
  t        k  S )z4Is this planar face coplanar with the provided planerg   )r=  r:   r;   r   r   r>  containsre   r   r  r  r_   )r   r  rB  _u_val1rD  _v_val1gp_pntr)  s           r   is_coplanarzFace.is_coplanar2  s    +/??+<(t||$++FFFFK NN6&>* ECv7889D	
r   c                    t        | j                        }|j                  t        t	        |       |       |j                         S )a  Point inside Face

        Returns whether or not the point is inside a Face within the specified tolerance.
        Points on the edge of the Face are considered inside.

        Args:
          point(VectorLike): tuple or Vector representing 3D point to be tested
          tolerance(float): tolerance for inside determination. Defaults to 1.0e-6.
          point: VectorLike:
          tolerance: float:  (Default value = 1.0e-6)

        Returns:
          bool: indicating whether or not point is within Face

        )r   r   rn  r:   re   	IsOnAFace)r   r  r  solid_classifiers       r   	is_insidezFace.is_inside>  s;      7t||D  !7C))++r   r  c                    y r   r   )r   surface_pointr  s      r   r  zFace.location_atV  s     r   c                    y r   r   )r   urY  r  s       r   r  zFace.location_at^  s     r   c                   d\  }}}|rit        |d   t        t        f      r|d   }nt        |d   t        t        f      r|d   }t        |      dk(  rt        |d   t        t        f      r|d   }t        |j                               j                  h d      }|rt        ddj                  |             |j                  d|      }|j                  d	|      }|j                  d
|      }|j                  dd      }||dk  r|dk  rd\  }}n||dk  s|dk  rt        d      | j                         }| j                         \  }	}
}}||	||
|	z
  z  z   }||||z
  z  z   }n6t        t        |      j                         |      }|j!                         \  }}t#               }t%               }t%               }|j'                  |||||       t        |      }t        |      j)                  t        |            j+                         }|t        |      j+                         nt        |      j+                         }t-        t/        |||            S )a  location_at

        Get the location (origin and orientation) on the surface of the face.

        This method supports two overloads:

        1. `location_at(u: float, v: float, *, x_dir: VectorLike | None = None) -> Location`
        - Specifies the point in normalized UV parameter space of the face.
        - `u` and `v` are floats between 0.0 and 1.0.
        - Optionally override the local X direction using `x_dir`.

        2. `location_at(surface_point: VectorLike, *, x_dir: VectorLike | None = None) -> Location`
        - Projects the given 3D point onto the face surface.
        - The point must be reasonably close to the face.
        - Optionally override the local X direction using `x_dir`.

        If no arguments are provided, the location at the center of the face
        (u=0.5, v=0.5) is returned.

        Args:
            u (float): Normalized horizontal surface parameter (optional).
            v (float): Normalized vertical surface parameter (optional).
            surface_point (VectorLike): A 3D point near the surface (optional).
            x_dir (VectorLike, optional): Direction for the local X axis. If not given,
                the tangent in the U direction is used.

        Returns:
            Location: A full 3D placement at the specified point on the face surface.

        Raises:
            ValueError: If only one of `u` or `v` is provided or invalid keyword args are passed.
        Nr  r  r   r   rg   >   rp  rY  r  rn  r  r  rn  rp  rY  r  Nr  r  #Both u & v values must be specifiedr  )r   re   r   r  r  r   r  r  r  r   r  r   r  r=  r4   r  LowerDistanceParametersr:   r;   D1r(  r$  rb   rd   )r   r}  r~  rn  rp  rY  r  
user_x_dirgeom_surfaceu_minu_maxv_minv_maxrF  rG  	projectorr(  dudvr  r  r  s                         r   r  zFace.location_atc  sD   B /q!$q'FH#56 $QDGc5\2G4yA~*T!WsEl"CG6;;=)440
 6tyy7N6OPQQ

?MBJJsAJJsAZZ.
 QUq1uDAq"AQBCC%)%6%6%8%)__%6"ueU A//EA//E2}%,,.I %<<>LE5 hXXuc2r2r
  ,779 % :))+&&( 	 V5FGGr   c                   t        | j                        }|D ]  }|j                  |j                          	 t        |j                               }|j                         }|S # t        $ r}t        d      |d}~ww xY w)aW  Make Holes in Face

        Create holes in the Face 'self' from interior_wires which must be entirely interior.
        Note that making holes in faces is more efficient than using boolean operations
        with solid object. Also note that OCCT core may fail unless the orientation of the wire
        is correct - use `Wire(forward_wire.wrapped.Reversed())` to reverse a wire.

        Example:

            For example, make a series of slots on the curved walls of a cylinder.

        .. image:: slotted_cylinder.png

        Args:
          interior_wires: a list of hole outline wires
          interior_wires: list[Wire]:

        Returns:
          Face: 'self' with holes

        Raises:
          RuntimeError: adding interior hole in non-planar face with provided interior_wires
          RuntimeError: resulting face is not valid

        r  N)r   r   r   r   rE   r  r  )r   r  r  interior_wirer  r  s         r   
make_holeszFace.make_holes  s    6 2$,,?+ 	7M 5 56	7	 4 4 67L $'')   	\	s   A$ $	A>-A99A>c                     y)a  normal_at point on surface

        Args:
            surface_point (VectorLike, optional): a point that lies on the surface where
                the normal. Defaults to the center (None).

        Returns:
            Vector: surface normal direction
        Nr   )r   rn  s     r   r  zFace.normal_at  r   r   c                     y)a  normal_at u, v values on Face

        Args:
            u (float): the horizontal coordinate in the parameter space of the Face,
                between 0.0 and 1.0
            v (float): the vertical coordinate in the parameter space of the Face,
                between 0.0 and 1.0
                Defaults to the center (None/None)

        Raises:
            ValueError: Either neither or both u v values must be provided

        Returns:
            Vector: surface normal direction
        Nr   )r   rp  rY  s      r   r  zFace.normal_at  r   r   c                   d\  }}}|rit        |d   t        t        f      r|d   }nt        |d   t        t        f      r|d   }t        |      dk(  rt        |d   t        t        f      r|d   }dj                  t        |j                               j                  g d            }|rt        d|       |j                  d|      }|j                  d	|      }|j                  d
|      }||dk  r|dk  rd\  }}n$|"t        d ||fD              dk(  rt        d      | j                         }|,| j                         \  }}	}
}|||	|z
  z  z   }|
|||
z
  z  z   }n6t        t        |      j!                         |      }|j#                         \  }}t%               }t'               }t)        | j*                        j-                  ||||       t        |      j/                         S )a0  normal_at

        Computes the normal vector at the desired location on the face.

        Args:
            surface_point (VectorLike, optional): a point that lies on the surface where the normal.
                Defaults to None.

        Returns:
            Vector: surface normal direction
        rr  r   r   rg   r  )rn  rp  rY  r  rn  rp  rY  rs  c              3  &   K   | ]	  }|d k(    yw)r  Nr   )r   r"  s     r   r   z!Face.normal_at.<locals>.<genexpr>$	  s     *E19*Es   rt  )r   re   r   r  r  r   r  r  r  r  r   r   r   r  r=  r4   r  ru  r:   r;   r   r   r>  r$  )r   r}  r~  rn  rp  rY  r  rn  rB  rC  rD  rE  rF  rG  r}  rf  r)  s                    r   r  zFace.normal_at	  s    /q!$q'FH#56 $QDGc5\2G4yA~*T!WsEl"CGyy))*EF
 6|nEFF

?MBJJsAJJsA QUq1uDAq"s*Eq!f*E'E'JBCC ##% -1__->*FFFFQ&6/22EQ&6/22E 3}%,,.I %<<>LE5t||$++E5&&If~((**r   c                    t        t        j                  | j                              }| j                  	| |_        |S | j                  |_        |S )z)Extract the perimeter wire from this Face)rj   r#   OuterWire_sr   r   )r   r`  s     r   r  zFace.outer_wire<	  sJ    Y**4<<89$($4$4$<D CGBRBRr   c                    | j                         \  }}}}||||z
  z  z   }||||z
  z  z   }t               }	t               }
t        | j                        j                  |||	|
       t        |	      S )a  position_at

        Computes a point on the Face given u, v coordinates.

        Args:
            u (float): the horizontal coordinate in the parameter space of the Face,
                between 0.0 and 1.0
            v (float): the vertical coordinate in the parameter space of the Face,
                between 0.0 and 1.0

        Returns:
            Vector: point on Face
        )r=  r:   r;   r   r   r>  re   )r   rp  rY  rB  rC  rD  rE  rF  rG  rf  r)  s              r   r  zFace.position_atB	  st     *.):&fvo..fvo..t||$++E5&&If~r   c           	     d   t        | |g      }t        | j                  t        |      |z        }t	               }t        |t              rt        d      t        |t              rLt        |f|j                  ft                     }|j                         s|j                  t        |             ns|j                         D ]`  }t        |f|j                  ft                     }t        |      D ]/  }|j                  t        t!        j                  |                   1 b |j#                  t%        | j'                         |            }t	               }	|D ]T  }
t)        |
j+                               dk(  r%|
j-                         }|2|	j                  |       D|	j                  |
       V |	S )a  Project Face to target Object

        Project a Face onto a Shape generating new Face(s) on the surfaces of the object.

        A projection with no taper is illustrated below:

        .. image:: flatProjection.png
            :alt: flatProjection

        Note that an array of faces is returned as the projection might result in faces
        on the "front" and "back" of the object (or even more if there are intermediate
        surfaces in the projection path). faces "behind" the projection are not
        returned.

        Args:
            target_object (Shape): Object to project onto
            direction (VectorLike): projection direction

        Returns:
            ShapeList[Face]: Face(s) projected on target object ordered by distance
        z'projection to a vertex is not supportedrg   )rz   rw   r   re   rn   r   r{   r  r   rq   r   r   r   shellsru   r   rM   r  r`   rH  r   r   r   )r   target_objectr   max_dimensionextruded_topods_selfintersected_shapestopods_shapetarget_shelltopods_shellprojected_shapesr  
shape_faces               r   project_to_shapezFace.project_to_shapeZ	  s   0 +D-+@A4LL&+m; 
 7@kmV,EFFmT**%'-*?*?)ACUCWL  &&("))& !. 4 4 6 Q.)+!))+&( 
 %@$M QL&--eFLL4N.OPQQ 077T[[]I8VW4=K' 	/E5;;=!Q&"ZZ\
)$++J7 ''.	/  r   zKThe 'to_arcs' method is deprecated and will be removed in a future version.c                    | j                   t        d      | j                  j                  t	        j
                  | j                  |            S )a  to_arcs

        Approximate planar face with arcs and straight line segments.

        This is a utility used internally to convert or adapt a face for Boolean operations. Its
        purpose is not typically for general use, but rather as a helper within the Boolean kernel
        to ensure input faces are in a compatible and canonical form.

        Args:
            tolerance (float, optional): Approximation tolerance. Defaults to 1e-3.

        Returns:
            Face: approximated face
        z!Cannot approximate an empty shape)r   r   r   r   r   ConvertFace_sr   )r   r  s     r   to_arcszFace.to_arcs	  s@    $ == @AA~~""8#9#9$,,	#RSSr   c                Z   | j                   t        d      | j                         x}s| S t        j                  |       }t               }|D ]  }|j                  |j                          t        |j                  | j                               }t        j                  |      |_        |S )zwithout_holes

        Remove all of the holes from this face.

        Returns:
            Face: A new Face instance identical to the original but without any holes.
        z&Cannot remove holes from an empty face)r   r   r  r   r   r$   Remover   rt   ApplyrM   r   )r   r  holelessreshaper	hole_wiremodified_shapes         r   r  zFace.without_holes	  s     == EFF#//111K==&$&$ 	/IOOI--.	/!(.."?@!;;~6r   c                p    | j                         rt        j                  dd       | j                         S )z?Return the outerwire, generate a warning if inner_wires presentz!Found holes, returning outer_wirer   )
stacklevel)r  warningswarnr  r   s    r   r  z	Face.wire	  s/    MM3   r   c                     y r   r   r   planar_shaper  r  extension_factors        r   wrapz	Face.wrap	       r   c                     y r   r   r  s        r   r  z	Face.wrap	  r  r   c                     y r   r   r  s        r   r  z	Face.wrap	  r  r   c                   t        |t              r| j                  ||d|      S t        |t              r| j	                  ||||      S t        |t
              r| j                  ||||      S t        dt        |             )u8  wrap

        Wrap a planar 2D shape onto a 3D surface.

        This method conforms a 2D shape defined on the XY plane (Edge,
        Wire, or Face) to the curvature of a non-planar 3D Face (the
        target surface), starting at a specified surface location. The
        operation attempts to preserve the original edge lengths and
        shape as closely as possible while minimizing the geometric
        distortion that naturally arises when mapping flat geometry onto
        curved surfaces.

        The wrapping process follows the local orientation of the surface
        and progressively fits each edge along the curvature. To help
        ensure continuity, the first and last edges are extended and trimmed
        to close small gaps introduced by distortion. The final shape is tightly
        aligned to the surface geometry.

        This method is useful for applying flat features—such as
        decorative patterns, cutouts, or boundary outlines—onto curved or
        freeform surfaces while retaining their original proportions.

        Args:
            planar_shape (Edge | Wire | Face): flat shape to wrap around surface
            surface_loc (Location): location on surface to wrap
            tolerance (float, optional): maximum allowed error. Defaults to 0.001
            extension_factor (float, optional): amount to extend the wrapped first
                and last edges to allow them to cross. Defaults to 0.1

        Raises:
            ValueError: Invalid planar shape

        Returns:
            Edge | Wire | Face: wrapped shape

        Tz2planar_shape must be of type Edge, Wire, Face not )	r   rh   r  rj   
_wrap_wirer   
_wrap_facer  r*  r  s        r   r  z	Face.wrap	  s    X lD)??<dINNlD)??k96F  lD)??k96F  @L!"$
 	
r   c           
        |j                   }t        |      }|d   j                         j                  j                  }t               }|D ]  }|j                         }	|	j                  j                  |	j                  j                  z   dz  }
|
|z
  }|||z  z   }|j                  |      }t        t        ||j                  |      | j                  |                  }t        |j                  t              sJ |xj                  |ddfz  c_        t        j!                  | ||      }|j#                  |        |S )u  wrap_faces

        Wrap a sequence of 2D faces onto a 3D surface, aligned along a guiding path.

        This method places multiple planar `Face` objects (defined in the XY plane) onto a
        curved 3D surface (`self`), following a given path (Wire or Edge) that lies on or
        closely follows the surface. Each face is spaced along the path according to its
        original horizontal (X-axis) position, preserving the relative layout of the input
        faces.

        The wrapping process attempts to maintain the shape and size of each face while
        minimizing distortion. Each face is repositioned to the origin, then individually
        wrapped onto the surface starting at a specific point along the path. The face's
        new orientation is defined using the path's tangent direction and the surface normal
        at that point.

        This is particularly useful for placing a series of features—such as embossed logos,
        engraved labels, or patterned tiles—onto a freeform or cylindrical surface, aligned
        along a reference edge or curve.

        Args:
            faces (Iterable[Face]): An iterable of 2D planar faces to be wrapped.
            path (Wire | Edge): A curve on the target surface that defines the alignment
                direction. The X-position of each face is mapped to a relative position
                along this path.
            start (float, optional): The relative starting point on the path (between 0.0
                and 1.0) where the first face should be placed. Defaults to 0.0.

        Returns:
            ShapeList[Face]: A list of wrapped face objects, aligned and conformed to the
                surface.
        r   r   r  r  )r   r   r4  r  rt  rn   r  r  rb   rd   rR  r  r   rD  re   r   r  r   )r   r   r2  startpath_length	face_listfirst_face_min_xwrapped_facesr   rO  face_center_xdelta_xrelative_position_on_wirepath_positionsurface_locationwrapped_faces                   r   
wrap_faceszFace.wrap_faces"
  s)   L kkK	$Q<446::<< *3 	/D$$&D!XXZZ$((**49M#&66G(-+0E(E% ,,-FGM'!//*CD..7  dmmV444MMgq!_,M99T41ABL  .!	/$ r   c                @    t        j                  | j                        S )z,Return the u min, u max, v min, v max values)r#   
UVBounds_sr   r   s    r   r=  zFace._uv_boundsc
  s    ##DLL11r   c           	        | j                  |j                         |||      }|j                         D cg c]  }| j                  ||||       }}t        j	                  ||j
                  g|      }|j                  j                  }	|j                  |j
                        }
|	j                  |
      dk  r| }|S c c}w )a  _wrap_face

        Helper method of wrap that handles wrapping faces on surfaces.

        Args:
            planar_face (Face): flat face to wrap around surface
            surface_loc (Location): location on surface to wrap
            tolerance (float, optional): maximum allowed error. Defaults to 0.001
            extension_factor (float, optional): amount to extend wrapped first
                and last edges to allow them to cross. Defaults to 0.1

        Returns:
            Face: wrapped face
        )r  r  r   )
r  r  r  r   r  rD  r  r   r  r  )r   planar_facer  r  r  wrapped_perimeterr  wrapped_holesr  surface_normalwrapped_normals              r   r  zFace._wrap_faceg
  s    * !OO""$k9>N

 !,,.
 OOA{I7GH
 
 (('001( ) 
 %++55%//0D0DEn-1(=L
s   B?c           
     J   |j                   }|j                  j                  }t        j                  | j
                        }t        |j                               dk(  r2|j                         }|J t        | j                  ||d|      g      S |j                         }	g }
d}|	d   j                  d      t        ddd      k(  r|}t        ddd      }nlt        j                  t        ddd      |	d   j                  d            }| j                  ||d|      }|j                  d      }|	d   j                  d      }t!        t#        ||| j%                  |                  }|	D ]  }|j'                  |       }| j                  ||d|      }|j                  d      }t!        t#        ||| j%                  |                  }|j                  d      }||j                  d      }|
j)                  |        |j*                  st        |
      S |
d   j-                  d||      \  }}|
d   j-                  d||      \  }}t/        ||      }|j1                         dk  rt3        d      |j5                  d      \  }}|j7                  d      }|j7                  d      }||z
  ||z
  z  }|j9                  |d	      }|j7                  d      }|j7                  d      }||z
  ||z
  z  } |j9                  d
|       }!||
d<   |!|
d<   |j                  d      |!j                  d      z
  j:                  }"t=               }#t?               }$|
D ]  }%|$jA                  |%j
                          |#jC                  |$       |#jE                          |#j                         }&tG               }'|'jI                  d|"z         |'jK                  |&       |'jM                          |'jO                          t        |'j                               }(|(jP                  st3        d      |(S )aD  _wrap_wire

        Helper method of wrap that handles wrapping wires on surfaces.

        Args:
            planar_wire (Wire): wire to wrap around surface
            surface_loc (Location): location on surface to wrap
            tolerance (float, optional): maximum allowed error. Defaults to 0.001
            extension_factor (float, optional): amount to extend wrapped first
                and last edges to allow them to cross. Defaults to 0.1

        Raises:
            RuntimeError: wrapped wire is not valid

        Returns:
            Wire: wrapped wire
        rg   NTr   r  r  Fz?Extended first/last edges do not intersect; increase extension.r  r  r   zwrapped wire is not valid))rD  r  r   r   r  r   r   r   r   rj   r  order_edgesr  re   rh   r  rb   rd   r  	translater   r   _extend_spliner2   	NbExtremar  
Parametersr  trimr   r   rS   r   r   r   r@   SetPrecisionLoad
FixReorderFixConnectedr  ))r   planar_wirer  r  r  rn  r  surface_geometryr  planar_edgeswrapped_edgesfirst_start_pointedge_surface_pointplanar_edge_end_pointconstruction_linewrapped_construction_lineedge_surface_locationlocal_planar_edger  
first_edgefirst_curve	last_edge
last_curverx  param_first
param_lastu_start_firstu_end_first	new_starttrimmed_firstu_start_last
u_end_lastnew_endtrimmed_lastclosing_errorwire_buildercombined_edgesr   raw_wrapped_wire
wire_fixerwrapped_wires)                                            r   r  zFace._wrap_wire
  sL   6 $,,)00::$..t||<{  "#q(%**,K***k4STUU"..0$& ! ?&&q)VAq!_<!.$*1aO! $q!Qa!<!<Q!?! /3oo!;i/% ";!F!Fq!I$0O$?$?$B! (")nn%78!
 ( 	/K + 5 57L6L M!%!#8$	"L ".!9!9!!<$,&-..);<%! %0$;$;A$>! ($0$<$<Q$?!  .!	/& $$&& #0"2"A"A"$4#

K !.b 1 @ @#%5!
	:
 ,KD"Q  #*"4"4Q"7Z)2215'003 =0[=5PQ	"	37'003%..q1
,l1JK ~~c73 )a(b %%a(<+C+CA+FF
& 	 /0-/! 	0D!!$,,/	0(',,."_
M 12()!JOO-.
 $$:;;r   )r  NN)r   zTopoDS_Face | Planer  strr  Color | Noner  Compound | NoneNr  NN)
r  rj   r  Iterable[Wire] | Noner  r  r  r  r  r  )r}  r
   r~  r
   r   r  )r   zNone | Axis)r   z
list[Axis])r   rb   )r   z
None | str)r   rh  )r   zPlane | None)r   zNone | float)r   zNone | tuple[float, float])r   r   r   rC  )r   r   )r   rh   r   rf   r   r   r   )r  list[list[VectorLike]]r  zlist[list[float]] | Noner   r   )ga2U0*3?)r  Iterable[VectorLike | Edge]r  r  r  r  r   r   )r  rd   r   r   )r  r  r  r  r  rd   r   r   )r  Wire | Iterable[Edge]r   rC  )rn  r    r  r  r   r   )r  r   r  zIterable[Wire]r   r   r  )r  r  r  Iterable[VectorLike] | Noner  r  r   r   )r  Nrg   r  )r  r  r  r  r  z!tuple[float, float, float] | Noner  r  r   r  r   r   )r  rh   r  rh   r   r   )r  rj   r  rj   r   r   )NNN)r  z3Iterable[tuple[Edge, Face, ContinuityLevel]] | Noner  zIterable[Edge] | Noner  r  r   r   )r#  rh   r$  r  r  r`   r   r   )r   ri  r   zlist[ShapeList[Face]])r#  Curve | Edge | Wirer2  r  r   r   )r?  rX   r   re   )
r!  r  rN  r  r   rj  r   zEdge | Noner   r   )rz  r  r   rj  r   r   )r   r+   )r   zShapeList[Wire])r  rd   r   rh  )r  )r  rf   r  r  r   rh  )rn  r  r  r  r   rb   )rp  r  rY  r  r  r  r   rb   )r  
list[Wire]r   r   )rn  r  r   re   )rp  r  rY  r  r   re   r   re   )r   rj   )r  rm   r   rf   r   zShapeList[Face | Shell])rg  )r  r  r   r   )rg  r  )
r  rh   r  rb   r  r  r  r  r   rh   )
r  rj   r  rb   r  r  r  r  r   rj   )
r  r   r  rb   r  r  r  r  r   r   )
r  r   r  rb   r  r  r  r  r   r   )r  )r   ri  r2  zWire | Edger  r  r   zShapeList[Face])r   z!tuple[float, float, float, float])r   r   r  r   r  rb   r  r  r  r  r   r   )r   r   r  rj   r  rb   r  r  r  r  r   rj   )Gr  r  r  r  orderr   r  r  r  r  rE  rH  rZ  rc  rf  rh  r"  r   r\  rz  r   r  r  r  r  r  r   r  r  rW   rd   r  r  r  staticmethodr  r  r  r  r  r  r   r&  r-  r]   TRANSFORMEDr7  rX   r:  rH  rS  rX  r  r  rg  rk  r  r  r  r  r  r  r  r  r  r  r  r=  r  r  __classcell__r   s   @r   r   r   6  s   D E  ""&   	
      .2""& + 	
    $:-| ) )   < ~ ~@ D D
  8 ( (T 0 0 1 1 	 	       U U
   '- '-R     P P&  -1.T&.T *.T 
	.T .T` 
  	R
-R
 ,R
 	R

 
R
 R
h X
 xx 
  CH88  &  . 7*7 7
 
7 7 1?	    7;04	Y'Y 4Y .	Y
 
Y Yv  7;4Y&4Y 4Y 5	4Y
 4Y 4Y 
4Y 4Yl !%	  
 !%	  
 ' 'R 
 269=E @E
 0E 7E 
E EN ** * 	*
 
* *6 % %N 
 ))	,$, ",
 
, ,` ,4+<+< $L !5B5B 5B #	5B
 5B 
5Bn#J1!

,0  ,0 $(	( !	
 
  @D ,=	 UHn)V 	 	  "7+r0; "; /9; 	 ; z UTT(.! 
 !"%  	
   
  
 !"%  	
   
  
 !"%  	
   
  !"%9
9
 9
 	9

  9
 
9
~ 	?? ? 	?
 
?B2 !"%''' ' 	'
  ' 
'Z !"%YYY Y 	Y
  Y 
Yr   c                       e Zd ZdZdZ	 	 	 	 d	 	 	 	 	 	 	 d fdZedd       Zedd       Z	eddd       Z
e	 	 	 	 	 	 	 	 dd       Zeej                  f	 	 	 	 	 dd	       Zdd
Zdd	 	 	 	 	 ddZ xZS )r   a  A Shell is a fundamental component in build123d's topological data structure
    representing a connected set of faces forming a closed surface in 3D space. As
    part of a geometric model, it defines a watertight enclosure, commonly encountered
    in solid modeling. Shells group faces in a coherent manner, playing a crucial role
    in representing complex shapes with voids and surfaces. This hierarchical structure
    allows for efficient handling of surfaces within a model, supporting various
    operations and analyses.g      @Nc                V   t        |t              rt        |      n|}t        |t              rt        t        |      x}      dk(  r|d   }t        |t              rQ|st        d      t               }t               }|j                  |       |j                  ||j                         |}nGt        |t              r7	 t        j                  t        |D cg c]  }|j                   c}            }t         
| E  ||||       yc c}w # t        $ r}	t        d      |	d}	~	ww xY w)a_  Build a shell from an OCCT TopoDS_Shape/TopoDS_Shell

        Args:
            obj (TopoDS_Shape | Face | Iterable[Face], optional): OCCT Shell, Face or Faces.
            label (str, optional): Defaults to ''.
            color (Color, optional): Defaults to None.
            parent (Compound, optional): assembly parent. Defaults to None.
        rg   r   z$Can't create a Shell from empty Facez*Unable to create Shell, invalid input typeNr  )r   r   r   r   r   r   r   rP   	MakeShellr   r   rM   r   rp   rD   r  r  r  )r   r   r  r  r  obj_listr6  shellr%  excr   s             r   r  zShell.__init__9  s    &c84d3i#c8$c-BX)Cq)H1+Cc4  !GHH"nG NEe$KKs{{+CX&Wll#45MAaii5M#NO 		 	 	
	 6N( W LMSVVWs*   ?D D	*D 	D 	D(D##D(c                    | j                   rft               j                  | j                        }t	               }t
        j                  t        |         }|J  |||       |j                         S y)z=volume - the volume of this Shell if manifold, otherwise zeror  )	is_manifoldr?   SolidFromShellr   r<   rm   shape_properties_LUTrv   Mass)r   solid_shellr@  calc_functions       r   r  zShell.volumec  sf     (*99$,,GK%J!66y7MNM ,,,+z2??$$r   c                f    t        t        j                   t        |j                  |                  S )a/  extrude

        Extrude a Wire into a Shell.

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

        Raises:
            ValueError: Unsupported class
            RuntimeError: Generated invalid result

        Returns:
            Edge: extruded shape
        )r   rM   rw   r   r   s      r   r   zShell.extrudeq  s$      V\\"7Y"OPQQr   c           	     N     | t        j                  t        |d|                  S )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. Wires may be closed or opened.

        Args:
            objs (list[Vertex, Wire]): wire perimeters or vertices
            ruled (bool, optional): stepped or smooth. Defaults to False (smooth).

        Raises:
            ValueError: Too few wires

        Returns:
            Shell: Lofted object
        F)rM   r   rx   )r   objsruleds      r   	make_loftzShell.make_loft  s!    $ 6<<
4 >?@@r   c                    t        |j                               }t        |j                  |j                  |t        z  d      } | t        j                  |j                                     S )a  sweep

        Revolve a 1D profile around an axis.

        Args:
            profile (Curve | Wire): the object to revolve
            angle (float): the angle to revolve through
            axis (Axis): rotation Axis

        Returns:
            Shell: resulting shell
        T)rj   r   r"   r   r^   rM   r   rm   r"  s        r   r&  zShell.revolve  sR    & w}}'-OOT\\57?D
 6<< 3 3 5677r   c                   t        |j                               }t        t        |j                               j                               }t        |j                        }|j                  |j                  dd       |j                  t        j                  |          |j                          t        t        j                  |j                                     }t        j                  r|j                         }|S )a  sweep

        Sweep a 1D profile along a 1D path

        Args:
            profile (Union[Curve, Edge, Wire]): the object to sweep
            path (Union[Curve, Edge, Wire]): the path to follow when sweeping
            transition (Transition, optional): handling of profile orientation at C1 path
                discontinuities. Defaults to Transition.TRANSFORMED.

        Returns:
            Shell: resulting Shell, may be non-planar
        F)rj   r   r  r!   r   r   r/  rm   r0  r   r   rM   ro   r1  )r   r#  r2  r3  r6  r'  s         r   r7  zShell.sweep  s    ( w}}'D&2245-dll;GOOUE2!!%"6"6z"BCv||GMMO45??\\^Fr   c                    t               }t        j                  | j                  |       t	        |j                               S )zCenter of mass of the shell)r<   r   LinearProperties_sr   re   r<  )r   r@  s     r   rH  zShell.center  s1    !^
$$T\\:>j--/00r   rl  c               t    | j                         j                  fd      d   }|j                  |      S )a  location_at

        Get the location (origin and orientation) on the surface of the shell.

        Args:
            surface_point (VectorLike): A 3D point near the surface.
            x_dir (VectorLike, optional): Direction for the local X axis. If not given,
                the tangent in the U direction is used.

        Returns:
            Location: A full 3D placement at the specified point on the shell surface.
        c                &    | j                        S r   rX  )r%  rn  s    r   r  z#Shell.location_at.<locals>.<lambda>  s    ammM.J r   r   rl  )r   r  r  )r   rn  r  r   s    `  r   r  zShell.location_at  s7    & zz|##$JKANU;;r   r  )r   z+TopoDS_Shell | Face | Iterable[Face] | Noner  r  r  r  r  r  r  )r   rj   r   rf   r   r   )F)r  zIterable[Vertex | Wire]r  rh  r   r   )r#  zCurve | Wirer$  r  r  r`   r   r   )r#  r  r2  r  r   r   r  )rn  rf   r  r  r   rb   )r  r  r  r  r  r  r  r  r  r   r  r&  r]   r  r7  rH  r  r  r   s   @r   r   r   ,  s+     E <@""&&
8&
 &
 	&

  &
T 	 	 R R" A A& 88 8 	8
 
8 82 
 ))	$ "
 
 B1 $(	<!< !	<
 
<r   r   c                    t        |       dk  r| gS t        | d   | dd       }g }|j                         D ]3  }|j                  |j	                         g|j                         z          5 |S )a%  Tries to determine how wires should be combined into faces.

    Assume:
        The wires make up one or more faces, which could have 'holes'
        Outer wires are listed ahead of inner wires
        there are no wires inside wires inside wires
        ( IE, islands -- we can deal with that later on )
        none of the wires are construction wires

    Compute:
        one or more sets of wires, with the outer wire listed first, and inner
        ones

    Returns, list of lists.

    Args:
      wire_list: list[Wire]:

    Returns:

    r   r   rg   N)r   r   r   r   r  r  )	wire_listr   r  r   s       r   sort_wires_by_build_orderr    s    0 9~
 	

 1y}-EL 
!  !	

 r   )r  r  r   zlist[list[Wire]])r  
__future__r   r   r  r  abcr   r   collections.abcr   r   mathr   typingr	   r
   r   r   r   r   r   
OCP.TopAbsTopAbsr   OCP.BRepr   r   OCP.BRepAdaptorr   OCP.BRepAlgor   OCP.BRepAlgoAPIr   r   OCP.BRepBuilderAPIr   r   r   OCP.BRepClass3dr   OCP.BRepExtremar   OCP.BRepFeatr   OCP.BRepFillr   OCP.BRepFilletAPIr   OCP.BRepGPropr   r   OCP.BRepIntCurveSurfacer   OCP.BRepOffsetAPIr    r!   OCP.BRepPrimAPIr"   OCP.BRepToolsr#   r$   r%   OCP.gcer&   OCP.Geomr'   r(   r)   r*   r+   r,   OCP.GeomAbsr-   r.   r/   r0   OCP.GeomAdaptorr1   OCP.GeomAPIr2   r3   r4   OCP.GeomLibr5   OCP.GeomProjLibr6   OCP.gpr7   r8   r9   r:   r;   	OCP.GPropr<   OCP.Precisionr=   OCP.ShapeAnalysisr>   OCP.ShapeFixr?   r@   OCP.StandardrA   rB   rC   rD   OCP.StdFailrE   
OCP.TColgprF   rG   OCP.TColStdrH   rI   rJ   rK   
OCP.TopExprL   
OCP.TopoDSrM   rN   rO   rP   rQ   OCP.TopToolsrR   rS   rT   
ocp_gordonrU   typing_extensionsrV   rW   build123d.build_enumsrX   rY   rZ   r[   r\   r]   build123d.geometryr^   r_   r`   ra   rb   rc   rd   re   rf   one_drh   ri   rj   rk   
shape_corerl   rm   rn   ro   rp   rq   rr   rs   rt   ru   rv   utilsrw   rx   ry   rz   zero_dr{   	compositer|   r}   three_dr~   r   r   r   r   r  r   r   r   <module>rO     s  5n #  
  # .  7 7     , - ! C 
 8 6 , ! 8 3 = T 1 N N   N M / 
 0 ' 9 9 " # 0 6  ( > 
 *  T T 
 1 . 
 
 
 > =     *CtV${	c5= {	|s7; sl?A<GL! A<H)r   