
    Og                       U d dl mZ d dlmZmZmZmZmZmZm	Z	m
Z
mZmZmZ d dlmZ d dlmZmZmZmZmZmZmZmZmZmZmZmZmZ e	rd dlmZm Z m!Z!m"Z" d dl#m$Z$  ed      Z% G d d	e      Z&ee'   Z(d
e)d<   e
e'e'f   Z*d
e)d<   ee*e&f   Z+d
e)d<    G d de,      Z- G d de-      Z. G d de-      Z/ G d de-      Z0 G d de-      Z1dFdZ2dd	 	 	 dGdZ3dHdZ4dIdZ5dJdZ6	 	 	 	 	 	 dKdZ7	 	 	 	 dLd Z8dMd!Z9dNd"Z: G d# d$      Z; G d% d&      Z< G d' d(e<      Z=dOdPd*Z> G d+ d)e<      Z? G d, d-e<      Z@ G d. d/      ZA	 dQ	 	 	 	 	 	 	 dRd0ZB ed d d       ZCdSd1ZDdTd2ZEdUd3ZF G d4 d5      ZGdVd6ZHdVd7ZIdWd8ZJdXd9ZKdYd:ZLdZd;ZM G d< d=      ZNd>d d?	 	 	 	 	 	 	 d[d@ZOd>dA	 	 	 	 	 d\dBZP	 	 	 	 	 	 d]dCZQ	 d^	 	 	 	 	 d_dDZRd`dEZSy>)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                  &    e Zd ZU dZded<   ded<   y)EdgeStatzNamed tuple of edge statistics.intcountbalanceN)__name__
__module____qualname____doc____annotations__     V/var/www/html/public_html/myphp/venv/lib/python3.12/site-packages/ezdxf/render/mesh.pyr$   r$   )   s    )JLr.   r$   r   FaceEdge	EdgeStatsc                      e Zd Zy)MeshBuilderErrorNr(   r)   r*   r-   r.   r/   r4   r4   5       r.   r4   c                      e Zd Zy)NodeMergingErrorNr5   r-   r.   r/   r8   r8   9   r6   r.   r8   c                      e Zd Zy)MultipleMeshesErrorNr5   r-   r.   r/   r:   r:   =   r6   r.   r:   c                      e Zd Zy)DegeneratedPathErrorNr5   r-   r.   r/   r<   r<   A   r6   r.   r<   c                      e Zd Zy)NonManifoldMeshErrorNr5   r-   r.   r/   r>   r>   E   r6   r.   r>   c              #  f   K   | D ](  }t        |      dk  r|d   |d   k(  r|dd  %| * yw)znYields all faces with more than two vertices as open faces
    (first vertex index != last vertex index).
       r   N)lenfacesfaces     r/   
open_facesrF   I   sE       t9q=7d2hs)OJ   /1Fclosec             #     K   t        |       D ]^  }|d   g}|dd D ]  }|d   |k7  s|j                  |        t        |      dk  r<|r|j                  |d          t        |       ` yw)zRemoves duplicated vertices and returns closed or open faces according
    the `close` argument. Returns only faces with at least 3 edges.
    r      NrA   r@   )rF   appendrB   tuple)rD   rI   rE   new_faceindexs        r/   normalize_facesrP   V   s      5! 	G9!"X 	'E|u$&	' x=1OOHQK(Ho	s   %A0AA0c              #  V   K   t        |       D ]  }t        |      E d{     y7 w)z$Yields all face edges as int tuples.N)rF   
face_edgesrC   s     r/   	all_edgesrS   j   s+     5! $d###$#s   )')c              #  f   K   t        |       }t        |      D ]  }| |   | |dz   |z     f  yw)z5Yields all edges of a single open face as int tuples.rK   N)rB   range)rE   sizerO   s      r/   rR   rR   p   s?     t9Dt 45k4d 23334rG   c                    t        dd      }i }t        |       D ]@  \  }}||f}d}||kD  r||f}d}|j                  ||      \  }}t        |dz   ||z         ||<   B |S )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   rK   rA   )r$   rS   get)	rD   new_edgestatsabedgeorientationr&   r'   s	            r/   get_edge_statsr_   w   s     1~HE%  A1!tq5a4DK42wuqy'K*?@dA Lr.   c                    t               }|dk(  ryt        j                         |z  }d}d}|D ]  }t        |      dk  r	 t         fd|D              }t        j                  |      t        |      z  }	 t        |d   |d   |d         }		 ||z
  j                         }
||	j                  |
      z  }|dz  } |dkD  r||z  S y# t        $ r Y w xY w# t        $ r Y w xY w# t        $ r Y w xY w)ae  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           r@   c              3  (   K   | ]	  }|     y wNr-   .0iverticess     r/   	<genexpr>z2estimate_face_normals_direction.<locals>.<genexpr>   s     !<!(1+!<   rK      )	rB   r   sumrM   
