
    h                       % S SK Jr  S SKJrJrJrJrJrJrJ	r	J
r
JrJrJr  S SKJr  S SKJrJrJrJrJrJrJrJrJrJrJrJrJr  \	(       a  S SKJrJ r J!r!J"r"  S SK#J$r$  \" S5      r% " S S	\5      r&\\'   r(S
\)S'   \
\'\'4   r*S
\)S'   \\*\&4   r+S
\)S'    " S S\,5      r- " S S\-5      r. " S S\-5      r/ " S S\-5      r0 " S S\-5      r1SFS jr2SS.   SGS jjr3SHS jr4SIS jr5SJS jr6      SKS jr7    SLS  jr8SMS! jr9SNS" jr: " S# S$5      r; " S% S&5      r< " S' S(\<5      r=SOSPS* jjr> " S+ S)\<5      r? " S, S-\<5      r@ " S. S/5      rA SQ       SRS0 jjrB\" S S S 5      rCSSS1 jrDSTS2 jrESUS3 jrF " S4 S55      rGSVS6 jrHSVS7 jrISWS8 jrJSXS9 jrKSYS: jrLSZS; jrM " S< S=5      rNS>S S?.       S[S@ jjrOS>SA.     S\SB jjrP      S]SC jrQ S^     S_SD jjrRS`SE jrSg>)a    )annotations)DictIterableIterator
NamedTupleOptionalSequenceTYPE_CHECKINGTupleTypeTypeVarUnion)	TypeAlias)BoundingBoxMatrix44NULLVECOCSUCSUVecVec3areais_planar_facenormal_vector_3psafe_normal_vectorsubdivide_facesubdivide_ngons)PolyfacePolymeshMeshSolid3d)GenericLayoutTypeTc                  .    \ rS rSr% SrS\S'   S\S'   Srg)EdgeStat)   zNamed tuple of edge statistics.intcountbalance N)__name__
__module____qualname____firstlineno____doc____annotations____static_attributes__r)       C/var/www/html/env/lib/python3.13/site-packages/ezdxf/render/mesh.pyr$   r$   )   s    )JLr1   r$   r   FaceEdge	EdgeStatsc                      \ rS rSrSrg)MeshBuilderError5   r)   Nr*   r+   r,   r-   r0   r)   r1   r2   r7   r7   5       r1   r7   c                      \ rS rSrSrg)NodeMergingError9   r)   Nr9   r)   r1   r2   r<   r<   9   r:   r1   r<   c                      \ rS rSrSrg)MultipleMeshesError=   r)   Nr9   r)   r1   r2   r?   r?   =   r:   r1   r?   c                      \ rS rSrSrg)DegeneratedPathErrorA   r)   Nr9   r)   r1   r2   rB   rB   A   r:   r1   rB   c                      \ rS rSrSrg)NonManifoldMeshErrorE   r)   Nr9   r)   r1   r2   rE   rE   E   r:   r1   rE   c              #  r   #    U  H-  n[        U5      S:  a  M  US   US   :X  a	  USS v   M)  Uv   M/     g7f)zfYields all faces with more than two vertices as open faces
(first vertex index != last vertex index).
   r   N)lenfacesfaces     r2   
open_facesrN   I   s?      t9q=7d2hs)OJ s   57Fclosec             #     #    [        U 5       Hj  nUS   /nUSS  H  nUS   U:w  d  M  UR                  U5        M!     [        U5      S:  a  MB  U(       a  UR                  US   5        [        U5      v   Ml     g7f)zRemoves duplicated vertices and returns closed or open faces according
the `close` argument. Returns only faces with at least 3 edges.
r      NrI   rH   )rN   appendrJ   tuple)rL   rP   rM   new_faceindexs        r2   normalize_facesrW   V   sw      5!G9!"XE|u$&  x=1OOHQK(Ho "s   $A=AA=c              #  Z   #    [        U 5       H  n[        U5       Sh  vN   M     g N	7f)z$Yields all face edges as int tuples.N)rN   
face_edgesrK   s     r2   	all_edgesrZ   j   s%     5!d### "#s   +)
+c              #  f   #    [        U 5      n[        U5       H  nX   XS-   U-     4v   M     g7f)z5Yields all edges of a single open face as int tuples.rR   N)rJ   range)rM   sizerV   s      r2   rY   rY   p   s5     t9Dtk4d 2333 s   /1c                    [        SS5      n0 n[        U 5       H:  u  p4X44nSnX4:  a  XC4nSnUR                  XQ5      u  px[        US-   X-   5      X%'   M<     U$ )a  Returns the edge statistics.

The Edge statistic contains for each edge `(a, b)` the :class:`EdgeStat` as
tuple `(count, balance)` where the vertex index `a` is always smaller than
the vertex index `b`.

The edge count is how often this edge is used in faces as `(a, b)` or
`(b, a)` and the balance is the count of edge `(a, b)` minus the count of
edge `(b, a)` and should be 0 in "healthy" closed surfaces.
A balance not 0 indicates an error which may be double coincident faces or
mixed face vertex orders.