IndexErrorr   ZeroDivisionError	normalizedot)rg   rD   
n_verticesmesh_centroidr&   direction_sumrE   face_verticesface_centroidface_normaloutward_vecs   `          r/   estimate_face_normals_directionrw      s,   & XJQHHX&3MEM t9q=	!!<t!<<M /#d);	*a -"2M!4DK
	(=8CCEK 	55
'( qyu$$#  		 ! 		 ! 		s6   C
:CC(
	CC	C%$C%(	C43C4c              #  F   K   | D ]  }t        t        |              y wrc   )rM   reversedrC   s     r/   flip_face_normalsrz      s&       $HTN##$s   !c                   | j                   |j                  |j                  z  |j                  |j                  z  z
  z  | j                  |j                   |j                  z  |j                  |j                   z  z
  z  z   | j                  |j                  |j                   z  |j                   |j                  z  z
  z  z   S )a  
    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)r[   r\   cs      r/   volume6r      s     	
qssQSSy1339$%
##qssQSS133Y&
'	(
##qssQSS133Y&
'	(r.   c                ~    	 t        t        |             }t        |j	                  |             S # t        $ r Y yw xY w)Nra   )r   r   rm   r   points_from_wcs)polygonocss     r/   area_3dr      sC    $W-. ##G,--  s   0 	<<c                  $   e Zd ZddZedd       Zedd       Zedd       Zedd       Zedd       Z	edd       Z
edd       Zedd	       Zedd
       Zedd       Zedd       Zedd       ZddZddZddZddZddZddZddZy) MeshDiagnosec                L    || _         i | _        t               | _        g | _        y rc   )_mesh_edge_statsr   _bbox_face_normals)selfmeshs     r/   __init__zMeshDiagnose.__init__   s"    
&( ]
)+r.   c                .    | j                   j                  S )z@Sequence of mesh vertices as :class:`~ezdxf.math.Vec3` instances)r   rg   r   s    r/   rg   zMeshDiagnose.vertices   s     zz"""r.   c                .    | j                   j                  S )z&Sequence of faces as ``Sequence[int]``)r   rD   r   s    r/   rD   zMeshDiagnose.faces   s     zzr.   c                    t        | j                        dk(  r(t        | j                  j	                               | _        | j                  S )zReturns all face normal vectors  as sequence. The ``NULLVEC``
        instance is used as normal vector for degenerated faces. (cached data)

        r   )rB   r   listr   face_normalsr   s    r/   r   zMeshDiagnose.face_normals   s=     t!!"a'!%djj&=&=&?!@D!!!r.   c                    | j                   j                  s| j                  j                         | _         | j                   S )zGReturns the :class:`~ezdxf.math.BoundingBox` of the mesh. (cached data))r   has_datar   bboxr   s    r/   r   zMeshDiagnose.bbox  s-     zz""*DJzzr.   c                ,    t        | j                        S )zReturns the vertex count.)rB   rg   r   s    r/   rp   zMeshDiagnose.n_vertices  s     4==!!r.   c                ,    t        | j                        S )zReturns the face count.)rB   rD   r   s    r/   n_faceszMeshDiagnose.n_faces  s     4::r.   c                ,    t        | j                        S )z,Returns the unique edge count. (cached data))rB   
edge_statsr   s    r/   n_edgeszMeshDiagnose.n_edges  s     4??##r.   c                ~    t        | j                        dk(  rt        | j                        | _        | j                  S )a^  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   )rB   r   r_   rD   r   s    r/   r   zMeshDiagnose.edge_stats  s4     t A%-djj9Dr.   c                N    | j                   | j                  z
  | j                  z   S )zReturns the Euler characteristic:
        https://en.wikipedia.org/wiki/Euler_characteristic

        This number is always 2 for convex polyhedra.
        )rp   r   r   r   s    r/   euler_characteristicz!MeshDiagnose.euler_characteristic(  s      -<<r.   c                V    t        d | j                  j                         D              S )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  :   K   | ]  }|j                   d k7    yw)r   N)r'   re   es     r/   rh   z6MeshDiagnose.is_edge_balance_broken.<locals>.<genexpr><  s     Da199>D   )anyr   valuesr   s    r/   is_edge_balance_brokenz#MeshDiagnose.is_edge_balance_broken1  s"     D4??+A+A+CDDDr.   c                V    t        d | j                  j                         D              S )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  :   K   | ]  }|j                   d k    yw)r@   Nr&   re   r]   s     r/   rh   z+MeshDiagnose.is_manifold.<locals>.<genexpr>E  s     Gd4::>Gr   allr   r   r   s    r/   is_manifoldzMeshDiagnose.is_manifold>  s"     Gdoo.D.D.FGGGr.   c                V    t        d | j                  j                         D              S )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  :   K   | ]  }|j                   d k(    yw)rj   Nr   r   s     r/   rh   z1MeshDiagnose.is_closed_surface.<locals>.<genexpr>Q  s     Ht4::?Hr   r   r   s    r/   is_closed_surfacezMeshDiagnose.is_closed_surfaceG  s"     Ht/E/E/GHHHr.   c                V    t        d | j                  j                         D              S )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  4   K   | ]  }|j                     y wrc   r   r   s     r/   rh   z0MeshDiagnose.total_edge_count.<locals>.<genexpr>X  s     =q177=s   )rk   r   r   r   s    r/   total_edge_countzMeshDiagnose.total_edge_countS  s"    
 =DOO$:$:$<===r.   c                6    | j                   j                         S )zBYields the unique edges of the mesh as int 2-tuples. (cached data))r   keysr   s    r/   unique_edgeszMeshDiagnose.unique_edgesZ  s    ##%%r.   c                B    t        | j                  | j                        S )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

        )rw   rg   rD   r   s    r/   rw   z,MeshDiagnose.estimate_face_normals_direction^  s    B /t}}djjIIr.   c                X    t        d | j                  j                         D               S )z+Returns ``True`` if any face is non-planar.c              3  2   K   | ]  }t        |        y wrc   )r   re   rE   s     r/   rh   z4MeshDiagnose.has_non_planar_faces.<locals>.<genexpr>  s     W~d+W   )r   r   faces_as_verticesr   s    r/   has_non_planar_facesz!MeshDiagnose.has_non_planar_faces  s#    W

8T8T8VWWWWr.   c                    | j                   r@d}| j                  j                  d      D ]  }|t        |d   |d   |d         z  } |dz  S y)aK  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!!!

        ra   r@   r   rK   rj   g      @)r   r   tessellationr   )r   volumerE   s      r/   r   zMeshDiagnose.volume  s\     !!F

//2 ='$q'47DG<<=C<r.   c                    | j                   }d}| j                  j                         D ]"  }|D cg c]  }||   	 }}|t        |      z  }$ |S c c}w )zReturns the surface area.ra   )rg   r   rF   r   )r   vsurface_arearE   rf   r   s         r/   r   zMeshDiagnose.surface_area  s_    MMJJ))+ 	-D%)*qt*G*GG,,L	-  +s   Ac                    | j                   dkD  r,t        j                  | j                        | j                   z  S t        S )z6Returns the centroid of all vertices. (center of mass)r   )rp   r   rk   rg   r   r   s    r/   centroidzMeshDiagnose.centroid  s/    ??Q88DMM*T__<<r.   N)r   MeshBuilder)returnSequence[Vec3])r   Sequence[Face]r   r   )r   r%   )r   r2   r   bool)r   Iterable[Edge])r   float)r   r   )r(   r)   r*   r   propertyrg   rD   r   r   rp   r   r   r   r   r   r   r   r   r   rw   r   r   r   r   r-   r.   r/   r   r      s#   , # #     " "   " "   $ $ 	  	  = = 
E 
E H H 	I 	I>&!JFX&r.   r   c                     e Zd ZdZd&dZd'dZd Zd(dZd)dZd*dZ	d+dZ
d,d	Zd-d
Zd.dZd/dZ	 	 	 d0	 	 	 	 	 d1dZ	 	 	 d0	 	 	 	 	 d2dZ	 	 	 d3	 	 	 d4dZed5d       Zed6d       Z	 	 	 d0	 	 	 	 	 d2dZd7d8dZ	 	 	 d0	 	 	 	 	 d2dZed9d       Zd:d;dZd<d=dZd>d?dZd@dAdZd@dBdZd@dCdZd&dZdDdZ d&dZ!dEdFd Z"dd!	 	 	 dGd"Z#	 dEd#dd$	 	 	 	 	 dHd%Z$y)Ir   aX  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                     g | _         g | _        y rc   rg   rD   r   s    r/   r   zMeshBuilder.__init__  s    $&*,
r.   c                ,    t        | j                        S )z9Returns the :class:`~ezdxf.math.BoundingBox` of the mesh.)r   rg   r   s    r/   r   zMeshBuilder.bbox  s    4==))r.   c                $    | j                  |       S )zReturns a copy of mesh.)from_builderr   s    r/   copyzMeshBuilder.copy  s      &&r.   c                    t        |       S )z7Returns the :class:`MeshDiagnose` object for this mesh.)r   r   s    r/   diagnosezMeshBuilder.diagnose  s    D!!r.   c                ^    | j                   t        fd| j                  |   D              S )z[Returns the face `index` as sequence of :class:`~ezdxf.math.Vec3`
        objects.
        c              3  (   K   | ]	  }|     y wrc   r-   )re   virg   s     r/   rh   z0MeshBuilder.get_face_vertices.<locals>.<genexpr>  s     >bXb\>ri   )rg   rM   rD   )r   rO   rg   s     @r/   get_face_verticeszMeshBuilder.get_face_vertices  s(     ==>DJJu,=>>>r.   c                r    | j                  |      }	 t        |      S # t        t        f$ r	 t        cY S w xY w)zReturns the normal vector of the face `index` as :class:`~ezdxf.math.Vec3`,
        returns the ``NULLVEC`` instance for degenerated  faces.
        )r   r   
ValueErrorrm   r   )r   rO   rE   s      r/   get_face_normalzMeshBuilder.get_face_normal  s>     %%e,	%d++-. 	N	s   
 66c              #     K   | j                         D ]  }	 t        |        y# t        t        f$ r t         Y -w xY ww)z`Yields all face normals, yields the ``NULLVEC`` instance for degenerated
        faces.
        N)r   r   r   rm   r   )r   rE   s     r/   r   zMeshBuilder.face_normals  sK      **, 	D(..	  12 s$   A'AAA AAc              #  z   K   | j                   }| j                  D ]  }|D cg c]  }||   	 c}  yc c}w w)z%Yields all faces as list of vertices.Nr   )r   r   rE   rO   s       r/   r   zMeshBuilder.faces_as_vertices  s:     MMJJ 	/D)-.1U8..	/.s    ;6;c              #  J   K   t        | j                        E d{    y7 w)zxYields all faces as sequence of integers where the first vertex
        is not coincident with the last vertex.
        N)rF   rD   r   s    r/   rF   zMeshBuilder.open_faces  s      djj)))s   #!#c                X    | j                   j                  | j                  |             y)a  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)rD   rL   add_vertices)r   rg   s     r/   add_facezMeshBuilder.add_face  s!     	

$++H56r.   c                    t        | j                        }| j                  j                  t        j                  |             t        t        |t        | j                                    S )ad  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

        )rB   rg   extendr   generaterM   rU   )r   rg   start_indexs      r/   r   zMeshBuilder.add_vertices  sH    " $--(T]]845U;DMM(:;<<r.   Nc                "   |+t        j                  |j                        }|j                  }|t	        d      |xs g }| j                  |      t        |      D ]/  }| j                  j                  t        fd|D                     1 y)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  (   K   | ]	  }|     y wrc   r-   )re   r   indicess     r/   rh   z'MeshBuilder.add_mesh.<locals>.<genexpr>+  s     #HBGBK#Hri   )	r   r   rg   rD   r   r   rF   rL   rM   )r   rg   rD   r   rs   r   s        @r/   add_meshzMeshBuilder.add_mesh  s    & yy/HJJEABB##H-'. 	JMJJe#H-#HHI	Jr.   c                L   |rt        |      ni }| j                  }||j                  |      }||j                  |      }|j	                  |      }|j                         5 }t        |      |_        t        | j                        |_        ddd       |S # 1 sw Y   |S xY w)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)dictrg   transform_verticespoints_to_wcsr   	edit_datar   rD   )r   layoutr   matrixucsrg   r   datas           r/   render_meshzMeshBuilder.render_mesh-  s      *4T*%
==00:H?((2H*5^^ 	* !NDMdjj)DJ		*
 	*
 s   $+BB#c                <   |rt        |      ni }| j                         D ]k  }t        |      }|dk  rt        j                  |      |z  }	 t        |      }|r|d   |z
  j                  |z  }	n|}	|j                  ||||	z  z   |       m y# t        $ r Y zw xY w)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}``

        r@   r   r   N)	r   r   rB   r   rk   r   rm   	magnitudeadd_line)
r   r   lengthrelativer   rE   r&   centern_lengths
             r/   render_normalszMeshBuilder.render_normalsK  s    $ *4T*%