r   rR   rI   )r$   rZ   get)	rL   new_edgestatsabedgeorientationr'   r(   s	            r2   get_edge_statsrf   w   sn     1~HE% t54DK42uqy'*?@ ! Lr1   c                  ^  [        T 5      nUS:X  a  g[        R                  " T 5      U-  nSnSnU H  n[        U5      S:  a  M   [        U 4S jU 5       5      n[        R                  " U5      [        U5      -  n [        US   US   US   5      n	 X-
  R                  5       n
XYR                  U
5      -  nUS-  nM     US:  a  XT-  $ g! [         a     M  f = f! [         a     M  f = f! [         a     M  f = f)a=  Returns the estimated face-normals direction as ``float`` value
in the range [-1.0, 1.0] for a closed surface.

This heuristic works well for simple convex hulls but struggles with
more complex structures like a torus (doughnut).

A counter-clockwise (ccw) vertex arrangement is assumed but a
clockwise (cw) arrangement works too but the values are reversed.

The closer the value to 1.0 (-1.0 for cw) the more likely all normals
pointing outwards from the surface.

The closer the value to -1.0 (1.0 for cw) the more likely all normals
pointing inwards from the surface.

r           rH   c              3  .   >#    U  H
  nTU   v   M     g 7fNr)   .0iverticess     r2   	<genexpr>2estimate_face_normals_direction.<locals>.<genexpr>   s     !<t!(1+t   rR      )	rJ   r   sumrT   
IndexErrorr   ZeroDivisionError	normalizedot)rn   rL   
n_verticesmesh_centroidr'   direction_sumrM   face_verticesface_centroidface_normaloutward_vecs   `          r2   estimate_face_normals_directionr      s!   & XJQHHX&3MEMt9q=	!!<t!<<M /#d);	*a -"2M!4DK
	(8CCEK 	55
' ( qy$$#  		 ! 		 ! 		s6   C C!C2
CC!
C/.C/2
D ?D c              #  J   #    U  H  n[        [        U5      5      v   M     g 7frj   )rT   reversedrK   s     r2   flip_face_normalsr      s       HTN## s   !#c                   U R                   UR                  UR                  -  UR                  UR                  -  -
  -  U R                  UR                   UR                  -  UR                  UR                   -  -
  -  -   U R                  UR                  UR                   -  UR                   UR                  -  -
  -  -   $ )ay  
Returns six times the volume of the tetrahedron determined by abc
and the origin (0, 0, 0).
The volume is positive if the origin is on the negative side of abc,
where the positive side is determined by the right-hand--rule.
So the volume is positive if the ccw normal to abc points outside the
tetrahedron.

This code is taken from chull.c; see "Computational Geometry in C."
)zxy)rb   rc   cs      r2   volume6r      s     	
qssQSSy1339$%
##qssQSS133Y&
'	(
##qssQSS133Y&
'	(r1   c                     [        [        U 5      5      n[        UR	                  U 5      5      $ ! [         a     gf = f)Nrh   )r   r   ru   r   points_from_wcs)polygonocss     r2   area_3dr      sC    $W-. ##G,--  s   0 
==c                  T   \ rS rSrSS jr\SS j5       r\SS j5       r\SS j5       r\SS j5       r	\SS j5       r
\SS j5       r\SS	 j5       r\SS
 j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       rSS jrSS jrS S jrSS jrS S jrS S jrS!S jrSrg)"MeshDiagnose   c                J    Xl         0 U l        [        5       U l        / U l        g rj   )_mesh_edge_statsr   _bbox_face_normals)selfmeshs     r2   __init__MeshDiagnose.__init__   s     
&( ]
)+r1   c                .    U R                   R                  $ )z@Sequence of mesh vertices as :class:`~ezdxf.math.Vec3` instances)r   rn   r   s    r2   rn   MeshDiagnose.vertices   s     zz"""r1   c                .    U R                   R                  $ )z&Sequence of faces as ``Sequence[int]``)r   rL   r   s    r2   rL   MeshDiagnose.faces   s     zzr1   c                    [        U R                  5      S:X  a(  [        U R                  R	                  5       5      U l        U R                  $ )zReturns all face normal vectors  as sequence. The ``NULLVEC``
instance is used as normal vector for degenerated faces. (cached data)

r   )rJ   r   listr   face_normalsr   s    r2   r   MeshDiagnose.face_normals   s=     t!!"a'!%djj&=&=&?!@D!!!r1   c                    U R                   R                  (       d  U R                  R                  5       U l         U R                   $ )zGReturns the :class:`~ezdxf.math.BoundingBox` of the mesh. (cached data))r   has_datar   bboxr   s    r2   r   MeshDiagnose.bbox  s-     zz""*DJzzr1   c                ,    [        U R                  5      $ )zReturns the vertex count.)rJ   rn   r   s    r2   rx   MeshDiagnose.n_vertices  s     4==!!r1   c                ,    [        U R                  5      $ )zReturns the face count.)rJ   rL   r   s    r2   n_facesMeshDiagnose.n_faces  s     4::r1   c                ,    [        U R                  5      $ )z,Returns the unique edge count. (cached data))rJ   
edge_statsr   s    r2   n_edgesMeshDiagnose.n_edges  s     4??##r1   c                    [        U R                  5      S:X  a  [        U R                  5      U l        U R                  $ )a6  Returns the edge statistics as a ``dict``. The dict-key is the edge
as tuple of two vertex indices `(a, b)` where `a` is always smaller than
`b`. The dict-value is an :class:`EdgeStat` tuple of edge count and edge
balance, see :class:`EdgeStat` for the definition of edge count and
edge balance. (cached data)
r   )rJ   r   rf   rL   r   s    r2   r   MeshDiagnose.edge_stats  s4     t A%-djj9Dr1   c                N    U R                   U R                  -
  U R                  -   $ )zReturns the Euler characteristic:
https://en.wikipedia.org/wiki/Euler_characteristic

This number is always 2 for convex polyhedra.
)rx   r   r   r   s    r2   euler_characteristic!MeshDiagnose.euler_characteristic(  s      -<<r1   c                V    [        S U R                  R                  5        5       5      $ )a  Returns ``True`` if the edge balance is broken, this indicates a
topology error for closed surfaces. A non-broken edge balance reflects
that each edge connects two faces, where the edge is clockwise oriented
in the first face and counter-clockwise oriented in the second face.
A broken edge balance indicates possible topology errors like mixed
face vertex orientations or a non-manifold mesh where an edge connects
more than two faces. (cached data)

c              3  >   #    U  H  oR                   S :g  v   M     g7f)r   N)r(   rl   es     r2   ro   6MeshDiagnose.is_edge_balance_broken.<locals>.<genexpr><  s     D+Ca99>+C   )anyr   valuesr   s    r2   is_edge_balance_broken#MeshDiagnose.is_edge_balance_broken1  s"     D4??+A+A+CDDDr1   c                V    [        S U R                  R                  5        5       5      $ )zReturns ``True`` if all edges have an edge count < 3. (cached data)

A non-manifold mesh has edges with 3 or more connected faces.

c              3  >   #    U  H  oR                   S :  v   M     g7f)rH   Nr'   rl   rd   s     r2   ro   +MeshDiagnose.is_manifold.<locals>.<genexpr>E  s     G.Fd::>.Fr   allr   r   r   s    r2   is_manifoldMeshDiagnose.is_manifold>  s"     Gdoo.D.D.FGGGr1   c                V    [        S U R                  R                  5        5       5      $ )a  Returns ``True`` if the mesh has a closed surface.
This method does not require a unified face orientation.
If multiple separated meshes are present the state is only ``True`` if
**all** meshes have a closed surface. (cached data)

Returns ``False`` for non-manifold meshes.

c              3  >   #    U  H  oR                   S :H  v   M     g7f)rr   Nr   r   s     r2   ro   1MeshDiagnose.is_closed_surface.<locals>.<genexpr>Q  s     H/Gt::?/Gr   r   r   s    r2   is_closed_surfaceMeshDiagnose.is_closed_surfaceG  s"     Ht/E/E/GHHHr1   c                V    [        S U R                  R                  5        5       5      $ )zReturns the total edge count of all faces, shared edges are counted
separately for each face. In closed surfaces this count should be 2x
the unique edge count :attr:`n_edges`. (cached data)
c              3  8   #    U  H  oR                   v   M     g 7frj   r   r   s     r2   ro   0MeshDiagnose.total_edge_count.<locals>.<genexpr>X  s     =$<q77$<   )rs   r   r   r   s    r2   total_edge_countMeshDiagnose.total_edge_countS  s"    
 =DOO$:$:$<===r1   c                6    U R                   R                  5       $ )zBYields the unique edges of the mesh as int 2-tuples. (cached data))r   keysr   s    r2   unique_edgesMeshDiagnose.unique_edgesZ  s    ##%%r1   c                B    [        U R                  U R                  5      $ )a-  Returns the estimated face-normals direction as ``float`` value
in the range [-1.0, 1.0] for a closed surface.

This heuristic works well for simple convex hulls but struggles with
more complex structures like a torus (doughnut).

A counter-clockwise (ccw) vertex arrangement for outward pointing faces
is assumed but a clockwise (cw) arrangement works too but the return
values are reversed.

The closer the value to 1.0 (-1.0 for cw) the more likely all normals
pointing outwards from the surface.

The closer the value to -1.0 (1.0 for cw) the more likely all normals
pointing inwards from the surface.

There are no exact confidence values if all faces pointing outwards,
here some examples for surfaces created by :mod:`ezdxf.render.forms`
functions:

    - :func:`~ezdxf.render.forms.cube` returns 1.0
    - :func:`~ezdxf.render.forms.cylinder` returns 0.9992
    - :func:`~ezdxf.render.forms.sphere` returns 0.9994
    - :func:`~ezdxf.render.forms.cone` returns 0.9162
    - :func:`~ezdxf.render.forms.cylinder` with all hull faces pointing
      outwards but caps pointing inwards returns 0.7785 but the
      property :attr:`is_edge_balance_broken` returns ``True`` which
      indicates the mixed vertex orientation
    - and the estimation of 0.0469 for a :func:`~ezdxf.render.forms.torus`
      is barely usable

)r   rn   rL   r   s    r2   r   ,MeshDiagnose.estimate_face_normals_direction^  s    B /t}}djjIIr1   c                `    [        S U R                  R                  5        5       5      (       + $ )z+Returns ``True`` if any face is non-planar.c              3  8   #    U  H  n[        U5      v   M     g 7frj   )r   rl   rM   s     r2   ro   4MeshDiagnose.has_non_planar_faces.<locals>.<genexpr>  s     W8V~d++8Vr   )r   r   faces_as_verticesr   s    r2   has_non_planar_faces!MeshDiagnose.has_non_planar_faces  s#    W

8T8T8VWWWWr1   c                    U R                   (       aB  SnU R                  R                  S5       H  nU[        US   US   US   5      -  nM     US-  $ g)a  Returns the volume of a closed surface or 0 otherwise.

.. warning::

    The face vertices have to be in counter-clockwise order, this
    requirement is not checked by this method.

    The result is not correct for multiple separated meshes in a single
    MeshBuilder object!!!

rh   rH   r   rR   rr   g      @)r   r   tessellationr   )r   volumerM   s      r2   r   MeshDiagnose.volume  sW     !!F

//2'$q'47DG<< 3C<r1   c                    U R                   nSnU R                  R                  5        H%  nU Vs/ s H  oAU   PM	     nnU[        U5      -  nM'     U$ s  snf )zReturns the surface area.rh   )rn   r   rN   r   )r   vsurface_arearM   rm   r   s         r2   r   MeshDiagnose.surface_area  sY    MMJJ))+D%)*TtTG*GG,,L ,  +s   Ac                    U R                   S:  a-  [        R                  " U R                  5      U R                   -  $ [        $ )z6Returns the centroid of all vertices. (center of mass)r   )rx   r   rs   rn   r   r   s    r2   centroidMeshDiagnose.centroid  s/    ??Q88DMM*T__<<r1   )r   r   r   r   N)r   MeshBuilder)returnSequence[Vec3])r   Sequence[Face]r   r   )r   r&   )r   r5   r   bool)r   Iterable[Edge])r   float)r   r   )r*   r+   r,   r-   r   propertyrn   rL   r   r   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r0   r)   r1   r2   r   r      s#   , # #     " "   " "   $ $ 	  	  = = 
E 
E H H 	I 	I>&!JFX&r1   r   c                     \ rS rSrSrS(S jrS)S jrS rS*S jrS+S jr	S,S jr
S-S	 jrS.S
 jrS/S jrS0S jrS1S jr   S2     S3S jjr   S2     S4S jjr   S5   S6S jjr\S7S j5       r\S8S j5       r   S2     S4S jjrS9S:S jjr   S2     S4S jjr\S;S j5       rS<S=S jjrS>S?S jjrS@SAS jjrSBSCS jjrSBSDS jjrSBSES jjrS(S jr SFS jr!S(S  jr"SGSHS! jjr#SS".   SIS# jjr$ SGS$SS%.     SJS& jjjr%S'r&g)Kr   i  a@  A simple Mesh builder. Stores a list of vertices and a faces list where
each face is a list of indices into the vertices list.

The :meth:`render_mesh` method, renders the mesh into a DXF MESH entity.
The MESH entity supports ngons in AutoCAD, ngons are polygons with more
than 4 vertices.

Can only create new meshes.

c                     / U l         / U l        g rj   rn   rL   r   s    r2   r   MeshBuilder.__init__  s    $&*,
r1   c                ,    [        U R                  5      $ )z9Returns the :class:`~ezdxf.math.BoundingBox` of the mesh.)r   rn   r   s    r2   r   MeshBuilder.bbox  s    4==))r1   c                $    U R                  U 5      $ )zReturns a copy of mesh.)from_builderr   s    r2   copyMeshBuilder.copy  s      &&r1   c                    [        U 5      $ )z7Returns the :class:`MeshDiagnose` object for this mesh.)r   r   s    r2   diagnoseMeshBuilder.diagnose  s    D!!r1   c                `   ^ U R                   m[        U4S jU R                  U    5       5      $ )zKReturns the face `index` as sequence of :class:`~ezdxf.math.Vec3`
objects.
c              3  .   >#    U  H
  nTU   v   M     g 7frj   r)   )rl   virn   s     r2   ro   0MeshBuilder.get_face_vertices.<locals>.<genexpr>  s     >,=bXb\,=rq   )rn   rT   rL   )r   rV   rn   s     @r2   get_face_verticesMeshBuilder.get_face_vertices  s(     ==>DJJu,=>>>r1   c                t    U R                  U5      n [        U5      $ ! [        [        4 a	    [        s $ f = f)zReturns the normal vector of the face `index` as :class:`~ezdxf.math.Vec3`,
returns the ``NULLVEC`` instance for degenerated  faces.
)r  r   
ValueErrorru   r   )r   rV   rM   s      r2   get_face_normalMeshBuilder.get_face_normal  s>     %%e,	%d++-. 	N	s   
 77c              #     #    U R                  5        H  n [        U5      v   M     g! [        [        4 a    [        v    M1  f = f7f)zPYields all face normals, yields the ``NULLVEC`` instance for degenerated
faces.
N)r   r   r	  ru   r   )r   rM   s     r2   r   MeshBuilder.face_normals  sE      **,D(.. -  12 s%   A)AAAAAc              #     #    U R                   nU R                   H  nU Vs/ s H  o1U   PM	     snv   M     gs  snf 7f)z%Yields all faces as list of vertices.Nr   )r   r   rM   rV   s       r2   r   MeshBuilder.faces_as_vertices  s6     MMJJD)-.U8.. .s    ?:?c              #  J   #    [        U R                  5       Sh  vN   g N7f)zhYields all faces as sequence of integers where the first vertex
is not coincident with the last vertex.
N)rN   rL   r   s    r2   rN   MeshBuilder.open_faces  s      djj)))s   #!#c                X    U R                   R                  U R                  U5      5        g)aK  Add a face as vertices list to the mesh. A face requires at least 3
vertices, each vertex is a ``(x, y, z)`` tuple or
:class:`~ezdxf.math.Vec3` object. The new vertex indices are stored as
face in the :attr:`faces` list.

Args:
    vertices: list of at least 3 vertices ``[(x1, y1, z1), (x2, y2, z2),
        (x3, y3, y3), ...]``

N)rL   rS   add_vertices)r   rn   s     r2   add_faceMeshBuilder.add_face  s!     	

$++H56r1   c                    [        U R                  5      nU R                  R                  [        R                  " U5      5        [        [        U[        U R                  5      5      5      $ )a  Add new vertices to the mesh, each vertex is a ``(x, y, z)`` tuple
or a :class:`~ezdxf.math.Vec3` object, returns the indices of the
`vertices` added to the :attr:`vertices` list.

e.g. adding 4 vertices to an empty mesh, returns the indices
``(0, 1, 2, 3)``, adding additional 4 vertices returns the indices
``(4, 5, 6, 7)``.

Args:
    vertices: list of vertices, vertex as ``(x, y, z)`` tuple or
        :class:`~ezdxf.math.Vec3` objects

Returns:
    tuple: indices of the `vertices` added to the :attr:`vertices` list

)rJ   rn   extendr   generaterT   r\   )r   rn   start_indexs      r2   r  MeshBuilder.add_vertices  sH    " $--(T]]845U;DMM(:;<<r1   Nc                8  ^ Ub,  [         R                  " UR                  5      nUR                  nUc  [	        S5      eU=(       d    / nU R                  U5      m[        U5       H1  nU R                  R                  [        U4S jU 5       5      5        M3     g)a  Add another mesh to this mesh.

A `mesh` can be a :class:`MeshBuilder`, :class:`MeshVertexMerger` or
:class:`~ezdxf.entities.Mesh` object or requires the attributes
:attr:`vertices` and :attr:`faces`.

Args:
    vertices: list of vertices, a vertex is a ``(x, y, z)`` tuple or
        :class:`~ezdxf.math.Vec3` object
    faces: list of faces, a face is a list of vertex indices
    mesh: another mesh entity

Nz"Requires vertices or another mesh.c              3  .   >#    U  H
  nTU   v   M     g 7frj   r)   )rl   r  indicess     r2   ro   'MeshBuilder.add_mesh.<locals>.<genexpr>+  s     #H-BGBK-rq   )	r   r   rn   rL   r	  r  rN   rS   rT   )r   rn   rL   r   r{   r  s        @r2   add_meshMeshBuilder.add_mesh  s}    & yy/HJJEABB##H-'.MJJe#H-#HHI /r1   c                ^   U(       a  [        U5      O0 nU R                  nUb  UR                  U5      nUb  UR                  U5      nUR	                  US9nUR                  5        n[        U5      Ul        [        U R                  5      Ul        SSS5        U$ ! , (       d  f       U$ = f)a]  Render mesh as :class:`~ezdxf.entities.Mesh` entity into `layout`.

Args:
    layout: :class:`~ezdxf.layouts.BaseLayout` object
    dxfattribs: dict of DXF attributes e.g. ``{'layer': 'mesh', 'color': 7}``
    matrix: transformation matrix of type :class:`~ezdxf.math.Matrix44`
    ucs: transform vertices by :class:`~ezdxf.math.UCS` to :ref:`WCS`

N
dxfattribs)dictrn   transform_verticespoints_to_wcsr  	edit_datar   rL   )r   layoutr#  matrixucsrn   r   datas           r2   render_meshMeshBuilder.render_mesh-  s      *4T*%
==00:H?((2H*5^^ !NDMdjj)DJ	 
  
 s   (+B
B,c                T   U(       a  [        U5      O0 nU R                  5        Ho  n[        U5      nUS:  a  M  [        R                  " U5      U-  n [        U5      nU(       a  US   U-
  R                  U-  n	OUn	UR                  XwX-  -   US9  Mq     g! [         a     M  f = f)a  Render face normals as :class:`~ezdxf.entities.Line` entities into
`layout`, useful to check orientation of mesh faces.

Args:
    layout: :class:`~ezdxf.layouts.BaseLayout` object
    length: visual length of normal, use length < 0 to point normals in
        opposite direction
    relative: scale length relative to face size if ``True``
    dxfattribs: dict of DXF attributes e.g. ``{'layer': 'normals', 'color': 6}``

rH   r   r"  N)	r$  r   rJ   r   rs   r   ru   	magnitudeadd_line)
r   r(  lengthrelativer#  rM   r'   centern_lengths
             r2   render_normalsMeshBuilder.render_normalsK  s    $ *4T*%
**,DIEqyXXd^e+F&t, 7V+66? OOFQ[$8ZOP - % s   B
B'&B'c                `    U " 5       n[        U[        5      (       d   eUR                  US9  U$ )a  Create new mesh from other mesh as class method.

Args:
    other: `mesh` of type :class:`MeshBuilder` and inherited or DXF
        :class:`~ezdxf.entities.Mesh` entity or any object providing
        attributes :attr:`vertices`, :attr:`edges` and :attr:`faces`.

r   )
isinstancer   r  clsotherr   s      r2   	from_meshMeshBuilder.from_meshm  s1     u$,,,,5!r1   c           
        UR                  5       S:w  a  [        SUR                  5        35      eU " 5       n[        U[        5      (       d   eUR                  (       a<  UR                  5       u  p4U H"  nUR                  UR                  5       5        M$     U$ UR                  (       a  UR                  5       n[        UR                  R                  S-
  5       H]  n[        UR                  R                  S-
  5       H4  nUR                  XgU4   XgUS-   4   XgS-   US-   4   XgS-   U4   45        M6     M_     U$ [        S5      e)zhCreate new mesh from a  :class:`~ezdxf.entities.Polyface` or
:class:`~ezdxf.entities.Polymesh` object.

POLYLINEzUnsupported DXF type: rR   zNot a polymesh or polyface.)dxftype	TypeErrorr:  r   is_poly_face_meshindexed_facesr  pointsis_polygon_meshget_mesh_vertex_cacher\   dxfm_countn_count)	r<  r=  r   _rL   rM   rn   mr4  s	            r2   from_polyfaceMeshBuilder.from_polyface}  s4    ==?j(4U]]_4EFGGu$,,,,""**,HAdkkm,    ""224H599,,q01uyy00145AMM$TN$AX.$UAE\2$UAX.	 6 2  9::r1   c                   U(       a  [        U5      O0 nUR                  US9n[        R                  U 5      nUb  UR	                  U5        Ub  UR	                  UR
                  5        UR                  UR                  SS9US9  U$ )aa  Render mesh as :class:`~ezdxf.entities.Polyface` entity into
`layout`.

Args:
    layout: :class:`~ezdxf.layouts.BaseLayout` object
    dxfattribs: dict of DXF attributes e.g. ``{'layer': 'mesh', 'color': 7}``
    matrix: transformation matrix of type :class:`~ezdxf.math.Matrix44`
    ucs: transform vertices by :class:`~ezdxf.math.UCS` to :ref:`WCS`

r"     max_vertex_count)r$  add_polyfaceMeshTransformerr   	transformr)  append_facesr   )r   r(  r#  r)  r*  polyfacets          r2   render_polyfaceMeshBuilder.render_polyface  s    " *4T*%
&&*&=((.KK?KK

#NNAN.! 	 	
 r1   c                    SSK Jn  U(       a  [        U5      O0 nUR                  US9nUR	                  U 5      nUR                  XE/5        U$ )a  Render mesh as :class:`~ezdxf.entities.Solid3d` entity into `layout`.

This is an **experimental** feature to create simple 3DSOLID entities from 
polyhedrons.

The method supports closed and open shells.  A 3DSOLID entity can contain 
multiple shells.  Separate the meshes beforehand by the method 
:meth:`separate_meshes` if required.  The normals vectors of all faces should 
point outwards. Faces can have more than 3 vertices (ngons) but non-planar 
faces and concave faces will cause problems in some CAD applications.  The 
method :meth:`mesh_tesselation` can help to break down the faces into triangles.

Requires a valid DXF document for `layout` and DXF version R2000 or newer.

Args:
    layout: :class:`~ezdxf.layouts.BaseLayout` object
    dxfattribs: dict of DXF attributes e.g. ``{'layer': 'mesh', 'color': 7}``

Raises:
    DXFValueError: valid DXF document required, if :attr:`layout.doc` is ``None``
    DXFVersionError: invalid DXF version

.. versionadded:: 1.2.0

r   )apir"  )
ezdxf.acisr]  r$  add_3dsolidbody_from_mesh
export_dxf)r   r(  r#  acissolid3dbodys         r2   render_3dsolidMeshBuilder.render_3dsolid  sK    4 	+)3T*%
$$
$;""4((r1   c                   U(       a  [        U5      O0 n[        R                  U 5      nUb  UR                  U5        Ub  UR                  UR                  5        UR                  SS9 H  nUR                  XbS9  M     g)aa  Render mesh as :class:`~ezdxf.entities.Face3d` entities into
`layout`.

Args:
    layout: :class:`~ezdxf.layouts.BaseLayout` object
    dxfattribs: dict of DXF attributes e.g. ``{'layer': 'mesh', 'color': 7}``
    matrix: transformation matrix of type :class:`~ezdxf.math.Matrix44`
    ucs: transform vertices by :class:`~ezdxf.math.UCS` to :ref:`WCS`

NrQ  rR  r"  )r$  rU  r   rV  r)  r   
add_3dface)r   r(  r#  r)  r*  rY  rM   s          r2   render_3dfacesMeshBuilder.render_3dfaces  so    " *4T*%
((.KK?KK

#NNAN6Dd: 7r1   c                    U " 5       n[        U[        5      (       d   e[        UR                  5      Ul        [        UR                  5      Ul        U$ )zCreate new mesh from other mesh builder, faster than
:meth:`from_mesh` but supports only :class:`MeshBuilder` and inherited
classes.

)r:  r   r   rn   rL   r;  s      r2   r   MeshBuilder.from_builder  sE     u$,,,, U^^,%++&
r1   c                    U n[        U5       H#  n[        UR                  UR                  5      nM%     [        R                  U5      $ )zReturns a new :class:`MeshBuilder` object with merged adjacent
coplanar faces.

The faces have to share at least two vertices and have to have the
same clockwise or counter-clockwise vertex order.

The current implementation is not very capable!

)r\   _merge_adjacent_coplanar_facesrn   rL   rU  r   )r   passesr   rL  s       r2   merge_coplanar_faces MeshBuilder.merge_coplanar_faces  s<     vA1$--LD ++D11r1   c                    U n[        [        U5      S5      nUS:  a  [        X25      nUS-  nUS:  a  M  [        R	                  U5      $ )zReturns a new :class:`MeshTransformer` object with all faces subdivided.

Args:
     level: subdivide levels from 1 to max of 5
     quads: create quad faces if ``True`` else create triangles
   r   rR   )minr&   
_subdividerU  r   )r   levelquadsr   s       r2   	subdivideMeshBuilder.subdivide  sO     CJ"aid*DQJE ai ++D11r1   c                \    [        US9nUR                  U S9  [        R                  U5      $ )zReturns a new mesh with optimized vertices. Coincident vertices are
merged together and all faces are open faces (first vertex != last
vertex). Uses internally the :class:`MeshVertexMerger` class to merge
vertices.
)	precisionr9  )MeshVertexMergerr  rU  r   )r   r{  r   s      r2   optimize_verticesMeshBuilder.optimize_vertices#  s-      )44 ++D11r1   c              #  T   #    [        U R                  5       U5       Sh  vN   g N7f)a[  Yields all faces as sequence of :class:`~ezdxf.math.Vec3` instances,
where all ngons which have more than `max_vertex_count` vertices gets
subdivided.
In contrast to the :meth:`tessellation` method, creates this method a
new vertex in the centroid of the face. This can create a more regular
tessellation but only works reliable for convex faces!
N)r   r   )r   rS  s     r2   r   MeshBuilder.subdivide_ngons-  s!      #4#9#9#;=MNNNs   (&(c              #     #    SSK Jn  U R                  5        H(  n[        U5      U::  a  Uv   M  U" U5       Sh  vN   M*     g N	7f)a  Yields all faces as sequence of :class:`~ezdxf.math.Vec3` instances,
each face has no more vertices than the given `max_vertex_count`. This
method uses the "ear clipping" algorithm which works with concave faces
too and does not create any additional vertices.
r   )mapbox_earcut_3dN)ezdxf.math.triangulationr  r   rJ   )r   rS  r  rM   s       r2   r   MeshBuilder.tessellation7  sB      	>**,D4y,,
+D111	 - 2s   :AA
Ac                    [        5       nU R                  US9 H  nUR                  U5        M     [        R	                  U5      $ )zReturns a new :class:`MeshTransformer` instance, where each face has
no more vertices than the given `max_vertex_count`.

The `fast` mode uses a shortcut for faces with less than 6 vertices
which may not work for concave faces!
rR  )r|  r   r  rU  r   )r   rS  r   rM   s       r2   mesh_tessellationMeshBuilder.mesh_tessellationE  sC      !%%7G%HDMM$ I++D11r1   c                J    [        [        U R                  5      5      U l        g)zEFlips the normals of all faces by reversing the vertex order inplace.N)r   r   rL   r   s    r2   flip_normalsMeshBuilder.flip_normalsQ  s    +DJJ78
r1   c                *    [        [        U 5      5      $ )zA single :class:`MeshBuilder` instance can store multiple separated
meshes. This function returns this separated meshes as multiple
:class:`MeshTransformer` instances.
)r   separate_meshesr   s    r2   r  MeshBuilder.separate_meshesU  s    
 OD)**r1   c                H    [        [        U R                  SS95      U l        g)zRemoves duplicated vertex indices from faces and stores all faces as
open faces, where the last vertex is not coincident with the first
vertex.
FrO   N)r   rW   rL   r   s    r2   rW   MeshBuilder.normalize_faces\  s    
 /$**EBC
r1   c                    [        XS9$ )aN  Returns a :class:`FaceOrientationDetector` or short `fod` instance.
The forward orientation is defined by the `reference` face which is
0 by default.

The `fod` can check if all faces are reachable from the reference face
and if all faces have the same orientation. The `fod` can be reused to
unify the face orientation of the mesh.

	reference)FaceOrientationDetector)r   r  s     r2   face_orientation_detector%MeshBuilder.face_orientation_detectorc  s     'tAAr1   fodc                   [        XS9$ )ap  Returns a new :class:`MeshTransformer` object with unified
face normal vectors of all faces.
The forward direction (not necessarily outwards) is defined by the
face-normals of the majority of the faces.
This function can not process non-manifold meshes (more than two faces
are connected by a single edge) or multiple disconnected meshes in a
single :class:`MeshBuilder` object.

It is possible to pass in an existing :class:`FaceOrientationDetector`
instance as argument `fod`.

Raises:
    NonManifoldError: non-manifold mesh
    MultipleMeshesError: the :class:`MeshBuilder` object contains multiple disconnected meshes

r  )unify_face_normals_by_majority)r   r  s     r2   unify_face_normalsMeshBuilder.unify_face_normalso  s    & .d<<r1   F)force_outwardsr  c               >    [        XUS9nU(       a  [        XA5        U$ )a  Returns a new :class:`MeshTransformer` object with unified
face normal vectors of all faces.
The forward direction (not necessarily outwards) is defined by the
reference face, which is the first face of the `mesh` by default.
This function can not process non-manifold
meshes (more than two faces are connected by a single edge) or multiple
disconnected meshes in a single :class:`MeshBuilder` object.

The outward direction of all face normals can be forced by stetting
the argument `force_outwards` to ``True`` but this works only for closed
surfaces, and it's time-consuming!

It is not possible to check for a closed surface as long the face normal
vectors are not unified. But it can be done afterward by the attribute
:meth:`MeshDiagnose.is_closed_surface` to see if the result is
trustworthy.

It is possible to pass in an existing :class:`FaceOrientationDetector`
instance as argument `fod`.

Args:
    reference: index of the reference face
    force_outwards: forces face-normals to point outwards, this works
        only for closed surfaces, and it's time-consuming!
    fod: :class:`FaceOrientationDetector` instance

Raises:
    ValueError: non-manifold mesh or the :class:`MeshBuilder` object
        contains multiple disconnected meshes

)r  r  )unify_face_normals_by_reference%_force_face_normals_pointing_outwards)r   r  r  r  r   s        r2   r  +MeshBuilder.unify_face_normals_by_reference  s!    L /tcR1$Br1   )rL   rn   )r   Noner   )r   r   )rV   r&   r   r   )rV   r&   r   r   )r   Iterator[Vec3])r   zIterator[list[Vec3]]r   Iterator[Face])rn   Iterable[UVec]r   r  rn   r  r   r3   )NNN)rn   zOptional[list[Vec3]]rL   zOptional[list[Face]]r   r  )r(  r!   r)  zOptional[Matrix44]r*  zOptional[UCS])rR   TN)r(  r!   r1  r   )r<  Type[T]r=  zUnion[MeshBuilder, Mesh]r   r"   )r<  r  r=  zUnion[Polymesh, Polyface]r   r"   rj   )r(  r!   r   r    )r<  r  r=  r   r   r"   )rR   )ro  r&   r   rU  )rR   T)rv  r&   r   rU     )r{  r&   r   rU  rQ  )r   Iterator[Sequence[Vec3]])rS  r&   r   r  )rS  r&   r   rU  )r   zlist[MeshTransformer]r   )r  r&   r   r  )r  !Optional[FaceOrientationDetector]r   rU  )r  r&   r  r  r   rU  )'r*   r+   r,   r-   r.   r   r   r   r   r  r
  r   r   rN   r  r  r  r,  r6  classmethodr>  rN  rZ  re  ri  r   rp  rx  r}  r   r   r  r  r  rW   r  r  r  r0   r)   r1   r2   r   r     s   	-
*'"?/*7=. *.&*	J&J $J
 
JD %)!! #	
 B  Q! Q  QD    B %)!! #	
 < J %)!;!; #	;
 ;4  222O2
29+D
B ;?=7=	=. ) 15))
 /) 
) )r1   r   c                  p    \ rS rSrSrSS jrSSS jjrSSS jjrSS jrSS jr	SS jr
SS	 jrSS
 jrSrg)rU  i  z3A mesh builder with inplace transformation support.c                X    [        UR                  U R                  5      5      U l        U $ )zTransform mesh inplace by applying the transformation `matrix`.

Args:
    matrix: 4x4 transformation matrix as :class:`~ezdxf.math.Matrix44`
        object

)r   r%  rn   )r   r)  s     r2   rV  MeshTransformer.transform  s$     V66t}}EFr1   c                    [        U[        [        45      (       a  [        XU5      nO[        U5      nU R                   Vs/ s H  oTU-   PM	     snU l        U $ s  snf )zTranslate mesh inplace.

Args:
    dx: translation in x-axis or translation vector
    dy: translation in y-axis
    dz: translation in z-axis

)r:  r   r&   r   rn   )r   dxdydzrY  r   s         r2   	translateMeshTransformer.translate  sP     b5#,''RR ARA(,61Q6 7s   Ac                    U R                    VVVs/ s H  u  pEn[        XA-  XR-  Xc-  5      PM     snnnU l         U $ s  snnnf )z|Scale mesh inplace.

Args:
    sx: scale factor for x-axis
    sy: scale factor for y-axis
    sz: scale factor for z-axis

)rn   r   )r   sxsyszr   r   r   s          r2   scaleMeshTransformer.scale  s>     FJ]]S]'!afafaf5]S Ts    =c                X    U R                    Vs/ s H  o"U-  PM	     snU l         U $ s  snf )zNScale mesh uniform inplace.

Args:
    s: scale factor for x-, y- and z-axis

rn   )r   sr   s      r2   scale_uniformMeshTransformer.scale_uniform  s*     )-61Q6 7s   'c                    [        [        R                  " U5      R                  U R                  5      5      U l        U $ )z^Rotate mesh around x-axis about `angle` inplace.

Args:
    angle: rotation angle in radians

)r   r   x_rotater%  rn   r   angles     r2   rotate_xMeshTransformer.rotate_x  /     X..u5HHWXr1   c                    [        [        R                  " U5      R                  U R                  5      5      U l        U $ )z^Rotate mesh around y-axis about `angle` inplace.

Args:
    angle: rotation angle in radians

)r   r   y_rotater%  rn   r  s     r2   rotate_yMeshTransformer.rotate_y  r  r1   c                    [        [        R                  " U5      R                  U R                  5      5      U l        U $ )z^Rotate mesh around z-axis about `angle` inplace.

Args:
    angle: rotation angle in radians

)r   r   z_rotater%  rn   r  s     r2   rotate_zMeshTransformer.rotate_z  r  r1   c                    [        [        R                  " X5      R                  U R                  5      5      U l        U $ )zRotate mesh around an arbitrary axis located in the origin (0, 0, 0)
about `angle`.

Args:
    axis: rotation axis as Vec3
    angle: rotation angle in radians

)r   r   axis_rotater%  rn   )r   axisr  s      r2   rotate_axisMeshTransformer.rotate_axis  s4       -@@O
 r1   r  N)r)  r   )r   r   r   )r  zUnion[float, UVec]r  r   r  r   )rR   rR   rR   )r  r   r  r   r  r   )r  r   )r  r   )r  r   r  r   )r*   r+   r,   r-   r.   rV  r  r  r  r  r  r  r  r0   r)   r1   r2   rU  rU    s-    =	 
r1   rU  r|  c                    [        5       nU R                  5        H7  n[        U5      S:  a  M  [        X15       H  nUR	                  U5        M     M9     U$ )zReturns a new :class:`MeshVertexMerger` object with subdivided faces
and edges.

Args:
     quads: create quad faces if ``True`` else create triangles

rH   )r|  r   rJ   r   r  )r   rw  new_meshrn   rM   s        r2   ru  ru    sR      !H**,x=1"83Dd# 4 -
 Or1   c                  \   ^  \ rS rSrSrSS	U 4S jjjrS
S jrSS jr\SS j5       r	Sr
U =r$ )r|  i"  a  Subclass of :class:`MeshBuilder`

Mesh with unique vertices and no doublets, but needs extra memory for
bookkeeping.

:class:`MeshVertexMerger` creates a key for every vertex by rounding its
components by the Python :func:`round` function and a given `precision`
value. Each vertex with the same key gets the same vertex index, which is
the index of first vertex with this key, so all vertices with the same key
will be located at the location of this first vertex. If you want an average
location of all vertices with the same key use the
:class:`MeshAverageVertexMerger` class.

Args:
    precision: floating point precision for vertex rounding

c                <   > [         TU ]  5         0 U l        Xl        g)zD
Args:
    precision: floating point precision for vertex rounding

Nsuperr   ledgerr{  r   r{  	__class__s     r2   r   MeshVertexMerger.__init__6  s     	')'r1   c                   / nU R                   n[        R                  " U5       H3  nUR                  U5      n UR	                  U R
                  U   5        M5     [        U5      $ ! [         aS    [        U R                  5      nU R                  R	                  U5        X`R
                  U'   UR	                  U5         M  f = f)aI  Add new `vertices` only, if no vertex with identical (x, y, z)
coordinates already exist, else the index of the existing vertex is
returned as index of the added vertices.

Args:
    vertices: list of vertices, vertex as (x, y, z) tuple or
        :class:`~ezdxf.math.Vec3` objects

Returns:
    indices of the added `vertices`

)
r{  r   r  roundrS   r  KeyErrorrJ   rn   rT   )r   rn   r  r{  vertexkeyrV   s          r2   r  MeshVertexMerger.add_vertices@  s     NN	mmH-F,,y)C&t{{3/0 . W~  &DMM*$$V,#(C u%	&s   A''ACCc                     U R                   [        U5      R                  U R                  5         $ ! [         a    [        S[        U5       S35      ef = f)Get index of `vertex`, raises :class:`IndexError` if not found.

Args:
    vertex: ``(x, y, z)`` tuple or :class:`~ezdxf.math.Vec3` object

(internal API)
Vertex  not found.r  r   r  r{  r  rt   strr   r  s     r2   rV   MeshVertexMerger.indexZ  sS    	A;;tF|11$..ABB 	Aws6{m;?@@	As	   03 #Ac                $    U R                  U5      $ z(Create new mesh from other mesh builder.r>  r<  r=  s     r2   r   MeshVertexMerger.from_builderg       }}U##r1   r  r{  r  r{  r&   r  r  r   r   r&   )r=  r   r   r|  r*   r+   r,   r-   r.   r   r  rV   r  r   r0   __classcell__r  s   @r2   r|  r|  "  s1    &( (4A $ $r1   c                  \   ^  \ rS rSrSrSS	U 4S jjjrS
S jrSS jr\SS j5       r	Sr
U =r$ )MeshAverageVertexMergerin  a2  Subclass of :class:`MeshBuilder`

Mesh with unique vertices and no doublets, but needs extra memory for
bookkeeping and runtime for calculation of average vertex location.

:class:`MeshAverageVertexMerger` creates a key for every vertex by rounding
its components by the Python :func:`round` function and a given `precision`
value. Each vertex with the same key gets the same vertex index, which is the
index of first vertex with this key, the difference to the
:class:`MeshVertexMerger` class is the calculation of the average location
for all vertices with the same key, this needs extra memory to keep track of
the count of vertices for each key and extra runtime for updating the vertex
location each time a vertex with an existing key is added.

Args:
    precision: floating point precision for vertex rounding

c                <   > [         TU ]  5         0 U l        Xl        g rj   r  r  s     r2   r    MeshAverageVertexMerger.__init__  s!      	 (r1   c                   / nU R                   n[        R                  " U5       Hr  nUR                  U5      n U R                  U   u  pgU R
                  U   U-  U-   nUS-  nX-  U R
                  U'   Xg4U R                  U'   UR                  U5        Mt     [        U5      $ ! [         aD    [        U R
                  5      nU R
                  R                  U5        US4U R                  U'    Nmf = f)a  Add new `vertices` only, if no vertex with identical ``(x, y, z)``
coordinates already exist, else the index of the existing vertex is
returned as index of the added vertices.

Args:
    vertices: list of vertices, vertex as ``(x, y, z)`` tuple or
    :class:`~ezdxf.math.Vec3` objects

Returns:
    tuple: indices of the `vertices` added to the
    :attr:`~MeshBuilder.vertices` list

rR   )
r{  r   r  r  r  rn   r  rJ   rS   rT   )	r   rn   r  r{  r  r  rV   r'   averages	            r2   r  $MeshAverageVertexMerger.add_vertices  s     NN	mmH-F,,y)C2#{{3/  ==/%76A
'.e$$)>C NN5!! ." W~  .DMM*$$V,$)1:C .s   B&&AC43C4c                     U R                   [        U5      R                  U R                  5         S   $ ! [         a    [        S[        U5       S35      ef = f)r  r   r  r  r  r  s     r2   rV   MeshAverageVertexMerger.index  sX    	A;;tF|11$..AB1EE 	Aws6{m;?@@	As	   36 #Ac                $    U R                  U5      $ r  r  r  s     r2   r   $MeshAverageVertexMerger.from_builder  r  r1   r  r  r  r  r  )r=  r   r   r  r  r  s   @r2   r  r  n  s2    (( (!FA $ $r1   r  c                  0    \ rS rSrSrSS jrSS	S jjrSrg)
_XFacei  )fingerprintr  _orientationc                F    [        U5      U l        Xl        [        U l        g rj   )hashr  r  VEC3_SENTINELr	  )r   r  s     r2   r   _XFace.__init__  s     $W$"/r1   c                   U R                   [        L aR  [        nU R                   Vs/ s H  oAU   PM	     sntpVnU H  n [	        XVU5      R                  U5      n  O   X0l         U R                   $ s  snf ! [         a     MG  f = frj   )r	  r  r   r  r   r  ru   )	r   rn   r{  re   rm   v0v1r   v2s	            r2   re   _XFace.orientation  s    -!K/3||<|!1+|<JBQ"222">"D"DY"OK  !,    =
 ) s   A2A77
BB)r	  r  r  N)r  r3   r  )rn   r   r{  r&   r   r   )r*   r+   r,   r-   	__slots__r   re   r0   r)   r1   r2   r  r    s    :I0
! !r1   r  c           
        0 n/ nU Hi  n[        U5      S:  a  [        S5      e[        U5      nUR                  U5        UR	                  UR                  X5      / 5      R                  U5        Mk     [        5       n[        5       nU GHY  nUR                  U;   a  M  UR                  UR                  5        UR                  nUR                  X5      n	X9   n
[        U5      nU
 H  nUR                  U;   a  M  UR                  [        UR                  5      5      n[        U5      S:  d  MJ  [        U5      [        UR                  5      :X  a  [        X\R                  5      nO [        X\R                  5      nUR                  UR                  5        [        U5      nM     [!        [#        U Vs/ s H  oU   PM	     sn5      5      nUR%                  U5        GM\     U$ ! [        [        4 a     GM  f = fs  snf )NrH   zfound invalid face count < 3rR   )rJ   r	  r  rS   
setdefaultre   r|  setr  addr  intersectionmerge_full_patchmerge_connected_pathsr<   rB   r   remove_colinear_face_verticesr  )rn   rL   r{  oriented_facesextended_facesrM   xfacer   donere   parallel_facesface_setparallel_facecommon_verticesrm   r  s                   r2   rn  rn    s    02N#%Nt9q=;<<te$!!%"3"3H"H"MTT	
  D5D$""#}}''<'4t9+M((D0&33C8M8M4NOO?#a''3}/D/D+EE+D2G2GHD!4T;P;PQ 223t9 , /d0Kd!d0KLMb1  2 K -.BC ! ! 1Ls   ,G*9H*G?>G?c              #    #    S	S jn[        U 5      S:  a  U  S h  vN   g U S   /nU SS   H/  nUR                  US   5      (       a  M  UR                  U5        M1     [        U5      S:  a.  [        U5      S:X  a  UR                  US   5        U S h  vN   g US   n[        n[        nSnUv   U[        L a  US-  n X   nU" XE5      nU[        L a  M  SnUR                  U5        X'S   H7  n	 U" XI5      R                  U5      (       a  U	nM$   Uv   SnUnU" XI5      nU	nM9     U(       d  US   v   g g  GN  N! [         a    Uv    g f = f! [
         a     Mq  f = f7f)
Nc                &    X-
  R                  5       $ rj   )rv   )r  r  s     r2   get_direction4remove_colinear_face_vertices.<locals>.get_direction  s    ""$$r1   rH   r   rR   rI   FT)r  r   r  r   )rJ   iscloserS   r  rt   ru   )
rn   r&  	_verticesr   startprev_vertexcurrent_directionr  yielded_anythingr  s
             r2   r  r    s    % 8}q &a[MIab\yy2''Q  9~y>QYq\*aLEK%K K
}
,q	"/K *%= }
, UL)	U+334EFF$ G
 )%8 * m ] 	 	  		 ! 		sv   E&D<*E&AE&D?*E&E E&E&5E*E&?E&EE&EE&
E#E&"E##E&c                   SS jnU" U 5      nU" U5      nU S   nU S   nU/n  X5   nX;   a  XCpCX:X  a  O UnXW;   a  [        eUR                  U5        M2  [        U5      S:  a  [        eU$ ! [          a    [        ef = f)Nc                j    [        X SS  5       VVs0 s H  u  pX_M	     nnnU S   X0S   '   U$ s  snnf )NrR   r   rI   )zip)pe1e2nodess       r2   build_nodes*merge_connected_paths.<locals>.build_nodes>  s=    &)!qrUm4mFBm4te 5s   /r   rH   )r2  Sequence[int])r  r<   rS   rJ   rB   )	p1p2r6  current_path
other_pathcurrent_nodefinishconnected_path	next_nodes	            r2   r  r  =  s    
 r?LRJa5LUF"^N
	#$2I "'1* ) #"l+   >Q""  	#""	#s   A- -A>c                    [        U 5      n/ n[        U 5       H3  u  pEXS-
     nXS-   U-     nXa;   a  Xq;   a  M"  UR                  U5        M5     U$ )NrR   )rJ   	enumeraterS   )pathpatchr'   new_pathposnodeprevsuccs           r2   r  r  ]  s[    IEHt_	!G}1W%&=T] % Or1   c                  2    \ rS rSrSS jrSS jrS	S jrSrg)
Lumpii  c                    [        5       U l        U/U l        [        U5       H)  u  p#U R                  R	                  X#::  a  X#4OX245        M+     g rj   )r  edgesrL   rY   r  )r   rM   rb   rc   s       r2   r   Lump.__init__j  s?     #
"&
t$DAJJNNQVA6!8 %r1   c                V    U R                   R                  UR                   5      (       + $ rj   )rM  
isdisjointr   r=  s     r2   is_connectedLump.is_connectedq  s    ::((555r1   c                    U R                   R                  UR                   5        U R                  R                  UR                  5        g rj   )rL   r  rM  updaterQ  s     r2   merge
Lump.merget  s.    

%++&

%++&r1   )rM  rL   N)rM   r3   )r=  rK  r   r   )r=  rK  )r*   r+   r,   r-   r   rR  rV  r0   r)   r1   r2   rK  rK  i  s    96'r1   rK  c                    [        U 5      nSnS[        U5      s=:  a  U:w  a6  O  U$ [        U5      n[        U5      nS[        U5      s=:  a
  U:w  a  M.   U$   U$ )Nr   rR   )_merge_lumpsrJ   )lumpsmerged_lumps	prior_lens      r2   merge_lumpsr]  y  sj    &LI
c,
,9
,  %	#L1 c,
,9
,  - r1   c                    / nU  HG  nU H-  nUR                  U5      (       d  M  UR                  U5          M4     UR                  U5        MI     U$ rj   )rR  rV  rS   )rZ  r[  lumpbases       r2   rY  rY    sS    !L D  &&

4  !
 %  r1   c              #    ^#    [        [        S [        U R                  5       5       5      5      n[	        U5      S:  ae  U R
                  mU HR  n[        5       nUR                   H  nUR                  U4S jU 5       5        M      [        R                  U5      v   MT     g[        R                  U 5      v   g7f)zwReturns the separated meshes in a single :class:`MeshBuilder` instance
as multiple :class:`MeshTransformer` instances.
c              3  8   #    U  H  n[        U5      v   M     g 7frj   )rK  r   s     r2   ro   "separate_meshes.<locals>.<genexpr>  s     )UAT$t**ATr   rR   c              3  .   >#    U  H
  nTU   v   M     g 7frj   r)   rk   s     r2   ro   rc    s     84ahqk4rq   N)
r   r]  rN   rL   rJ   rn   r|  r  rU  r   )rM  disconnected_lumpsr_  r   rM   rn   s        @r2   r  r    s      k)UAGGAT)UUV
"::&D#%D

8488 #!..t44	 ' **1--s   B;B>c                J   [         R                  " U 5      [        U 5      -  n[         R                  " U5      [        U5      -  n[        U S   U S   U S   5      S-  n[        US   US   US   5      S-  nX$-   nX5-   nUR	                  U5      nUR	                  U5      n	X:  $ )zReturns ``True`` if the normals of the two faces are pointing
away from the center of the two faces.

.. warning::

    This does not work for any arbitrary face pair!

r   rR   rr   g      ?)r   rs   rJ   r   distance)
f0f1c0c1n0n1e0r3  d0d1s
             r2   have_away_pointing_normalsrq    s     
"B	B	"B	B	"Q%A1	.	4B	"Q%A1	.	4B	B	B	RB	RB7Nr1   c                ~    SSK Jn  U" S5      R                  U 5      nUR                  5       u  n      pE[	        X55      $ )a  Returns the state of face-normals of the unit-cube after the
transformation `m` was applied.

    - ``True``: face-normals pointing outwards
    - ``False``: face-normals pointing inwards

The state before the transformation is outward-pointing face-normals.

rR   )cubeT)formsrs  rV  r   rq  )rM  rs  	unit_cubebottomrL  tops         r2   !face_normals_after_transformationrx    s?     T
$$Q'I'99;FAq!Q%f22r1   c                    0 nU  H6  n[        U5       H$  nUR                  U/ 5      R                  U5        M&     M8     U$ rj   )rY   r  rS   )rL   mappingrM   rd   s       r2   _make_edge_mappingr{    sB    &(Gt$DtR(//5 %  Nr1   c                      \ rS rSrSrSSS jjr\SS j5       r\SS j5       r\SS j5       r	\SS j5       r
\SS j5       r\SS	 j5       rSSS
 jjr\SS j5       rSS jrSrg)r  i  a  
Helper class for face orientation and face normal vector detection. Use the
method :meth:`MeshBuilder.face_orientation_detector` to create an instance.

The face orientation detector classifies the faces of a mesh by their
forward or backward orientation.
The forward orientation is defined by a reference face, which is the first
face of the mesh by default and this orientation is not necessarily outwards.

This class has some overlapping features with :class:`MeshDiagnose` but it
has a longer setup time and needs more memory than :class:`MeshDiagnose`.

Args:
    mesh: source mesh as :class:`MeshBuilder` object
    reference: index of the reference face

c                    Xl         [        UR                  5      U l        X l        SU l        [        5       U l        [        5       U l        U R                  U5        g )NT)
r   r{  rL   edge_mappingr  r   r$  forwardbackwardclassify_faces)r   r   r  s      r2   r    FaceOrientationDetector.__init__  sE    
4Ftzz4R"(,)-I&r1   c                2    [        U R                  5      S:H  $ )z^Returns ``True`` if all reachable faces are forward oriented
according to the reference face.
r   )rJ   r  r   s    r2   has_uniform_face_normals0FaceOrientationDetector.has_uniform_face_normals  s    
 4==!Q&&r1   c                l    [        U R                  R                  5      [        U R                  5      :H  $ )zmReturns ``True`` if all faces are reachable from the reference face
same as property :attr:`is_single_mesh`.
)rJ   r   rL   rs   r'   r   s    r2   all_reachable%FaceOrientationDetector.all_reachable  s&    
 4::##$DJJ77r1   c                    U R                   $ )zZReturns ``True`` if only a single mesh is present same as property
:attr:`all_reachable`.
)r  r   s    r2   is_single_mesh&FaceOrientationDetector.is_single_mesh  s    
 !!!r1   c                V    [        U R                  5      [        U R                  5      4$ )z9Returns the count of forward and backward oriented faces.)rJ   r  r  r   s    r2   r'   FaceOrientationDetector.count  s!     4<< #dmm"444r1   c                H    [        U R                  R                  5       5      $ )z"Yields all forward oriented faces.)iterr  r   r   s    r2   forward_faces%FaceOrientationDetector.forward_faces  s     DLL'')**r1   c                H    [        U R                  R                  5       5      $ )z#Yields all backward oriented faces.)r  r  r   r   s    r2   backward_faces&FaceOrientationDetector.backward_faces  s     DMM((*++r1   c                  ^ ^^^	^
^ SU
4S jjnSU4S jjnS	UU
U4S jjmS	UU	U 4S jjn[        U5      T l        ST l        T R                  m	[	        5       m
[	        5       mT R
                  R                  U   S4/m[        T5      (       aE  TR                  S5      u  pVU(       a	  U" U5        OU" U5        U" XV5        [        T5      (       a  ME  T
T l	        TT l
        g)
zDetect the forward and backward oriented faces.

The forward and backward orientation has to be defined by a `reference`
face.
c                "   > U T[        U 5      '   g rj   id)fr  s    r2   add_forward;FaceOrientationDetector.classify_faces.<locals>.add_forward  s    GBqENr1   c                "   > U T[        U 5      '   g rj   r  )r  r  s    r2   add_backward<FaceOrientationDetector.classify_faces.<locals>.add_backward  s    HRUOr1   c                \   > [        U 5      nUT;  a  UT;  a  TR                  X45        g g g rj   )r  rS   )r  re   r  r  r  process_facess      r2   add_face_to_processCFaceOrientationDetector.classify_faces.<locals>.add_face_to_process  s4    Q%C'!c&9$$a%56 ':!r1   c                  > [        U 5       He  nTU   n[        U5      S:  a  U H  nX@L a  M	  T" XA(       + 5        M      TUS   US   4   n[        U5      S:X  a  T" US   U5        M^  STl        Mg     g ! [         a     Mw  f = f)NrR   r   F)rY   rJ   r  r   )r  re   rd   linked_faceslinked_facer  r~  r   s        r2   #add_adjacent_faces_to_process_queueSFaceOrientationDetector.classify_faces.<locals>.add_adjacent_faces_to_process_queue#  s    "1+D1|$q('3&+$+KI (4#/Qa0@#AL |$)'QE (-D$) &   s   A77
BBTr   N)r  r3   )r  r3   re   r   )r&   r  r   r~  r$  r   rL   rJ   popr  r  )r   r  r  r  r  rM   face_orientationr  r  r~  r  r  s   `      @@@@@r2   r  &FaceOrientationDetector.classify_faces  s    		 	7 	7
	- 	-. Y((#'6$(F37::3C3CI3NPT2U1V-  %2%6%6q%9"DD!T"/G -    r1   c                   U R                   (       d  g/ nU R                  nUR                  5        HL  nUR                  X15      nUS   US   4nUR                  XQ5      n[	        U5      [	        U5      -   S:w  d  ML    g   g)a  Returns ``True`` if the mesh has a closed surface.
This method does not require a unified face orientation.
If multiple separated meshes are present the state is only ``True`` if
**all** meshes have a closed surface.

Returns ``False`` for non-manifold meshes.

FrR   r   rr   T)r   r~  r   r_   rJ   )r   emptyr~  rd   forward_connected_facesreversed_edgebackward_connected_facess          r2   r   )FaceOrientationDetector.is_closed_surfaceM  s     (( %%'D&2&6&6t&C# GT!W,M'3'7'7'M$*+c2J.KKqP ( r1   c                    SSK Jn  [        U R                  R	                  5       5      nX R
                     nU" X#5      $ )zReturns ``True`` if the normal vector of the reference face is
pointing outwards. This works only for meshes with unified faces which
represent a closed surfaces, and it's a time-consuming calculation!

r    is_face_normal_pointing_outwards)
ezdxf.mathr  r   r   r   r  )r   r  rL   reference_faces       r2   #is_reference_face_pointing_outwards;FaceOrientationDetector.is_reference_face_pointing_outwardsd  s6     	@TZZ1134~~./FFr1   )r   r  r~  r  r   r  Nr  )r   r   r  r&   r   )r   ztuple[int, int]r  )r  r&   r   r  )r*   r+   r,   r-   r.   r   r   r  r  r  r'   r  r  r  r   r  r0   r)   r1   r2   r  r    s    $' ' ' 8 8 " " 5 5 + + , ,:!x  ,
Gr1   r  N)r  r  c               @    Uc  [        X5      nSS jn[        XU5      $ )ae  Unifies the orientation of all faces of a :class:`MeshBuilder` object.
The forward orientation is defined by a reference face, which is the first
face of the `mesh` by default. This function can not process non-manifold
meshes (more than two faces are connected by a single edge) or multiple
disconnected meshes in a single :class:`MeshBuilder` object.
Returns always a copy of the source `mesh` as :class:`MeshTransformer`
object.

Args:
    mesh: source :class:`MeshBuilder` object
    fod: an already created :class:`FaceOrientationDetector`
        instance or ``None`` to create one internally.
    reference: index of the reference face for the internally created
        :class:`FaceOrientationDetector` instance

Raises:
    NonManifoldError: non-manifold mesh
    MultipleMeshesError: :class:`MeshBuilder` object contains multiple
        disconnected meshes

c                    U R                   $ rj   )r  )detectors    r2   backward_selector:unify_face_normals_by_reference.<locals>.backward_selector  s       r1   r  r  r  _unify_face_normals)r   r  r  r  s       r2   r  r  q  s(    6 {%d6! t*;<<r1   r  c               @    Uc  [        U 5      nSS jn[        XU5      $ )a  Unifies the orientation of all faces of a :class:`MeshBuilder` object.
The forward orientation is defined by the orientation of the majority of
the faces.
This function can not process non-manifold meshes (more than two faces are
connected by a single edge) or multiple disconnected meshes in a single
:class:`MeshBuilder` object.
Returns always a copy of the source `mesh` as :class:`MeshTransformer`
object.

Args:
    mesh: source :class:`MeshBuilder` object
    fod: an already created :class:`FaceOrientationDetector`
        instance or ``None`` to create one internally.

Raises:
    NonManifoldError: non-manifold mesh
    MultipleMeshesError: :class:`MeshBuilder` object contains multiple
        disconnected meshes

c                X    U R                   u  pX:  a  U R                  $ U R                  $ rj   )r'   r  r  )r  count0count1s      r2   r  9unify_face_normals_by_majority.<locals>.backward_selector  s)    !$*$4x  J(:J:JJr1   r  r  )r   r  r  s      r2   r  r    s)    2 {%d+K t*;<<r1   c                   UR                   (       d  [        S5      eUR                  (       d  [        SUR                   35      e[        5       n[        U R                  5      Ul        UR                  (       ds  U" U5      n/ nU R                   HQ  n[        U5      U;   a%  UR                  [        [        U5      5      5        M7  UR                  [        U5      5        MS     XSl	        U$ [        U R                  5      Ul	        U$ )zUnifies all faces of a MeshBuilder. The backward_selector() function
returns the face ledger which represents the backward oriented faces.
non-manifold meshz1not all faces are reachable from reference face #)r   rE   r  r?   r  rU  r   rn   r  rL   r  rS   rT   r   )r   r  r  r  r  rL   rM   s          r2   r  r    s     ??"#677!?O
 	
  HT]]+H''$S)JJD$x8#U8D>23U4[)	 
  O djj)Or1   c                    SSK Jn  [        U R                  5       5      nX1   nU" X45      (       d  U R	                  5         gg)zDetect the orientation of the reference face and flip all face normals
if required.

Requirements: all face normals have to be unified and the mesh has a closed
surface

r   r  N)r  r  r   r   r  )r   r  r  rL   r  s        r2   r  r    s?     <'')*E%N+EBB Cr1   c                   U R                  5       nUR                  5         UR                  SS9nUR                  (       d  [	        S5      eUR
                  (       d&  [        [        U5      5      S   nUR                  SS9nUR                  (       d  [        XS9nUR                  5       nUR                  (       a%  UR                  5       (       d  UR                  5         U$ )aV  Returns a new mesh with these properties:

    - one mesh: removes all meshes except the first mesh starting at the
      first face
    - optimized vertices: merges shared vertices
    - normalized faces: removes duplicated face vertices and faces with less
      than 3 vertices
    - open faces: all faces are open faces where first vertex != last vertex
    - unified face-orientation: all faces have the same orientation (ccw or cw)
    - ccw face-orientation: all face-normals are pointing outwards if the
      mesh has a closed surface

Raises:
    NonManifoldMeshError: non-manifold mesh

r   r  r  r  )r}  rW   r  r   rE   r  r   r  r  r  r   r  r  )r   r  r  s      r2   normalize_meshr    s    " %%'H

,
,q
,
9C??"#67712150010=''28E002
S%L%L%N%NOr1   )rL   Iterable[Face]r   r  )rL   zlist[Sequence[int]]r   Iterator[Sequence[int]])rL   r  r   zIterator[Edge])rM   r3   r   r   )rL   r  r   r5   )rn   r   rL   r   r   r   )rL   zSequence[Sequence[int]]r   r  )rb   r   rc   r   r   r   r   r   )r   r   r   r   )T)r   r|  r  )rn   z
list[Vec3]rL   z
list[Face]r{  r&   r   r|  )rn   r   r   r  )r9  r8  r:  r8  r   r8  )rC  r8  rD  r8  )rZ  zIterable[Lump]r   z
list[Lump])rM  r   r   zIterator[MeshTransformer])rh  r   ri  r   r   r   )rM  r   r   r   )rL   r  r   zdict[Edge, list[Face]])r   r   r  r  r  r&   r   rU  )r   r   r  r  r   rU  )r   r   r  r  r   rU  r  )r   r   r  r&   r   r  )r   r   r   rU  )T
__future__r   typingr   r   r   r   r   r	   r
   r   r   r   r   typing_extensionsr   r  r   r   r   r   r   r   r   r   r   r   r   r   r   ezdxf.entitiesr   r   r   r    ezdxf.eztypesr!   r"   r$   r&   r3   r/   r4   r5   	Exceptionr7   r<   r?   rB   rE   rN   rW   rZ   rY   rf   r   r   r   r   r   r   rU  ru  r|  r  r  rn  r  r  r  r  rK  r]  rY  r  rq  rx  r{  r  r  r  r  r  r  r)   r1   r2   <module>r     s   #    (     @@/CLz  3-i S/i !D(N+	9 +	y 		' 		* 		+ 		+ 	
   	($4800%30
0f$"$$$.z zzE EP^k ^B"I${ I$XP$k P$f! !. ?@))!+)8;))X Q14n@	' ' 	.&,3$]G ]GF .2	!=
!= 
+!= 	!=
 !=N .2 =
 = 
+ = 	 =F
	  	@ )*
"%	$"r1   