**, 	QDIEqyXXd^e+F&t, 7V+66? OOFFQ[$8ZOP	Q % s   B	BBc                \     |        }t        |t              sJ |j                  |       |S )a0  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      r/   	from_meshzMeshBuilder.from_meshm  s.     u$,,,5!r.   c           
     x   |j                         dk7  rt        d|j                                 |        }t        |t              sJ |j                  r;|j                         \  }}|D ]!  }|j                  |j                                # |S |j                  r|j                         }t        |j                  j                  dz
        D ]^  }t        |j                  j                  dz
        D ]7  }|j                  |||f   |||dz   f   ||dz   |dz   f   ||dz   |f   f       9 ` |S t        d      )zxCreate new mesh from a  :class:`~ezdxf.entities.Polyface` or
        :class:`~ezdxf.entities.Polymesh` object.

        POLYLINEzUnsupported DXF type: rK   zNot a polymesh or polyface.)dxftype	TypeErrorr  r   is_poly_face_meshindexed_facesr   pointsis_polygon_meshget_mesh_vertex_cacherU   dxfm_countn_count)	r
  r  r   _rD   rE   rg   mr  s	            r/   from_polyfacezMeshBuilder.from_polyface}  sJ    ==?j(4U]]_4EFGGu$,,,""**,HAu -dkkm,-   ""224H599,,q01 	uyy00145 AMM$QTN$QAX.$QUAE\2$QUAX.		  9::r.   c                   |rt        |      ni }|j                  |      }t        j                  |       }||j	                  |       ||j	                  |j
                         |j                  |j                  d      |       |S )a  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          r/   render_polyfacezMeshBuilder.render_polyface  s    " *4T*%
&&*&=((.KK?KK

#NNAN.! 	 	
 r.   c                    ddl m} |rt        |      ni }|j                  |      }|j	                  |       }|j                  ||g       |S )aq  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         r/   render_3dsolidzMeshBuilder.render_3dsolid  sM    4 	+)3T*%
$$
$;""4($(r.   c                    |rt        |      ni }t        j                  |       }||j                  |       ||j                  |j                         |j                  d      D ]  }|j                  ||        y)a  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`

        Nr  r  r   )r   r!  r   r"  r   r   
add_3dface)r   r   r   r   r   r%  rE   s          r/   render_3dfaceszMeshBuilder.render_3dfaces  sv    " *4T*%
((.KK?KK

#NNAN6 	;Ddz:	;r.   c                     |        }t        |t              sJ t        |j                        |_        t        |j                        |_        |S )zCreate new mesh from other mesh builder, faster than
        :meth:`from_mesh` but supports only :class:`MeshBuilder` and inherited
        classes.

        )r  r   r   rg   rD   r	  s      r/   r   zMeshBuilder.from_builder  sB     u$,,, U^^,%++&
r.   c                    | }t        |      D ]"  }t        |j                  |j                        }$ t        j                  |      S )a  Returns 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!

        )rU   _merge_adjacent_coplanar_facesrg   rD   r!  r   )r   passesr   r  s       r/   merge_coplanar_facesz MeshBuilder.merge_coplanar_faces  sC     v 	MA1$--LD	M++D11r.   c                    | }t        t        |      d      }|dkD  rt        ||      }|dz  }|dkD  rt        j	                  |      S )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   rK   )minr%   
_subdivider!  r   )r   levelquadsr   s       r/   	subdividezMeshBuilder.subdivide  sQ     CJ"aidE*DQJE ai ++D11r.   c                h    t        |      }|j                  |        t        j                  |      S )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.
        )	precisionr  )MeshVertexMergerr   r!  r   )r   rA  r   s      r/   optimize_verticeszMeshBuilder.optimize_vertices#  s-      )44 ++D11r.   c              #  T   K   t        | j                         |      E d{    y7 w)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   r  s     r/   r   zMeshBuilder.subdivide_ngons-  s"      #4#9#9#;=MNNNs   (&(c              #     K   ddl m} | j                         D ]%  }t        |      |k  r|  ||      E d{    ' y7 w)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.triangulationrF  r   rB   )r   r  rF  rE   s       r/   r   zMeshBuilder.tessellation7  sH      	>**, 	2D4y,,
+D111		2 2s   8AAAc                    t               }| j                  |      D ]  }|j                  |        t        j	                  |      S )a  Returns 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!
        r  )rB  r   r   r!  r   )r   r  r   rE   s       r/   mesh_tessellationzMeshBuilder.mesh_tessellationE  sG      !%%7G%H 	 DMM$	 ++D11r.   c                J    t        t        | j                              | _        y)zEFlips the normals of all faces by reversing the vertex order inplace.N)r   rz   rD   r   s    r/   flip_normalszMeshBuilder.flip_normalsQ  s    +DJJ78
r.   c                *    t        t        |             S )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    r/   rM  zMeshBuilder.separate_meshesU  s    
 OD)**r.   c                N    t        t        | j                  d            | _        y)zRemoves duplicated vertex indices from faces and stores all faces as
        open faces, where the last vertex is not coincident with the first
        vertex.
        FrH   N)r   rP   rD   r   s    r/   rP   zMeshBuilder.normalize_faces\  s    
 /$**EBC
r.   c                    t        | |      S )a~  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   rQ  s     r/   face_orientation_detectorz%MeshBuilder.face_orientation_detectorc  s     'tyAAr.   fodc                   t        | |      S )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
        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

        rT  )unify_face_normals_by_majority)r   rU  s     r/   unify_face_normalszMeshBuilder.unify_face_normalso  s    & .d<<r.   F)force_outwardsrU  c               >    t        | ||      }|rt        ||       |S )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

        )rQ  rU  )unify_face_normals_by_reference%_force_face_normals_pointing_outwards)r   rQ  rY  rU  r   s        r/   r[  z+MeshBuilder.unify_face_normals_by_reference  s%    L /tycR1$	Br.   )r   Noner   )r   r   )rO   r%   r   r   )rO   r%   r   r   )r   Iterator[Vec3])r   zIterator[list[Vec3]]r   Iterator[Face])rg   Iterable[UVec]r   r]  rg   ra  r   r0   )NNN)rg   zOptional[list[Vec3]]rD   zOptional[list[Face]]r   r]  )r   r!   r   zOptional[Matrix44]r   zOptional[UCS])rK   TN)r   r!   r   r   )r
  Type[T]r  zUnion[MeshBuilder, Mesh]r   r"   )r
  rc  r  zUnion[Polymesh, Polyface]r   r"   rc   )r   r!   r   r    )r
  rc  r  r   r   r"   )rK   )r7  r%   r   r!  )rK   T)r=  r%   r   r!     )rA  r%   r   r!  r  )r   Iterator[Sequence[Vec3]])r  r%   r   rg  )r  r%   r   r!  )r   zlist[MeshTransformer]r   )rQ  r%   r   rR  )rU  !Optional[FaceOrientationDetector]r   r!  )rQ  r%   rU  ri  r   r!  )%r(   r)   r*   r+   r   r   r   r   r   r   r   r   rF   r   r   r   r   r  classmethodr  r  r&  r0  r3  r   r8  r?  rC  r   r   rI  rK  rM  rP   rS  rX  r[  r-   r.   r/   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))
 /) 
)r.   r   c                  T    e Zd ZdZddZdddZdddZddZddZddZ	ddZ
dd	Zy
)r!  z3A mesh builder with inplace transformation support.c                X    t        |j                  | j                              | _        | S )zTransform mesh inplace by applying the transformation `matrix`.

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

        )r   r   rg   )r   r   s     r/   r"  zMeshTransformer.transform  s$     V66t}}EFr.   c                    t        |t        t        f      rt        |||      }nt        |      }| j                  D cg c]  }||z   	 c}| _        | S c c}w )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   rg   )r   dxdydzr%  r   s         r/   	translatezMeshTransformer.translate  sM     b5#,'RR ARA(,61Q6 7s   Ac                    | j                   D cg c]  \  }}}t        ||z  ||z  ||z         c}}}| _         | S c c}}}w )zScale mesh inplace.

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

        )rg   r   )r   sxsyszr}   r~   r|   s          r/   scalezMeshTransformer.scale  sE     FJ]]SS'!Qa"fa"fa"f5S Ts   !>c                T    | j                   D cg c]  }||z  	 c}| _         | S c c}w )zfScale mesh uniform inplace.

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

        )rg   )r   sr   s      r/   scale_uniformzMeshTransformer.scale_uniform  s(     )-61Q6 7s   %c                ~    t        t        j                  |      j                  | j                              | _        | S )zvRotate mesh around x-axis about `angle` inplace.

        Args:
            angle: rotation angle in radians

        )r   r   x_rotater   rg   r   angles     r/   rotate_xzMeshTransformer.rotate_x  /     X..u5HHWXr.   c                ~    t        t        j                  |      j                  | j                              | _        | S )zvRotate mesh around y-axis about `angle` inplace.

        Args:
            angle: rotation angle in radians

        )r   r   y_rotater   rg   r|  s     r/   rotate_yzMeshTransformer.rotate_y  r  r.   c                ~    t        t        j                  |      j                  | j                              | _        | S )zvRotate mesh around z-axis about `angle` inplace.

        Args:
            angle: rotation angle in radians

        )r   r   z_rotater   rg   r|  s     r/   rotate_zzMeshTransformer.rotate_z  r  r.   c                    t        t        j                  ||      j                  | j                              | _        | S )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   rg   )r   axisr}  s      r/   rotate_axiszMeshTransformer.rotate_axis  s6       u-@@O
 r.   N)r   r   )r   r   r   )rn  zUnion[float, UVec]ro  r   rp  r   )rK   rK   rK   )rs  r   rt  r   ru  r   )rx  r   )r}  r   )r  r   r}  r   )r(   r)   r*   r+   r"  rq  rv  ry  r~  r  r  r  r-   r.   r/   r!  r!    s-    =	 
r.   r!  rB  c                    t               }| j                         D ]3  }t        |      dk  rt        ||      D ]  }|j	                  |        5 |S )zReturns a new :class:`MeshVertexMerger` object with subdivided faces
    and edges.

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

    r@   )rB  r   rB   r   r   )r   r>  new_meshrg   rE   s        r/   r<  r<    s^      !H**, $x=1"8U3 	$Dd#	$$
 Or.   c                  H     e Zd ZdZdd fdZddZd	dZed
d       Z xZ	S )rB  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         |           i | _        || _        y)z\
        Args:
            precision: floating point precision for vertex rounding

        Nsuperr   ledgerrA  r   rA  	__class__s     r/   r   zMeshVertexMerger.__init__6  s     	')'r.   c                   g }| j                   }t        j                  |      D ]2  }|j                  |      }	 |j	                  | j
                  |          4 t        |      S # t        $ rS t        | j                        }| j                  j	                  |       || j
                  |<   |j	                  |       Y w xY w)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:
            indices of the added `vertices`

        )
rA  r   r   roundrL   r  KeyErrorrB   rg   rM   )r   rg   r   rA  vertexkeyrO   s          r/   r   zMeshVertexMerger.add_vertices@  s     NN	mmH- 	&F,,y)C&t{{3/0	& W~  &DMM*$$V,#(C u%	&s   A$$AC ?C c                    	 | j                   t        |      j                  | j                           S # t        $ r t        dt        |       d      w xY w)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  rA  r  rl   strr   r  s     r/   rO   zMeshVertexMerger.indexZ  sS    	A;;tF|11$..ABB 	Aws6{m;?@@	As	   03 "Ac                $    | j                  |      S z(Create new mesh from other mesh builder.r  r
  r  s     r/   r   zMeshVertexMerger.from_builderg       }}U##r.   rd  rA  r%   rb  r  r   r   r%   )r  r   r   rB  
r(   r)   r*   r+   r   r   rO   rj  r   __classcell__r  s   @r/   rB  rB  "  s,    &(4A $ $r.   c                  H     e Zd ZdZdd fdZddZd	dZed
d       Z xZ	S )MeshAverageVertexMergeraf  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         |           i | _        || _        y rc   r  r  s     r/   r   z MeshAverageVertexMerger.__init__  s#      	 (r.   c                   g }| j                   }t        j                  |      D ]t  }|j                  |      }	 | j                  |   \  }}| j
                  |   |z  |z   }|dz  }||z  | j
                  |<   ||f| j                  |<   |j                  |       v t        |      S # t        $ rD t        | j
                        }| j
                  j                  |       |df| j                  |<   Y jw xY w)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

        rK   )
rA  r   r   r  r  rg   r  rB   rL   rM   )	r   rg   r   rA  r  r  rO   r&   averages	            r/   r   z$MeshAverageVertexMerger.add_vertices  s     NN	mmH- 	"F,,y)C2#{{3/u  ==/%76A
'.e$$)5>C NN5!!	"" W~  .DMM*$$V,$)1:C .s   B&&A
C32C3c                    	 | j                   t        |      j                  | j                           d   S # t        $ r t        dt        |       d      w xY w)r  r   r  r  r  r  s     r/   rO   zMeshAverageVertexMerger.index  sX    	A;;tF|11$..AB1EE 	Aws6{m;?@@	As	   36 "Ac                $    | j                  |      S r  r  r  s     r/   r   z$MeshAverageVertexMerger.from_builder  r  r.   rd  r  rb  r  )r  r   r   r  r  r  s   @r/   r  r  n  s-    ((!FA $ $r.   r  c                  "    e Zd ZdZddZdddZy)_XFace)fingerprintr   _orientationc                H    t        |      | _        || _        t        | _        y rc   )hashr  r   VEC3_SENTINELr  )r   r   s     r/   r   z_XFace.__init__  s     $W$"/r.   c                
   | j                   t        u rRt        }| j                  D cg c]  }||   	 c}^}}}|D ]   }	 t	        |||      j                  |      } n || _         | j                   S c c}w # t        $ r Y Fw xY wrc   )r  r  r   r   r   r  rm   )	r   rg   rA  r^   rf   v0v1r   v2s	            r/   r^   z_XFace.orientation  s    -!K/3||<!(1+<JBQ "22r2">"D"DY"OK !,D    =
 ) s   A1A66	BBN)r   r0   rf  )rg   r   rA  r%   r   r   )r(   r)   r*   	__slots__r   r^   r-   r.   r/   r  r    s    :I0
!r.   r  c           
        i }g }|D ]h  }t        |      dk  rt        d      t        |      }|j                  |       |j	                  |j                  | |      g       j                  |       j t               }t               }|D ]O  }|j                  |v r|j                  |j                         |j                  }|j                  | |      }	||	   }
t        |      }|
D ]  }|j                  |v r|j                  t        |j                              }t        |      dkD  sEt        |      t        |j                        k(  rt        ||j                        }n	 t        ||j                        }|j                  |j                         t        |      } t!        t#        |D cg c]  }| |   	 c}            }|j%                  |       R |S # t        t        f$ r Y 	w xY wc c}w )Nr@   zfound invalid face count < 3rK   )rB   r   r  rL   
setdefaultr^   rB  setr  addr   intersectionmerge_full_patchmerge_connected_pathsr8   r<   r   remove_colinear_face_verticesr   )rg   rD   rA  oriented_facesextended_facesrE   xfacer   doner^   parallel_facesface_setparallel_facecommon_verticesrf   r  s                   r/   r6  r6    s    02N#%N 
t9q=;<<te$!!%"3"3Hi"H"MTT	

 D5D $""#}}'')<'4t9+ 	%M((D0&33C8M8M4NOO?#a''3}/D/D+EE+D-2G2GHD!4T=;P;PQ 223t9	% /d0K!0KLMb12 K -.BC ! ! 1Ls   $G0G3G0/G0c              #    K   d	d}t        |       dk  r| E d {    y | d   g}| dd  D ](  }|j                  |d         r|j                  |       * t        |      dk  r-t        |      dk(  r|j                  |d          |E d {    y |d   }t        }t        }d}| |t        u r|dz  }	 | |   } |||      }|t        u rd}|j                  |       ||d  D ]2  }		  |||	      j                  |      r|	}	 | d}|} |||	      }|	}4 |s|d    y y 7 7 # t        $ r | Y y w xY w# t
        $ r Y cw xY ww)
Nc                (    || z
  j                         S rc   )rn   )r  r  s     r/   get_directionz4remove_colinear_face_vertices.<locals>.get_direction  s    R""$$r.   r@   r   rK   rA   FT)r  r   r  r   )rB   iscloserL   r  rl   rm   )
rg   r  	_verticesr   startprev_vertexcurrent_directionr   yielded_anythingr  s
             r/   r  r    s    % 8}q &a[MIab\  yy2'Q  9~y>QYq\*aLEK%K K
}
,q	";/K *%= }
, UKL) 	UF+334EF$ G
 )%8 m ] 	 	  		 ! 		sv   ED%'EAED()E6D* ;EE)D=#E(E*D:7E9D::E=	E	EE		Ec                    dd} ||       } ||      }| d   }| d   }|g}	 	 ||   }||v r||}}||k(  rn|}||v rt        |j                  |       2t        |      dk  rt        |S # t         $ r t        w xY w)Nc                l    t        | | dd        D ci c]  \  }}||
 }}}| d   || d   <   |S c c}}w )NrK   r   rA   )zip)pe1e2nodess       r/   build_nodesz*merge_connected_paths.<locals>.build_nodes>  sD    &)!QqrUm4FBR44tae 5s   0r   r@   )r  Sequence[int])r  r8   rL   rB   r<   )	p1p2r  current_path
other_pathcurrent_nodefinishconnected_path	next_nodes	            r/   r  r  =  s    
 r?LRJa5LUF"^N
	#$\2I 
"'1<*L >) #"l+   >Q""  	#""	#s   A+ +A;c                    t        |       }g }t        |       D ]2  \  }}| |dz
     }| |dz   |z     }||v r||v r"|j                  |       4 |S )NrK   )rB   	enumeraterL   )pathpatchr&   new_pathposnodeprevsuccs           r/   r  r  ]  sj    IEHt_ 	TC!G}S1W%&5=TU] Or.   c                  $    e Zd ZddZddZddZy)Lumpc                    t               | _        |g| _        t        |      D ]+  \  }}| j                  j	                  ||k  r||fn||f       - y rc   )r  edgesrD   rR   r  )r   rE   r[   r\   s       r/   r   zLump.__init__j  sL     #
"&
t$ 	9DAqJJNNQ!VAq6!Q8	9r.   c                N    | j                   j                  |j                          S rc   )r  
isdisjointr   r  s     r/   is_connectedzLump.is_connectedq  s    ::((555r.   c                    | j                   j                  |j                          | j                  j                  |j                         y rc   )rD   r   r  updater  s     r/   mergez
Lump.merget  s.    

%++&

%++&r.   N)rE   r0   )r  r  r   r   )r  r  )r(   r)   r*   r   r  r  r-   r.   r/   r  r  i  s    96'r.   r  c                    t        |       }d}dt        |      cxk  r|k7  r2n |S t        |      }t        |      }dt        |      cxk  r|k7  r+|S  |S )Nr   rK   )_merge_lumpsrB   )lumpsmerged_lumps	prior_lens      r/   merge_lumpsr   y  sj    &LI
c,
,9
,  %	#L1 c,
,9
,  - r.   c                    g }| D ]>  }|D ]&  }|j                  |      s|j                  |        - |j                  |       @ |S rc   )r  r  rL   )r  r  lumpbases       r/   r  r    sZ    !L &  	&D  &

4 	&
 %& r.   c              #  t  K   t        t        d t        | j                        D                    }t	        |      dkD  r`| j
                  |D ]N  }t               }|j                  D ]  }|j                  fd|D                t        j                  |       P yt        j                  |        yw)zReturns the separated meshes in a single :class:`MeshBuilder` instance
    as multiple :class:`MeshTransformer` instances.
    c              3  2   K   | ]  }t        |        y wrc   )r  r   s     r/   rh   z"separate_meshes.<locals>.<genexpr>  s     )U$t*)Ur   rK   c              3  (   K   | ]	  }|     y wrc   r-   rd   s     r/   rh   z"separate_meshes.<locals>.<genexpr>  s     8ahqk8ri   N)
r   r   rF   rD   rB   rg   rB  r   r!  r   )r  disconnected_lumpsr  r   rE   rg   s        @r/   rM  rM    s      k)UAGGAT)UUV
"::& 	5D#%D

 984889!..t44		5 **1--s   B5B8c                L   t        j                  |       t        |       z  }t        j                  |      t        |      z  }t        | d   | d   | d         dz  }t        |d   |d   |d         dz  }||z   }||z   }|j	                  |      }|j	                  |      }	|	|kD  S )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   rK   rj   g      ?)r   rk   rB   r   distance)
f0f1c0c1n0n1e0r  d0d1s
             r/   have_away_pointing_normalsr    s     
"B	B	"B	B	"Q%A1	.	4B	"Q%A1	.	4B	bB	bB	RB	RB7Nr.   c                    ddl m}  |d      j                  |       }|j                         \  }}}}}}t	        ||      S )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.

    rK   )cubeT)formsr  r"  r   r  )r  r  	unit_cubebottomr  tops         r/   !face_normals_after_transformationr    sC     T
$$Q'I'99;FAq!Q%fc22r.   c                z    i }| D ]3  }t        |      D ]#  }|j                  |g       j                  |       % 5 |S rc   )rR   r  rL   )rD   mappingrE   r]   s       r/   _make_edge_mappingr    sL    &(G 6t$ 	6DtR(//5	66 Nr.   c                      e Zd ZdZdddZedd       Zedd       Zedd       Zedd       Z	edd       Z
edd       Zddd	Zedd
       ZddZy)rR  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                    || _         t        |j                        | _        || _        d| _        t               | _        t               | _        | j                  |       y )NT)
r   r  rD   edge_mappingrQ  r   r   forwardbackwardclassify_faces)r   r   rQ  s      r/   r   z FaceOrientationDetector.__init__  sI    
4Ftzz4R"(,)-I&r.   c                2    t        | j                        dk(  S )znReturns ``True`` if all reachable faces are forward oriented
        according to the reference face.
        r   )rB   r"  r   s    r/   has_uniform_face_normalsz0FaceOrientationDetector.has_uniform_face_normals  s    
 4==!Q&&r.   c                l    t        | j                  j                        t        | j                        k(  S )z}Returns ``True`` if all faces are reachable from the reference face
        same as property :attr:`is_single_mesh`.
        )rB   r   rD   rk   r&   r   s    r/   all_reachablez%FaceOrientationDetector.all_reachable  s&    
 4::##$DJJ77r.   c                    | j                   S )zjReturns ``True`` if only a single mesh is present same as property
        :attr:`all_reachable`.
        )r'  r   s    r/   is_single_meshz&FaceOrientationDetector.is_single_mesh  s    
 !!!r.   c                V    t        | j                        t        | j                        fS )z9Returns the count of forward and backward oriented faces.)rB   r!  r"  r   s    r/   r&   zFaceOrientationDetector.count  s!     4<< #dmm"444r.   c                H    t        | j                  j                               S )z"Yields all forward oriented faces.)iterr!  r   r   s    r/   forward_facesz%FaceOrientationDetector.forward_faces  s     DLL'')**r.   c                H    t        | j                  j                               S )z#Yields all backward oriented faces.)r,  r"  r   r   s    r/   backward_facesz&FaceOrientationDetector.backward_faces  s     DMM((*++r.   c                   	
 d
fd}dfd}d	
fdd		 fd}t        |       _        d _         j                  	t	               
t	                j
                  j                  |   dfgt              r<j                  d      \  }}|r	 ||       n ||        |||       t              r<
 _	         _
        y)
zDetect the forward and backward oriented faces.

        The forward and backward orientation has to be defined by a `reference`
        face.
        c                "    | t        |       <   y rc   id)fr!  s    r/   add_forwardz;FaceOrientationDetector.classify_faces.<locals>.add_forward  s    GBqENr.   c                "    | t        |       <   y rc   r2  )r4  r"  s    r/   add_backwardz<FaceOrientationDetector.classify_faces.<locals>.add_backward  s    HRUOr.   c                V    t        |       }|vr|vrj                  | |f       y y y rc   )r3  rL   )r4  r^   r  r"  r!  process_facess      r/   add_face_to_processzCFaceOrientationDetector.classify_faces.<locals>.add_face_to_process  s6    Q%C'!c&9$$a%56 ':!r.   c                    t        |       D ][  }|   }t        |      dkD  r|D ]  }|| u r ||         	 |d   |d   f   }t        |      dk(  r |d   |       Ud_        ] y # t        $ r Y jw xY w)NrK   r   F)rR   rB   r  r   )r4  r^   r]   linked_faceslinked_facer:  r   r   s        r/   #add_adjacent_faces_to_process_queuezSFaceOrientationDetector.classify_faces.<locals>.add_adjacent_faces_to_process_queue#  s    "1 -+D1|$q('3 J&!+$+K[IJ#/Qa0@#AL |$)'QE (-D$)-   s   A,,	A87A8Tr   N)r4  r0   )r4  r0   r^   r   )r%   rQ  r   r   r   r   rD   rB   popr!  r"  )r   rQ  r5  r7  r>  rE   face_orientationr:  r"  r   r!  r9  s   `      @@@@@r/   r#  z&FaceOrientationDetector.classify_faces  s    		 	7
	-. Y((#'6$(F37::3C3CI3NPT2U1V- %2%6%6q%9"D"D!T"/6FG -   r.   c                    | j                   syg }| j                  }|j                         D ]L  }|j                  ||      }|d   |d   f}|j                  ||      }t	        |      t	        |      z   dk7  sL y y)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.

        FrK   r   rj   T)r   r   r   rX   rB   )r   emptyr   r]   forward_connected_facesreversed_edgebackward_connected_facess          r/   r   z)FaceOrientationDetector.is_closed_surfaceM  s     (( %%' 	D&2&6&6tU&C# GT!W,M'3'7'7u'M$*+c2J.KKqP	 r.   c                    ddl m} t        | j                  j	                               }|| j
                     } |||      S )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.mathrH  r   r   r   rQ  )r   rH  rD   reference_faces       r/   #is_reference_face_pointing_outwardsz;FaceOrientationDetector.is_reference_face_pointing_outwardsd  s:     	@TZZ1134t~~./~FFr.   Nrh  )r   r   rQ  r%   r   )r   ztuple[int, int]r_  )rQ  r%   r   r]  )r(   r)   r*   r+   r   r   r%  r'  r)  r&   r-  r/  r#  r   rK  r-   r.   r/   rR  rR    s    $' ' ' 8 8 " " 5 5 + + , ,:!x  ,
Gr.   rR  N)rU  rQ  c               @    |t        | |      }dd}t        | ||      S )a  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                    | j                   S rc   )r"  )detectors    r/   backward_selectorz:unify_face_normals_by_reference.<locals>.backward_selector  s       r.   rN  rR  rR  _unify_face_normals)r   rU  rQ  rO  s       r/   r[  r[  q  s,    6 {%dI6! tS*;<<r.   rT  c               >    |t        |       }dd}t        | ||      S )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                Z    | j                   \  }}||k\  r| j                  S | j                  S rc   )r&   r"  r!  )rN  count0count1s      r/   rO  z9unify_face_normals_by_majority.<locals>.backward_selector  s-    !$*f$4x  J(:J:JJr.   rP  rQ  )r   rU  rO  s      r/   rW  rW    s+    2 {%d+K tS*;<<r.   c                   |j                   st        d      |j                  st        d|j                         t               }t        | j                        |_        |j                  so ||      }g }| j                  D ]M  }t        |      |v r$|j                  t        t        |                   4|j                  t        |             O ||_	        |S t        | j                        |_	        |S )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   r>   r'  r:   rQ  r!  r   rg   r%  rD   r3  rL   rM   ry   )r   rU  rO  r  r"  rD   rE   s          r/   rR  rR    s     ??"#677!?O
 	
  HT]]+H''$S)JJ 	*D$x8#U8D>23U4[)		*
  O djj)Or.   c                    ddl m} t        | j                               }||   } |||      s| j	                          yy)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   rG  N)rI  rH  r   r   rK  )r   rQ  rH  rD   rJ  s        r/   r\  r\    s@     <'')*E9%N+E>B Cr.   c                   | j                         }|j                          |j                  d      }|j                  st	        d      |j
                  s)t        t        |            d   }|j                  d      }|j                  st        ||      }|j                         }|j                  r |j                         s|j                          |S )a  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   rP  rX  rT  )rC  rP   rS  r   r>   r'  r   rM  r%  r[  r   rK  rK  )r   r  rU  s      r/   normalize_meshr[    s    " %%'H

,
,q
,
9C??"#67712150010=''28E002
S%L%L%NOr.   )rD   Iterable[Face]r   r`  )rD   zlist[Sequence[int]]r   Iterator[Sequence[int]])rD   r\  r   zIterator[Edge])rE   r0   r   r   )rD   r\  r   r2   )rg   r   rD   r   r   r   )rD   zSequence[Sequence[int]]r   r]  )r[   r   r\   r   r   r   r   r   )r   r   r   r   )T)r   rB  rf  )rg   z
list[Vec3]rD   z
list[Face]rA  r%   r   rB  )rg   r   r   r^  )r  r  r  r  r   r  )r  r  r  r  )r  zIterable[Lump]r   z
list[Lump])r  r   r   zIterator[MeshTransformer])r
  r   r  r   r   r   )r  r   r   r   )rD   r\  r   zdict[Edge, list[Face]])r   r   rU  ri  rQ  r%   r   r!  )r   r   rU  ri  r   r!  )r   r   rU  rR  r   r!  rh  )r   r   rQ  r%   r   r]  )r   r   r   r!  )T
__future__r   typingr   r   r   r   r   r	   r
   r   r   r   r   typing_extensionsr   rI  r   r   r   r   r   r   r   r   r   r   r   r   r   ezdxf.entitiesr   r   r   r    ezdxf.eztypesr!   r"   r$   r%   r0   r,   r1   r2   	Exceptionr4   r8   r:   r<   r>   rF   rP   rS   rR   r_   rw   rz   r   r   r   r   r!  r<  rB  r  r  r6  r  r  r  r  r  r   r  rM  r  r  r  rR  r[  rW  rR  r\  r[  r-   r.   r/   <module>rd     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
	  	@ )*
"%	$"r.   