
    Mi                    l   S r SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKrSSKJr  SSKJr  SSKJrJr  SSK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!  \RD                  " \#5      r$\RJ                  r%\	R                  r&\RN                  r'\RP                  " \RB                  5      RR                  S	-  r*S
r+Sr,\RZ                  " S\RB                  S9r.S\.R^                  S'   S\0S\14S jr2SbS jr3S\44S jr5S r6S r7S\14S jr8S\14S jr9ScS\1S\14S jjr:S r;ScS jr<S r=S\S\\!   4S jr>S r? \R                  R                  rAS" rDS# rEScS$ jrFS% rGS& rHS' rIS( rJS) rKS* rLSdS\M4S+ jjrN\R                  SSS,S,SS,4S-\\\R                        S.\1S/\1S0\\   S1\14
S2 jjrQS3 rRS4 rSSeS5 jrT SeS6 jrUSfS7 jrVSgS8 jrWShS9 jrXS:\ \\4   S\4S; jrYS< rZSiS= jr[S> r\ SdS\ S?   4S@ jjr]    SjSA\1SB\1SC\\   SD\14SE jjr^SkSF jr_SG r`SH raSI rbSJ rcSlSK jrdSL reSM rfSN rgSdS\04SO jjrhSP riSQ rjSR rkSgSS jrlSmST jrmSnSU jrnSoSV\44SW jjroSoSV\44SX jjrp " SY SZ\5      rqSgS[ jrrS\ rsScS] jrt SdS^\\0   S_\ \\\4   S`\\   4Sa jjrug! \B a    \$R                  S 5        S! rA GNYf = f)pz 
Grab bag of utility functions.
    N)Mapping)deepcopy)BytesIOStringIO   )chain)		ArrayLikeDictIntegerIterableNDArrayOptionalSetUnionfloat64d   :0yE>F   dtype	WRITEABLEnamereturnc                 X    [         R                  S:  a  SSKJn  OSSKJn  U" U 5      SL$ )z
Check to see if a module is installed by name without
actually importing the module.

Parameters
------------
name : str
  The name of the module to check

Returns
------------
installed : bool
  True if module is installed
)   
   r   )	find_spec)find_loaderN)sysversion_infoimportlib.utilr   pkgutilr   )r   r   s     f/var/www/eduai.edurigo.com/storigo/production/storigo_env/lib/python3.13/site-packages/trimesh/util.py
has_moduler$   ;   s)     7", 	5T?$&&    c                 2   [         R                  " U 5      n Uc  [        n[        U R                  5      S:X  ab  [         R
                  " [         R                  " X -  S/U R                  S   -  5      5      nX2:  nX4==   S-  ss'   XR                  S5      -  nOo[        U R                  5      S:X  aK  [         R
                  " [         R                  " X 5      5      nX2:  nU(       a  X-  nOU R                  5       nO[        S5      eU(       a  XT   U4$ U$ )a  
Unitize a vector or an array or row-vectors.

Parameters
------------
vectors : (n,m) or (j) float
   Vector or vectors to be unitized
check_valid :  bool
   If set, will return mask of nonzero vectors
threshold : float
   Cutoff for a value to be considered zero.

Returns
---------
unit :  (n,m) or (j) float
   Input vectors but unitized
valid : (n,) bool or bool
    Mask of nonzero vectors returned if `check_valid`
         ?r   r)   r   z vectors must be (n, ) or (n, d)!)
np
asanyarrayTOL_ZEROlenshapesqrtdotreshapecopy
ValueError)vectorscheck_valid	thresholdnormvalidunits         r#   unitizer;   T   s    * mmG$G 	
7==Q wwrvvg/#q9I1IJK g..	W]]	q	 wwrvvg/0 >D<<>D;<<{E!!Kr%   c                    [         R                  " S[        SS9  [        R                  " U [        R
                  S9n [        R                  " U[        R
                  S9n[        R                  " X-
  S-  R                  5       5      $ )z:
DEPRECATED: use `np.linalg.norm(a - b)` instead of this.
zp`trimesh.util.euclidean` is deprecated and will be removed in January 2025. replace with `np.linalg.norm(a - b)`r'   category
stacklevelr   )warningswarnDeprecationWarningr+   r,   r   r0   sumabs     r#   	euclideanrG      se     MM	1 $ 	arzz*A
arzz*A77QUqL%%'((r%   c                 @    [        U S5      =(       d    [        U S5      $ )z
Check if an object is file-like

Parameters
------------
obj : object
   Any object type to be checked

Returns
-----------
is_file : bool
    True if object is a file
readwrite)hasattrobjs    r#   is_filerN      s     3873#88r%   c                 v    U R                   R                  n[        U S5      =(       a    UR                  S5      $ )z
Check if the object is a `pathlib.Path` or subclass.

Parameters
------------
obj : object
  Object to be checked

Returns
------------
is_pathlib : bool
  Is the input object a pathlib path
absolutePath)	__class____name__rK   endswithrM   r   s     r#   
is_pathlibrV      s.     ==!!D3
#=f(==r%   c                 V    [         R                  " S[        SS9  [        U [        5      $ )zd
DEPRECATED : this is not necessary since we dropped Python 2.

Replace with `isinstance(obj, str)`
zo`trimesh.util.is_string` is deprecated and will be removed in January 2025. replace with `isinstance(obj, str)`r'   r=   )r@   rA   rB   
isinstancestrrL   s    r#   	is_stringrZ      s,     MM	0 $ c3r%   c                 d  ^  [        T S5      (       + =(       a    [        T S5      =(       d    [        T S5      nU=(       a$    [        U 4S j[        [        [        4 5       5      nU=(       a    [        T 5      R                  S;  n[        T S5      (       a  U=(       a    T R                  S:g  nU$ )z
Check if an object is a sequence or not.

Parameters
-------------
obj : object
  Any object type to be checked

Returns
-------------
is_sequence : bool
    True if object is sequence
strip__getitem____iter__c              3   F   >#    U  H  n[        TU5      (       + v   M     g 7fN)rX   .0irM   s     r#   	<genexpr>is_sequence.<locals>.<genexpr>   s      G6F*S!,,,6Fs   !)
PointCloudr/    )rK   alldictsetrY   typerS   r/   )rM   seqs   ` r#   is_sequencerm      s     sG$$Dm)D ZJC
 
G#GtS#6FGGC 
:$s)$$N:C sG%cii2oJr%   allow_zerosc                 D   [        U S5      (       a"  [        U R                  5      [        U5      :w  a  g[        U 5      S:X  a  SU;   a  g[        U R                  U5       H<  u  p4[	        U5      (       a	  X4;   a  M    gUS:  a  US:X  a	  U(       d    gM5  XC:w  d  M<    g   g)a  
Compare the shape of a numpy.ndarray to a target shape,
with any value less than zero being considered a wildcard

Note that if a list-like object is passed that is not a numpy
array, this function will not convert it and will return False.

Parameters
------------
obj :   np.ndarray
  Array to check the shape on
shape : list or tuple
  Any negative term will be considered a wildcard
  Any tuple term will be evaluated as an OR
allow_zeros: bool
  if False, zeros do not match negatives in shape

Returns
---------
shape_ok : bool
  True if shape of obj matches query shape

Examples
------------------------
In [1]: a = np.random.random((100, 3))

In [2]: a.shape
Out[2]: (100, 3)

In [3]: trimesh.util.is_shape(a, (-1, 3))
Out[3]: True

In [4]: trimesh.util.is_shape(a, (-1, 3, 5))
Out[4]: False

In [5]: trimesh.util.is_shape(a, (100, -1))
Out[5]: True

In [6]: trimesh.util.is_shape(a, (-1, (3, 4)))
Out[6]: True

In [7]: trimesh.util.is_shape(a, (-1, (4, 5)))
Out[7]: False
r/   Fr   r)   T)rK   r.   r/   ziprm   )rM   r/   rn   rc   targets        r#   is_shaperr      s    b 3  C		Nc%j$@ 3x1}u
 E*	v{ A:Avk  ;+ +2 r%   c                 >    [        U 5      (       a  [        U 5      $ U /$ )ae  
Given an object, if it is a sequence return, otherwise
add it to a length 1 sequence and return.

Useful for wrapping functions which sometimes return single
objects and other times return lists of objects.

Parameters
-------------
obj : object
  An object to be made a sequence

Returns
--------------
as_sequence : (n,) sequence
   Contains input value
)rm   listrL   s    r#   make_sequenceru   H  s     $ 3Cyur%   c           
         [         R                  " U [         R                  S9n [        U S5      (       a  U [        * :  n[         R
                  " [         R                  " X [        :  5      5      n[         R                  " [        U 5      [         R                  S9nSXBSS2S4   '   SU[         R                  " USS2S4   USS2S4   5      '   O[        U S5      (       a  U [        * :  n[         R
                  " [         R                  " X [        :  5      5      n[         R                  " [        U 5      [         R                  S9nSXBSS2S4   '   SU[         R                  " USS2S4   USS2S4   5      '   SU[         R                  " [         R                  " USS2S4   USS2S4   5      USS2S4   5      '   O[        S	5      eXR                  S
5      -  nU(       a  XT4$ U$ )aB  
For a set of 3D vectors alter the sign so they are all in the
upper hemisphere.

If the vector lies on the plane all vectors with negative Y
will be reversed.

If the vector has a zero Z and Y value vectors with a
negative X value will be reversed.

Parameters
------------
vectors : (n, 3) float
  Input vectors
return_sign : bool
  Return the sign mask or not

Returns
----------
oriented: (n, 3) float
  Vectors with same magnitude as source
  but possibly reversed to ensure all vectors
  are in the same hemisphere.
sign : (n,) float
  [OPTIONAL] sign of original vectors
r   r)   r'   g      Nr   r   r)   r   r'   zvectors must be (n, 3)!r*   )r+   r,   r   rr   r-   logical_not
logical_oronesr.   logical_andr4   r2   )r5   return_signnegativezerosignsorienteds         r#   vector_hemispherer   `  s   8 mmG2::6G!! hY&~~bmmH6HIJGBJJ7 $q!tn =AbnnT!Q$Z!Q$89	'7	#	#hY&~~bmmH6HIJ GBJJ7 $q!tn<@bnnT!Q$Z!Q$89
  	NN2>>$q!t*d1a4jA8AqD>R	

 233 w//HOr%   c                    [         R                  " U [         R                  S9n [        U S5      (       d  [	        S5      e[        U SS9u  pSU[         R                  " U5      [        :  '   UR                  u  p4n[         R                  " [        U 5      S4[         R                  S9n[         R                  " [         R                  " XC5      [         R                  " U5      45      Xb'   U$ )z
Convert a set of cartesian points to (n, 2) spherical unit
vectors.

Parameters
------------
cartesian : (n, 3) float
   Points in space

Returns
------------
spherical : (n, 2) float
   Angles, in radians
r   rx   z Cartesian points must be (n, 3)!T)r6           r'   )r+   r,   r   rr   r4   r;   abs	TOL_MERGETzerosr.   column_stackarctan2arccos)	cartesianr:   r9   xyz	sphericals          r#   vector_to_sphericalr     s     irzz:IIw'';<<)6KD%(D		!"ffGA!#i.!,BJJ?I

1(8"))A,'GHIr%   r   c                    [         R                  " U [         R                  S9n [        U S5      (       d  [	        S5      eU R
                  u  p[         R                  " U5      [         R                  " U5      pC[         R                  " U5      [         R                  " U5      pe[         R                  " XE-  X5-  U45      $ )z
Convert an array of `(n, 2)` spherical angles to `(n, 3)` unit vectors.

Parameters
------------
spherical : (n , 2) float
   Angles, in radians

Returns
-----------
vectors : (n, 3) float
  Unit vectors
r   rw   z%spherical coordinates must be (n, 2)!)	r+   r,   r   rr   r4   r   sincosr   )r   thetaphistctspcps          r#   spherical_to_vectorr     s     irzz:IIw''@AAJEVVE]BFF5MVVC["&&+??BGRWb122r%   c                 4   [        U [        R                  5      (       aM  U R                  S5      n [        R                  " X 45      nUR                  S5      SS R                  S5      nU$ SSKnUR                  U 5      u  pE[        U5        [        XE5      $ )aW  
For an iterable, group values into pairs.

Parameters
------------
iterable : (m, ) list
   A sequence of values

Returns
-----------
pairs: (n, 2)
  Pairs of sequential values

Example
-----------
In [1]: data
Out[1]: [0, 1, 2, 3, 4, 5, 6]

In [2]: list(trimesh.util.pairwise(data))
Out[2]: [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

r)   r   rw   r   N)	rX   r+   ndarrayr2   r   	itertoolsteenextrp   )iterablestackedpairsr   rE   rF   s         r#   pairwiser     s    2 (BJJ''##B'//8"67#Ab)11': =="DAGq9r%   z1np.linalg.multi_dot not available, using fallbackc                     [         R                  " U 5      n U S   R                  5       nU SS  H  n[         R                  " X5      nM     U$ )z
Compute the dot product of two or more arrays in a single function call.
In most versions of numpy this is included, this slower function is
provided for backwards compatibility with ancient versions of numpy.
r   r   N)r+   r,   r3   r1   )arraysresultrc   s      r#   	multi_dotr     sE     v&!AVVF&F r%   c                     [         R                  " U 5      n [         R                  " X-  S/U R                  S   -  5      $ )a  
Dot product by row of a and b.

There are a lot of ways to do this though
performance varies very widely. This method
uses a dot product to sum the row and avoids
function calls if at all possible.

Comparing performance of some equivalent versions:
```
In [1]: import numpy as np; import trimesh

In [2]: a = np.random.random((10000, 3))

In [3]: b = np.random.random((10000, 3))

In [4]: %timeit (a * b).sum(axis=1)
1000 loops, best of 3: 181 us per loop

In [5]: %timeit np.einsum('ij,ij->i', a, b)
10000 loops, best of 3: 62.7 us per loop

In [6]: %timeit np.diag(np.dot(a, b.T))
1 loop, best of 3: 429 ms per loop

In [7]: %timeit np.dot(a * b, np.ones(a.shape[1]))
10000 loops, best of 3: 61.3 us per loop

In [8]: %timeit trimesh.util.diagonal_dot(a, b)
10000 loops, best of 3: 55.2 us per loop
```

Parameters
------------
a : (m, d) float
  First array
b : (m, d) float
  Second array

Returns
-------------
result : (m,) float
  Dot product of each row
r(   r   )r+   r,   r1   r/   rD   s     r#   diagonal_dotr     s7    ` 	aA 66!%#+,,r%   c                     [         R                  " [         R                  " U S-  S/U R                  S   -  5      5      $ )a  
Compute the norm per-row of a numpy array.

This is identical to np.linalg.norm(data, axis=1) but roughly
three times faster due to being less general.

In [3]: %timeit trimesh.util.row_norm(a)
76.3 us +/- 651 ns per loop

In [4]: %timeit np.linalg.norm(a, axis=1)
220 us +/- 5.41 us per loop

Parameters
-------------
data : (n, d) float
  Input 2D data to calculate per-row norm of

Returns
-------------
norm : (n,) float
  Norm of each row of input array
r'   r   )r+   r0   r1   r/   )datas    r#   row_normr   Q  s1    . 77266$'A3A#6788r%   c                 n   [         R                  " U [         R                  S9n U R                  nUS:X  a  SnOr[	        U5      S:w  a  [        S5      eUS   S:X  a8  [         R                  " U [         R                  " [	        U 5      5      45      n SnOUS   S:X  a  SnO[        S	5      eU(       a  X4$ U $ )
ab  
For a list of (n, 2) or (n, 3) points return them
as (n, 3) 3D points, 2D points on the XY plane.

Parameters
------------
points :  (n, 2) or (n, 3) float
  Points in either 2D or 3D space
return_2D : bool
  Were the original points 2D?

Returns
----------
points : (n, 3) float
  Points in space
is_2D : bool
  [OPTIONAL] if source points were (n, 2)
r   )r   Fr'   zPoints must be 2D array!r   Tr   z Points must be (n, 2) or (n, 3)!)r+   r,   r   r/   r.   r4   r   r   )points	return_2Dr/   is_2Ds       r#   stack_3Dr   k  s    & ]]64FLLE}	Uq344	qQ&"((3v;*?!@A	qQ;<<}Mr%   c           	      V   [         R                  " U [         R                  S9n [        U 5      S:w  a  [	        S5      e[         R                  " U[         R                  S9nUR
                  S:X  a#  [         R                  " XR
                  S   5      n[        U R                  U5       VVs/ s H  u  p#[         R                  " USU06PM     nnn[         R                  " [         R                  " USS065      R                  U R
                  S   S	5      R                  nU$ s  snnf )
a<  
Return a grid from an (2,dimension) bounds with samples step distance apart.

Parameters
------------
bounds: (2,dimension) list of [[min x, min y, etc], [max x, max y, etc]]
step:   float, or (dimension) floats, separation between points

Returns
---------
grid: (n, dimension), points inside the specified bounds
r   r'   bounds must be (2, dimension!rg   r   stepindexingijr)   )r+   r,   r   r.   r4   r/   tilerp   r   arangevstackmeshgridr2   )boundsr   rF   sgrid_elementsgrids         r#   grid_aranger     s     ]]64F
6{a899 ==RZZ0DzzRwwt\\!_-7:688T7JK7JtqRYY**7JMK
		"++}<t<=	a"	%	
 	
 K L   -"D%c           	      V   [         R                  " U [         R                  S9n [        U 5      S:w  a  [	        S5      e[         R                  " U[         R
                  S9nUR                  S:X  a#  [         R                  " XR                  S   5      n[        U R                  U5       VVs/ s H  u  p#[         R                  " USU06PM     nnn[         R                  " [         R                  " USS065      R                  U R                  S   S	5      R                  nU$ s  snnf )
a>  
Return a grid spaced inside a bounding box with edges spaced using np.linspace.

Parameters
------------
bounds: (2,dimension) list of [[min x, min y, etc], [max x, max y, etc]]
count:  int, or (dimension,) int, number of samples per side

Returns
---------
grid: (n, dimension) float, points in the specified bounds
r   r'   r   rg   r   numr   r   r)   )r+   r,   r   r.   r4   int64r/   r   rp   r   linspacer   r   r2   )r   countrF   cr   r   s         r#   grid_linspacer     s     ]]64F
6{a899MM%rxx0E{{b||A/8;FHHe8LM8LR[[!++8LMM
		"++}<t<=	a"	%	
 	
 K Nr   c                 v    [         R                  " [        5      nU  H  u  p#X   R                  U5        M     U$ )aN  
Given a set of key value pairs, create a dictionary.
If a key occurs multiple times, stack the values into an array.

Can be called like the regular dict(pairs) constructor

Parameters
------------
pairs: (n, 2) array of key, value pairs

Returns
----------
result: dict, with all values stored (rather than last with regular dict)

)collectionsdefaultdictrt   append)r   r   kvs       r#   
multi_dictr     s5      $$T*F	 Mr%   c                 D    [         R                  " [        U 5      5      nU$ )z
Ensure that any arrays or dicts passed containing
numpy arrays are properly converted to lists

Parameters
-------------
data : any
  Usually a dict with some numpy arrays as values

Returns
----------
result : any
  JSON-serializable version of data
)jsonloadsjsonify)r   r   s     r#   tolistr     s     ZZ&FMr%   c                     U R                  5       nU R                  S5      nU R                  U5        [        U[        5      nU H   nU(       a  [        U5      nOUnUS:  d  M     g   g)zc
Returns True if file has non-ASCII characters (> 0x7F, or 127)
Should work in both Python 2 and 3
i      TF)tellrI   seekrX   rY   ord)file_objstartfbytesis_strfbytecodes         r#   is_binary_filer     s_    
 MMOE]]4 FMM%$Fu:DD#:  r%   c                     U R                  5       nU R                  SS5        U R                  5       nU R                  U5        X!-
  nU$ )z
For an open file object how far is it to the end

Parameters
------------
file_obj: open file-like object

Returns
----------
distance: int, bytes to end of file
r   r'   )r   r   )r   position_currentposition_enddistances       r#   distance_to_endr     sC      }}MM!Q==?LMM"#.HOr%   c                     [        [        [        R                  " U 5      5      5      nUb  [        R                  " X!S5      n[        U5      $ )z
Return the number of digits to the first nonzero decimal.

Parameters
-----------
decimal:    float
min_digits: int, minimum number of digits to return

Returns
-----------

digits: int, number of digits to the first nonzero decimal
   )r   intr+   log10clip)decimal
min_digitsdigitss      r#   decimal_to_digitsr      s<     RXXg&'(FR0v;r%   Tloggerscolorscapture_warnings	blacklistonly_parentc                 `  ^ Uc  / SQn[         R                  " U5        [         R                  " SS5      nU(       a   SSKJn  U" SSSS	S
SSSS.S9nUc  [         R                  " 5       nUR                  U5        UR                  U 5        Uc;  [        [         R                  R                  R                  R                  5       5      nUR                  [         R                  " S5      5        S[         R                  " S5      l        U V	s0 s HV  n	[#        U	S5      (       d  M  [%        U	[         R                  5      (       d  M7  U	R&                  U;  d  MI  U	R&                  U	_MX     nn	U(       aV  0 n
[)        UR+                  5       5       H5  m[-        U4S jU
R+                  5        5       5      (       a  M-  UT   U
T'   M7     U
nUR                  5        H%  nUR/                  U5        UR                  U 5        M'     [0        R2                  " SSS9  g! [
         a     GNf = fs  sn	f )a   
Attach a stream handler to all loggers.

Parameters
------------
level : enum
  Logging level, like logging.INFO
handler : None or logging.Handler
  Handler to attach
loggers : None or (n,) logging.Logger
  If None, will try to attach to all available
colors : bool
  If True try to use colorlog formatter
blacklist : (n,) str
  Names of loggers NOT to attach to
only_parent
  Only attach to parent loggers, i.e. `trimesh`, `trimesh.sub1`, `trimesh.sub2`
  will only attach to `trimesh` and not the sub-loggers
N)
TerminalIPythonApp
PYREADLINEpyembreeshapely
matplotlibzparso.cacheparsozparso.python.diffasynciozprompt_toolkit.bufferzD[%(asctime)s] %(levelname)-7s (%(filename)s:%(lineno)3s) %(message)sz%Y-%m-%d %H:%M:%Sr   )ColoredFormatterzW%(log_color)s%(levelname)-8s%(reset)s %(filename)17s:%(lineno)-4s  %(blue)4s%(message)sTcyangreenyellowred)DEBUGINFOWARNINGERRORCRITICAL)datefmtreset
log_colorszpy.warningsr   r   c              3   L   >#    U  H  nTR                  U S 35      v   M     g7f).N)
startswith)rb   pr   s     r#   rd    attach_to_log.<locals>.<genexpr>  s%     O9NAt!Aw//9Ns   !$   )	precisionsuppress)loggingcaptureWarnings	Formattercolorlogr   ImportErrorStreamHandlersetFormattersetLevelrj   Loggermanager
loggerDictvaluesadd	getLoggerdisabledrK   rX   r   sortedkeysany
addHandlerr+   set_printoptions)levelhandlerr   r   r   r   r   	formatterr   Lparent_loggersloggerr   s               @r#   attach_to_logr+  4  s   < 
	 ,- !!NI 	1(J ##'" %I& '') #U gnn,,77>>@A KK!!-01 .2Gj!*
 A1f 	 q'..) 	 FF)#	 		   7<<>*DO9L9L9NOOO'.t}t$ +
 ! .."'" #
 !d3a  		.s)   H H+(H+	H+H+
H('H(c                    [         R                  " U 5      n [        U 5      S:X  a  [         R                  " / 5      $ [	        U S   5      (       a  S[        U S   5      4nOSn[         R
                  " U SS U SS 45      R                  U5      $ )a~  
Stack a list of values that represent a polyline into
individual line segments with duplicated consecutive values.

Parameters
------------
indices : (m,) any
  List of items to be stacked

Returns
---------
stacked : (n, 2) any
  Stacked items

Examples
----------
In [1]: trimesh.util.stack_lines([0, 1, 2])
Out[1]:
array([[0, 1],
       [1, 2]])

In [2]: trimesh.util.stack_lines([0, 1, 2, 4, 5])
Out[2]:
array([[0, 1],
       [1, 2],
       [2, 4],
       [4, 5]])

In [3]: trimesh.util.stack_lines([[0, 0], [1, 1], [2, 2], [3, 3]])
Out[3]:
array([[0, 0],
       [1, 1],
       [1, 1],
       [2, 2],
       [2, 2],
       [3, 3]])

r   r)   rw   Nr   )r+   r,   r.   arrayrm   r   r2   )indicesr/   s     r#   stack_linesr/    s~    N mmG$G
7|qxx|	WQZ	 	 S_%??GCRL'!"+67??FFr%   c                 n   [         R                  " U  Vs/ s H  n[        U5      PM     sn5      n[         R                  " S[         R                  " U5      SS 5      n/ n[        XA5       H)  u  pg[        U5      S:X  a  M  UR                  Xv-   5        M+     [        U 5      n[        U5      nX4$ s  snf )a  
Given a sequence of zero-indexed faces and vertices
combine them into a single array of faces and
a single array of vertices.

Parameters
-----------
vertices_seq : (n, ) sequence of (m, d) float
  Multiple arrays of verticesvertex arrays
faces_seq : (n, ) sequence of (p, j) int
  Zero indexed faces for matching vertices

Returns
----------
vertices : (i, d) float
  Points in space
faces : (j, 3) int
  Reference vertex indices
r   Nr)   )r+   r-  r.   r   cumsumrp   vstack_empty)	vertices_seq	faces_seqrc   vertices_lenface_offset	new_facesoffsetfacesverticess	            r#   append_facesr;    s    * 88\:\SV\:;L))Aryy6s;<KI[4u:?(	 5 L)H#E? ;s   B2c                    [         R                  " U 5      n [        U5      n[        U5      n[        U5      n[        U5      n[	        U R
                  5      S:  a  [        S[        U R
                  5      5      eU R                  R                  b  [        S5      eUR                  S5      nU R                  R                  S;   a  XA-   nO_U R                  R                  S:X  a%  UR                  SS	[        U5      -   S
-   5      U-   nO [        SU R                  R                  5      e[	        U5      n[	        U R
                  5      S:X  a.  X`R
                  S   -  nUS[	        U5      *  U-   n[	        U5      nU[	        U 5      -  n[         R                  " U R                  S5      SU45      R                  S5      nUR                  " U6 SU*  n	U	$ )a  
Convert a 1 or 2D array into a string with a specified number
of digits and delimiter. The reason this exists is that the
basic numpy array to string conversions are surprisingly bad.

Parameters
------------
array : (n,) or (n, d) float or int
   Data to be converted
   If shape is (n,) only column delimiter will be used
col_delim : str
  What string should separate values in a column
row_delim : str
  What string should separate values in a row
digits : int
  How many digits should floating point numbers include
value_format : str
   Format string for each value or sequence of values
   If multiple values per value_format it must divide
   into array evenly.

Returns
----------
formatted : str
   String representation of original array
r'   -conversion only works on 1D/2D arrays not %s!Nz<array is  structured, use structured_array_to_string instead{rc   uf{}{:.f}dtype %s not convertible!r   r*   r)   )r+   r,   r   rY   r.   r/   r4   r   namesr   kindreplacer   r   r2   format)
r-  	col_delim	row_delimr   value_formatrepeats
format_strend_junkshaped	formatteds
             r#   array_to_stringrR    s   8 MM% E[FIIII|$L 5;;!;S=M
 	

 {{$WXX   %G{{:%!-
			S	 !))$F0Cd0JKiW
4ekk6F6FGG 9~H
5;;1kk!n$
 13y>/2Y>
y> #e*J WWU]]7+a\:BB2FF !!6*:XI6Ir%   c           	      d   [         R                  " U 5      n [        U5      n[        U5      n[        U5      n[        U5      n[	        U R
                  5      S:  a  [        S[        U R
                  5      5      eU R                  R                  c  [        S5      eUR                  S5      S:  a  [        SU5      eSnU R                  R                   H  nX   R                  R                  n[	        X   R
                  5      S:X  a  X   R
                  S   OSnUS	;   a  UR                  S
S5      U-   n	OAUS:X  a%  UR                  S
S[        U5      -   S-   5      U-   n	O[        SU R                  5      eXXU	-  -  nM     US[	        U5      *  U-   nU[	        U 5      -  n[	        U 5      n
[         R                  " U R                  R                   Vs/ s H  oU   R                  U
S45      PM     sn5      R                  S5      nUR                  " U6 S[	        U5      *  nU$ s  snf )a  
Convert an unstructured array into a string with a specified
number of digits and delimiter. The reason thisexists is
that the basic numpy array to string conversionsare
surprisingly bad.

Parameters
------------
array : (n,) or (n, d) float or int
   Data to be converted
   If shape is (n,) only column delimiter will be used
col_delim : str
  What string should separate values in a column
row_delim : str
  What string should separate values in a row
digits : int
  How many digits should floating point numbers include
value_format : str
   Format string for each value or sequence of values
   If multiple values per value_format it must divide
   into array evenly.

Returns
----------
formatted : str
   String representation of original array
r   r=  Nz4array is not structured, use array_to_string insteadr>  zNvalue_format %s is invalid, repeating unstructured array values is unsupported r'   r?  rB  z{:0.0f}rA  rC  rD  rE  r)   )r+   r,   r   rY   r.   r/   r4   r   rF  r   rG  rH  hstackr2   rI  )r-  rJ  rK  r   rL  rN  r   rG  element_row_lengthelement_format_strr   r   	flattenedrQ  s                 r#   structured_array_to_stringrY  T  s    > MM% E[FIIII|$L 5;;!;S=M
 	

 {{ OPP #"&
 	
 J!!{  %%589J9J5Kq5PU[..q1VW:!-!5!5dI!F!RS[ $$T53v;+>+EFR  8%++FF+===
 "  -s9~o.:J#e*J JE		050A0AB0A1q		5"+	&0ABgbk 
 !!9-.?Y@I 	Cs   H-c                    [         R                  " U 5      n U R                  n[         R                  " U 5      nUc  U R                  n[         R                  " U5      R
                  US.nUS;   a[  [        R                  " UR                  U5      R                  5       5      n[        US5      (       a  UR                  S5      nXeS'   U$ US:X  a  U R                  SS9US'   U$ [        S	U S
35      e)aZ  
Export a numpy array to a compact serializable dictionary.

Parameters
------------
array : array
  Any numpy array
dtype : str or None
  Optional dtype to encode array
encoding : str
  'base64' or 'binary'

Returns
---------
encoded : dict
  Has keys:
  'dtype':  str, of dtype
  'shape':  tuple of shape
  'base64': str, base64 encoded string
)r   r/   )base64dict64decodeutf-8r[  binaryC)orderz	encoding z is not available!)r+   r,   r/   ravelr   rY   r[  	b64encodeastypetobytesrK   r]  r4   )r-  r   encodingr/   flatencodedpackeds          r#   array_to_encodedrj    s    * MM% EKKE88E?D}++e<G''!!$++e"4"<"<">?68$$]]7+F"
 N	 
X	!MMM4 N 9XJ.@ABBr%   c                     U R                  5       nU HT  n[        US5      (       d  M  UR                  U5      nX4:w  d  M.  X   XR                  U5      '   U R                  U5        MV     U $ )a~  
If a dictionary has keys that are bytes decode them to a str.

Parameters
------------
store : dict
  Dictionary with data

Returns
---------
result : dict
  Values are untouched but keys that were bytes
  are converted to ASCII strings.

Example
-----------
In [1]: d
Out[1]: {1020: 'nah', b'hi': 'stuff'}

In [2]: trimesh.util.decode_keys(d)
Out[2]: {1020: 'nah', 'hi': 'stuff'}
r]  )r!  rK   r]  pop)storerf  r!  keydecodeds        r#   decode_keysrp    s^    . ::<D3!!jj*G~.3jjj*+		#  Lr%   c                    X;  a  U $ X-   R                  U5      nU R                  U5      (       a  SnOUS   nU Vs/ s H  oUR                  US5      PM     nnUU-   UR                  S U 5       5      -   nUR                  5       nU$ s  snf )a$  
Strip comments from a text block.

Parameters
-----------
text : str
  Text to remove comments from
starts_with : str
  Character or substring that starts a comment
new_line : str
  Character or substring that ends a comment

Returns
-----------
stripped : str
  Text with comments stripped
rT  r   r   c              3   v   #    U  H/  n[        U5      S :  d  M  [        US    5      S:  d  M(  US    v   M1     g7f)r   r   N)r.   )rb   rc   s     r#   rd    comment_strip.<locals>.<genexpr>  s0     NgQ!AaD	A!gs   999)splitr  joinr\   )textstarts_withnew_linert  leadrc   removedr   s           r#   comment_stripr{    s    &  _##K0E {##Qx .33Uwwx#UG3 	
	
--NgN
N	O  \\^FM 4s   B
rh  c                    [        U [        5      (       d3  [        U 5      (       a  [        R                  " U 5      nU$ [        S5      e[        U 5      n [        R                  " U S   5      nSU ;   a/  [        R                  " [        R                  " U S   5      U5      nOSU ;   a  [        R                  " U S   US9nSU ;   a  WR                  U S   5      nW$ )a-  
Turn a dictionary with base64 encoded strings back into a numpy array.

Parameters
------------
encoded
  Has keys:
    dtype: string of dtype
    shape: int tuple of shape
    base64: base64 encoded string of flat array
    binary:  decode result coming from numpy.tobytes

Returns
----------
array
z(Unable to extract numpy array from inputr   r[  r_  r   r/   )rX   ri   rm   r+   r,   r4   rp  r   
frombufferr[  	b64decoder2   )rh  as_arrayr   r-  s       r#   encoded_to_arrayr  $  s    $ gt$$w}}W-HOGHH'"GHHWW%&E7f..wx/@A5I	W	gh/u='gg./Lr%   c                    ^   [        U[        5      (       a  [        U 4S jU 5       5      $ [        T U5        g! [         a     gf = f)a4  
Given an object, if it is a member of the class 'name',
or a subclass of 'name', return True.

Parameters
------------
obj : instance
  Some object of some class
name: str
  The name of the class we want to check for

Returns
---------
is_instance : bool
  Whether the object is a member of the named class
c              3   <   >#    U  H  n[        TU5      v   M     g 7fr`   )is_instance_namedra   s     r#   rd   $is_instance_named.<locals>.<genexpr>\  s     ?$Q(a00$s   TF)rX   rt   r"  
type_namedr4   rU   s   ` r#   r  r  I  sE    "dD!!?$???sD! s   ): : 
AAc                    [         R                  " [        U R                  R                  5      /5      n[        U5       H6  nUR                  US    Vs/ s H  o3c  M  UR                  PM     sn5        M8      [        R                  " U5      nU Vs/ s H  n[        US5      (       d  M  UPM     sn$ s  snf ! [         a    / n N9f = fs  snf )z(
Return the bases of the object passed.
r)   rS   )r   dequert   rR   	__bases__ranger   __base__r+   rU  
IndexErrorrK   )rM   depthbasesrc   s       r#   
type_basesr  d  s     tCMM$;$;<=>E5\%)E)Qjajj)EF 		%  7u!: 6Au77 F  7s*   B7
B7
;B< C.C<C
Cc                     [        U5      nU R                  R                  U:X  a  U R                  $ [        U 5       H  nUR                  U:X  d  M  Us  $    [	        SU-   5      e)z
Similar to the type() builtin, but looks in class bases
for named instance.

Parameters
------------
obj : any
  Object to look for class of
name : str
  Nnme of class

Returns
----------
class : Optional[Callable]
  Camed class, or None
z Unable to extract class of name )rY   rR   rS   r  r4   )rM   r   bases      r#   r  r  r  s[    $ t9D
}}%}}3==D K   7$>
??r%   )ztrimesh.Trimeshztrimesh.path.Path2Dztrimesh.path.Path3Dc                 `	  ^ / n[        X5       HF  n[        US5      (       a!  UR                  UR                  5       5        M5  UR	                  U5        MH     [        U5      S:X  a  US   R                  5       $ [        U5      S:X  a  SSKJn  U" 5       $ U Vs/ s H  n[        US5      (       d  M  UPM     nnU Vs/ s H  n[        US5      (       d  M  UPM     nn[        U5      [        U5      :  a  SSK	J
n  U" U5      $ [        U5      S:X  a  / $ [        US   S5      n	[        U V
s/ s H  oR                  R                  5       PM     sn
U V
s/ s H  oR                  R                  5       PM     sn
5      u  pSn[        S	 U 5       5      (       aA  [!        U V
s/ s H  oR"                  PM     sn
5      nUR$                  UR$                  :X  d   eSn[        S
 U 5       5      (       aA  [!        U V
s/ s H  oR&                  PM     sn
5      nUR$                  UR$                  :X  d   e US   R(                  R                  USS  V
s/ s H  oR(                  PM     sn
5      n0 n UR3                  S U 5       5      /  0 nUS   R4                  R7                  5        Hc  m[9        U4S jU 5       5      (       d  M   [:        R                  " U Vs/ s H  nUR4                  R=                  T/ 5      PM!     snSS9UT'   Me     0 nUS   R@                  R7                  5        Hc  m[9        U4S jU 5       5      (       d  M   [:        R                  " U Vs/ s H  nUR@                  R=                  T/ 5      PM!     snSS9UT'   Me     U	" UUUUUUUUSS9	n [C        US   RD                  5      Ul#        U$ s  snf s  snf s  sn
f s  sn
f s  sn
f s  sn
f s  sn
f ! [*         a5  n[,        R/                  S[0         3SS9  Sn[0        (       a  Ue SnAGNSnAff = f! [*         a     GNf = fs  snf ! [*         a    [,        R?                  ST S3SS9   GM  f = fs  snf ! [*         a    [,        R?                  ST S3SS9   GMe  f = f! [*         a     U$ f = f)a  
Concatenate two or more meshes.

Parameters
------------
a : trimesh.Trimesh
  Mesh or list of meshes to be concatenated
  object, or list of such
b : trimesh.Trimesh
  Mesh or list of meshes to be concatenated

Returns
----------
result
  Concatenated mesh
Scener   r   )Trimeshr  rQ   )concatenateNc              3   @   #    U  H  nS UR                   ;   v   M     g7f)face_normalsN_cacherb   ms     r#   rd   concatenate.<locals>.<genexpr>  s     
7w!>QXX%w   c              3   @   #    U  H  nS UR                   ;   v   M     g7f)vertex_normalsNr  r  s     r#   rd   r    s     
9Aqxx'r  zfailed to combine visuals Texc_infoc              3   L   #    U  H  n[        UR                  5      v   M     g 7fr`   )r   metadatar  s     r#   rd   r    s     ?w!!**--ws   "$c              3      >#    U  H@  n[        UR                  R                  T/ 5      5      [        UR                  5      :H  v   MB     g 7fr`   )r.   vertex_attributesgetr:  rb   r  rn  s     r#   rd   r    s8     YQXAs1&&**334AJJGQX   AAaxisz)Failed to concatenate `vertex_attribute['z']`c              3      >#    U  H@  n[        UR                  R                  T/ 5      5      [        UR                  5      :H  v   MB     g 7fr`   )r.   face_attributesr  r9  r  s     r#   rd   r    s6     TGqs1$$((b12c!''lBGr  z'Failed to concatenate `face_attribute['F)	r:  r9  r  r  visualr  r  r  process)$r   r  extenddumpr   r.   r3   r  r  	path.utilr  r  r;  r:  r9  r"  r2  r  r/   r  r  BaseExceptionlogdebug_STRICTupdater  r!  rh   r+   r  warningr  r   source_source)rE   rF   r  rc   r  rA  is_meshis_pathconcatenate_pathtrimesh_typer  r:  r9  r  r  r  Er  r  meshr  r   rn  s                         @r#   r  r    sP   & D1[Q((KK! KKN  4yA~Aw||~	Ta!yB$Q"3Ay"Aq$GB?$Q"3Av">q$G? 7|c'l">((
7|q	 gaj)4L #$+,Gq	G,w.Ow!ww||~w.OOH
 L

7w
777#W$EW^^W$EF!!U[[000 N

9
999%&IA'7'7&IJ##x~~555""..'!"+/N+Q+/NO H	?w?	?@
 qz++002YQXYYY)+ELMWTT++//R8WMTU*!#&	 3 Oqz))..0TGTTT	')~~CJK74T))--c267KRS($ 1 !%+'
F!'!*"3"34 MA C?" 	-.O %F 'J
 0O 		.wi84	H7G    N  ?uCHSW   L  =cU#FQU  *  Ms   O4O OO0#O#O
'OO$:O. O).O. 8P0 Q$&Q

	QQ5*&Q0
	Q5,R )O. .
P-8*P((P-0
P>=P>Q"Q-,Q-0Q55"RR
R-,R-repaironly_watertight	min_facesr   c                 8   [        U5      n[        U5      S:X  a  / $ U R                  R                  [        R
                  5      nU R                  R                  [        R
                  5      n/ n/ n	/ n
/ n[        R                  " [        U5      5      nU GHC  n[        R                  " U5      n[        U5      S:X  a  M+  UR                  R                  S:X  a1  UR                  5       (       d  M\  Ub  UR                  5       U:  a  Mu  OUb  [        U5      U:  a  M  Xm   n[        R                  " UR                  S5      5      n[        R                  " [        U5      5      X'   U
R                  U R                   U   5        UR                  X   5        U	R                  X   5         UR                  U R"                  R%                  U5      5        GMF     [        U	5      S:X  a  [        R(                  " / 5      $ [+        U S5      nU(       a  Sn [        R(                  " U5      nUS   R-                  USS 5      n[3        X5      u  pU" U	U[        R4                  " U
5      U[7        U R8                  5      S
S9n[7        U R:                  5      Ul        U$ Uc  S/[        U	5      -  n[?        XX5       VVVVs/ s H&  u  nnnnU" UUUU[7        U R8                  5      S
S9PM(     nnnnnU Vs/ s H#  n[A        US[7        U R:                  5      5      PM%       nU(       a@  U Vs/ s H2  n[        UR                  5      S:  =(       a    URC                  5       PM4     nnO#U(       a  U Vs/ s H  nURD                  PM     nnU(       a+  [?        UW5       VVs/ s H  u  nnU(       d  M  UPM     snn$ U$ ! [&         a  nUeSnAff = f! [&         a    [.        R1                  SSS	9   GNf = fs  snnnnf s  snf s  snf s  snf s  snnf )a$  
Return a subset of a mesh.

Parameters
------------
mesh : Trimesh
    Source mesh to take geometry from
faces_sequence : sequence (p,) int
    Indexes of mesh.faces
repair
    Try to make submeshes watertight
only_watertight
    Only return submeshes which are watertight
min_faces
  Minimum number of faces allowed in a submesh.
append : bool
    Return a single mesh which has the faces appended,
    if this flag is set, only_watertight is ignored

Returns
---------
result : Trimesh | list[Trimesh]
  Depending on if `append` is true or not.
r   rF   Nr)   r  r   zfailed to combine visualsTr  F)r:  r9  r  r  r  r  r  r   )#rt   r.   r9  viewr+   r   r:  r   r,   r   rG  r"  rC   uniquer2   r   r  r  face_subsetr  r-  r  r  r  r  r;  r   r   r  r  r  rp   setattr
fill_holesis_watertight)r  faces_sequencer  r  r  r   original_facesoriginal_verticesr9  r:  normalsvisualsmaskindexcurrentr  r  r  r  appendedr   rA  nr   r   rrc   
watertightws                                r#   submeshr    s   B .)N
>a	 ZZ__RZZ0N**2::6EHGG 99S*+,De$u:?;;s"99;;$y)@"s5zI'= '7??2./ yyV-t((/0T]#)12	NN4;;2259:5  > 8}xx| dI.L	Bhhw'GQZ++GABK8F 'x77+dmm,
 $DKK0&3x=( hw@
 AJAq!Q 	dmm,	
 A  
 <BB6aWQ	8DKK016B EKKFqc!''la':ALLN:F
K
	/56v!aoov
6!&*5;5da5;;M  	G	  	BII1DIA	B(
 C
 L 7 <sN   1*O-O -O?
<*P39P:P(P:P
OOOO<;O<c                    [        U 5      S:X  a  [        R                  " U5      $ [        U 5      U:  a<  [        R                  " U5      nU(       a  X[        U 5      * S& U$ XS[        U 5      & U$ [        R                  " U 5      $ )z
Parameters
------------
data : (n,)
  1D array
count : int
  Minimum length of result array

Returns
---------
padded : (m,)
  1D array where m >= count
r   N)r.   r+   r   r,   )r   r   rightpaddeds       r#   zero_padr    st     4yA~xx	TU	%#'CI:<   #';SY}}T""r%   c                 f     " S S[         R                  5      n[         R                  " U 4SU0UD6$ )a	  
A version of json.dumps that can handle numpy arrays
by creating a custom encoder for numpy dtypes.

Parameters
--------------
obj : list, dict
  A JSON-serializable blob
kwargs : dict
  Passed to json.dumps

Returns
--------------
dumped : str
  JSON dump of obj
c                       \ rS rSrS rSrg)jsonify.<locals>.EdgeEncoderi  c                     [        US5      (       a  UR                  5       $ [        US5      (       a  UR                  5       $ [        R                  R                  X5      $ )Nr   	timestamp)rK   r   r  r   JSONEncoderdefault)selfrM   s     r#   r  $jsonify.<locals>.EdgeEncoder.default  sM     sH%%zz|#k**}}&##++D66r%   rg   N)rS   
__module____qualname____firstlineno__r  __static_attributes__rg   r%   r#   EdgeEncoderr    s    	7r%   r  cls)r   r  dumps)rM   kwargsr  s      r#   r   r     s/    $7d&& 7 ::c5{5f55r%   c                    [        U[        R                  5      (       a  [        R                  " XR                  S9$ [        XR
                  5      (       d  Uc  U $ [        U 5      (       a2  [        U 5      S:X  a#  [        U S   UR
                  5      (       a  U S   $ [        U [        5      (       aG  UR
                  R                  S:X  a-  U R                  S5      (       a  SSKJn  UR                  U 5      $ UR                  U 5      n U $ )z
Convert an item to have the dtype of another item

Parameters
------------
item : any
  Item to be converted
like : any
  Object with target dtype
  If None, item is returned unmodified

Returns
----------
result: item, but in dtype of like
r   r   r   PolygonPOLYGON)wkt)rX   r+   r   r,   r   rR   rm   r.   rY   rS   r  r   r  r   )itemliker  s      r#   convert_liker    s    " $

##}}T44 $''4< 4SY!^
47DNN0S0SAw 	4NN##y0OOI&& 	 yy >>$DKr%   c           
         SSK n[        R                  " U [        R                  SS9n [	        U R
                  5      S:X  a;  U R
                  S   S:w  a  [        S5      eU R                  [	        U 5      S	45      n O4[	        U R
                  5      S:w  d  U R                  S:X  a  [        S
5      eU R
                  S   nUS-  S:w  a  [        S5      e[        US-  5      nUR                  R                  US9nUR                  R                  [        [        R                  " [	        U 5      5      U S/[	        U 5      -  5      US9$ )a>  
Given a set of axis aligned bounds create an r-tree for
broad-phase collision detection.

Parameters
------------
bounds : (n, 2D) or (n, 2, D) float
  Non-interleaved bounds where D=dimension
  E.G a 2D bounds tree:
  [(minx, miny, maxx, maxy), ...]

Returns
---------
tree : Rtree
  Tree containing bounds by index
r   NTr   r3   r   r   r'   zbounds not (n, 2, dimension)!r)   z"Bounds must be (n, dimension * 2)!zBounds must be (n,dimension*2)!)	dimension)
properties)rtreer+   r-  r   r.   r/   r4   r2   sizer   r  PropertyIndexrp   r   )r   r  r  r  s       r#   bounds_treer    s   "  XXfBJJT:F
6<<A<<?a<==Vb 12	V\\	a	6;;!#3=>> QIA!:;;IM"I%%	%:J;;BIIc&k"FTFS[,@Aj   r%   c                     [        U [        5      (       a  [        U 5      $ [        U [        5      (       a  [	        U 5      $ [        [        U 5      R                   S35      e)z
Wrap a string or bytes object as a file object.

Parameters
------------
item: str or bytes
  Item to be wrapped

Returns
---------
wrapped : file-like object
  Contains data from item
z is not wrappable!)rX   rY   r   bytesr   r4   rk   rS   )r  s    r#   wrap_as_streamr  7  sP     $~	D%	 	 t}
T
++,,>?
@@r%   c                 0    [        X5      u  p#USU-  -  nU$ )a
  
Round a single value to a specified number of significant figures.

Parameters
------------
values : float
  Value to be rounded
sigfig : int
  Number of significant figures to reduce to

Returns
----------
rounded : float
  Value rounded to the specified number of significant figures


Examples
----------
In [1]: trimesh.util.round_sigfig(-232453.00014045456, 1)
Out[1]: -200000.0

In [2]: trimesh.util.round_sigfig(.00014045456, 1)
Out[2]: 0.0001

In [3]: trimesh.util.round_sigfig(.00014045456, 4)
Out[3]: 0.0001405
r   )
sigfig_int)r  sigfigas_int
multiplierroundeds        r#   sigfig_roundr  L  s$    8 $F3FJ'GNr%   c                 p   [         R                  " U 5      R                  S5      n [         R                  " U[         R                  S9R                  S5      nUR                  U R                  :w  a  [        S5      e[         R                  " [        U 5      5      n[         R                  " U 5      [        :  n[         R                  " [         R                  " [         R                  " X   5      5      5      X#'   X!-
  S-   nU SU-  -  R                  5       R                  [         R                  5      nXT4$ )a  
Convert a set of floating point values into integers
with a specified number of significant figures and an
exponent.

Parameters
------------
values : (n,) float or int
  Array of values
sigfig : (n,) int
  Number of significant figures to keep

Returns
------------
as_int : (n,) int
  Every value[i] has sigfig[i] digits
multiplier : (n, int)
  Exponent, so as_int * 10 ** multiplier is
  the same order of magnitude as the input
r)   r   zsigfig must match identifierr   r   )r+   r,   r2   r   r/   r4   r   r.   r   r-   floorr   roundrd  )r  r  exponentnonzeror  r  s         r#   r   r   n  s    * ]]6"**2.F]]62::2>F||v||#788xxF$HffVnx'G"&&*A!BCH"Q&JJ'..077AFr%   c           	         [        U5      R                  5       n[        U [        5      (       a  [	        U 5      n UR                  S5      (       aO  [        R                  " U 5      nUR                  5        Vs0 s H  o3[	        UR                  U5      5      _M     sn$ UR                  S5      (       a=  SSK
n[        U SS5      SS nU[	        UR                  U SS	9R                  5       5      0$ S
USS ;   aD  SSKnUR                  U SS9nUR                  5        Vs0 s H  o3UR                  U5      _M     sn$ [!        S5      es  snf s  snf )aP  
Given an open file object and a file type, return all components
of the archive as open file objects in a dict.

Parameters
------------
file_obj : file-like
  Containing compressed data
file_type : str
  File extension, 'zip', 'tar.gz', etc

Returns
---------
decompressed : dict
  Data from archive in format {file name : file-like}
rp   bz2r   Nr   archive1234r  )modetari)fileobjr  zUnsupported type passed!)rY   lowerrX   r  r  rT   zipfileZipFilenamelistrI   r  getattropentarfilegetnamesextractfiler4   )r   	file_typearchiver   r  r  s         r#   
decompressr    s7   $ I$$&I(E""!(+%  //(+ELEUEUEWXEWTnW\\$%788EWXX%   x7<nSXXhSX%A%F%F%HIJJ	"#,,xc,:<C<L<L<NO<NDg))$//<NOO
/
00 Y Ps   8$E
!Ec                 v   [        5       n[        R                  " U4S[        R                  S.UD6 nU R	                  5        H7  u  pE[        US5      (       a  UR                  5       nUR                  XE5        M9     SSS5        UR                  S5        UR                  5       nU$ ! , (       d  f       N1= f)z
Compress data stored in a dict.

Parameters
-----------
info : dict
  Data to compress in form:
  {file name in archive: bytes or file-like object}
kwargs : dict
  Passed to zipfile.ZipFile
Returns
-----------
compressed : bytes
  Compressed file data
r  )r  compressionrI   Nr   )	r   r  r  ZIP_DEFLATEDitemsrK   rI   writestrr   )infor  r   zipperr   r   
compresseds          r#   compressr&    s      yH	
(<(<
@F
	**,JDtV$$yy{OOD'	 '
 MM!J
 
s   AB**
B8c                     [        U 5      n Uc  SS/nU R                  [        U5      5      (       a#  U H  nU R                  U5      (       d  M  Us  $    U R                  S5      S   $ )aU  
Find the file extension of a file name, including support for
special case multipart file extensions (like .tar.gz)

Parameters
------------
file_name : str
  File name
special : list of str
  Multipart extensions
  eg: ['tar.bz2', 'tar.gz']

Returns
----------
extension : str
  Last characters after a period, or
  a value from 'special'
ztar.bz2ztar.gzr
  r)   )rY   rT   tuplert  )	file_namespecialends      r#   split_extensionr,    sg    & IIh'%.))C!!#&&
  ??3##r%   c                 p   [         R                  " U  Vs/ s H  n[        U5      PM     sn[         R                  S9n[         R                  " U [         R                  S9n[         R                  " USS USS USS /[         R                  S9R
                  n[        U 5      S:X  a$  [         R                  " USSS2   5      USSS2'   U$ [         R                  " U5      SS n[         R                  " [        U5      [        S9nSXeS-
  '   SXeS-
  '   XF   n[         R                  " S[         R                  " US-
  5      5      n[         R                  " US   [        S9n[        [        U5      S-
  5       H  nS	XuU   S-   XQS-       SSS2'   M     [         R                  " XG   5      XG'   U$ s  snf )
a  
Convert a sequence of triangle strips to (n, 3) faces.

Processes all strips at once using np.concatenate and is significantly
faster than loop-based methods.

From the OpenGL programming guide describing a single triangle
strip [v0, v1, v2, v3, v4]:

Draws a series of triangles (three-sided polygons) using vertices
v0, v1, v2, then v2, v1, v3  (note the order), then v2, v3, v4,
and so on. The ordering is to ensure that the triangles are all
drawn with the same orientation so that the strip can correctly form
part of a surface.

Parameters
------------
strips: (n,) list of (m,) int
  Vertex indices

Returns
------------
faces : (m, 3) int
  Vertex indices representing triangles
r   Nr   r)   r'   Fr   T)r+   r-  r.   r   r  r   fliplrr1  r{   boolr   r   r  )stripsrc   lengthsblobtrilength_indexkeepflips           r#   triangle_strips_to_facesr8    s   8 hh/1A/rxx@G >>&1D ((D"ItAbz484BHH
E
G
GC 6{aIIc!$Q$i(ADqD	
 99W%cr*L773s84(D"D	"D	
)C 99Q		'A+ 67L88L$D1D3|$q()?C!_q <A#67!< *		#)$CIJ= 0s   F3c                     U  Vs/ s HK  n[         R                  " US   [         R                  " [        U5      S-
  [        S9-  USS USS /5      PMM     nn[         R
                  " U5      $ s  snf )z
Convert fans of m + 2 vertex indices in fan format to m triangles

Parameters
----------
fans: (n,) list of (m + 2,) int
  Vertex indices

Returns
-------
faces: (m, 3) int
  Vertex indices representing triangles
r   r'   r   r   r)   N)r+   	transposer{   r.   r   r  )fansfanr9  s      r#   triangle_fans_to_facesr=  3  st    " C 	c!frwws3x!|3??QrCPQPRGTU 
  >>%  	s   AA/c                    U  Vs/ s H  n[        U5      S:  d  M  UPM     nn[        U5      S:X  a  [        R                  " US   5      $ [        U5      S:X  a  [        R                  " / 5      $ [        R                  " U5      $ s  snf )z
A thin wrapper for numpy.vstack that ignores empty lists.

Parameters
------------
tup : tuple or list of arrays
  With the same number of columns

Returns
------------
stacked : (n, d) array
  With same number of columns as
  constituent arrays.
r   r   )r.   r+   r,   r-  r   )tuprc   	stackables      r#   r2  r2  I  so       .Cq3q6A:CI.
9~}}Yq\**	Y1	xx|99Y /s
   BBc                 d   S[        U SS5      ;   n[        U[        5      n[        U[        5      nU(       a(  U(       a!  U R	                  UR                  U5      5        O@U(       d(  U(       a!  U R	                  UR                  U5      5        OU R	                  U5        U R                  5         U$ )a  
If a file is open in binary mode and a
string is passed, encode and write.

If a file is open in text mode and bytes are
passed decode bytes to str and write.

Assumes binary mode if file_obj does not have
a 'mode' attribute (e.g. io.BufferedRandom).

Parameters
-----------
file_obj : file object
  With 'write' and 'mode'
stuff :  str or bytes
  Stuff to be written
encoding : str
  Encoding of text
rF   r  )r  rX   rY   r  rJ   encoder]  flush)r   stuffrf  binary_filestring_stuffbinary_stuffs         r#   write_encodedrH  d  s~    ( 6377KeS)LeU+L|u||H-.\u||H-.uNNLr%   c                 n    [         R                  " [        R                  " S5      SS9R                  SU  $ )z
Generate a random alphaNumber unique identifier
using UUID logic.

Parameters
------------
length : int
  Length of desired identifier

Returns
------------
unique : str
  Unique alphaNumber identifier
   r   )r   versionN)uuidUUIDrandomgetrandbitshex)lengths    r#   	unique_idrR    s-     99++C0!<@@&IIr%   c                 6   [         R                  " U [         R                  SS9n U R                  S:w  a  [	        S5      e[         R
                  R                  U 5      nX!:  a  [         R                  " S5      $ X-  n [         R                  " U S   * U S   S/5      n[         R
                  R                  U5      nXA:  aC  [         R                  " U S	   * U S   S/5      nU[         R
                  R                  U5      -  nOX4-  n[         R                  " X5      n[         R                  " X5U /[         R                  S
9n[        (       a  [         R                  " [         R                  " X05      5      S:  d   e[         R                  " [         R                  " XP5      5      S:  d   e[         R                  " [         R                  " X55      5      S:  d   e[         R                  " [         R
                  R                  USS9S5      (       d   eU$ )al  
Generate an arbitrary basis (also known as a coordinate frame)
from a given z-axis vector.

Parameters
------------
z : (3,) float
  A vector along the positive z-axis.
epsilon : float
  Numbers smaller than this considered zero.

Returns
---------
x : (3,) float
  Vector along x axis.
y : (3,) float
  Vector along y axis.
z : (3,) float
  Vector along z axis.
Tr  )r   zz must be (3,) float!r   r   r   r   r'   r   r   r  r(   )r+   r-  r   r/   r4   linalgr8   eyecrossr  r   r1   allclose)r   epsilonz_normr   x_normr   r   s          r#   generate_basisr[    s   , 	"**40Aww$011YY^^AFvvay KA
1Q4%1s#$AYY^^AF HHqteQqT3'(	RYY^^A 	

AXXqQirzz2FwvvbffQl#d***vvbffQl#d***vvbffQl#d***{{299>>&q>93????Mr%   atolc                 B    X-
  n[         R                  " X2* :  X2:  5      $ )a  
A replacement for np.isclose that does fewer checks
and validation and as a result is roughly 4x faster.

Note that this is used in tight loops, and as such
a and b MUST be np.ndarray, not list or "array-like"

Parameters
------------
a : np.ndarray
  To be compared
b : np.ndarray
  To be compared
atol : float
  Acceptable distance between `a` and `b` to be "close"

Returns
-----------
close : np.ndarray, bool
  Per-element closeness
)r+   r|   )rE   rF   r\  diffs       r#   iscloser_    s"    , 5D>>$,44r%   c                 J    [        [        R                  " X-
  5      5      U:  $ )aD  
A replacement for np.allclose that does few checks
and validation and as a result is faster.

Parameters
------------
a : np.ndarray
  To be compared
b : np.ndarray
  To be compared
atol : float
  Acceptable distance between `a` and `b` to be "close"

Returns
-----------
bool indicating if all elements are within `atol`.
)floatr+   ptp)rE   rF   r\  s      r#   rW  rW    s    & $&&r%   c                   B    \ rS rSrSrS rS rS rS rS r	S r
S	 rS
rg)FunctionRegistryi	  a  
Non-overwritable mapping of string keys to functions.

This allows external packages to register additional implementations
of common functionality without risk of breaking implementations provided
by trimesh.

See trimesh.voxel.morphology for example usage.
c                 L    0 U l         UR                  5        H	  u  p#X0U'   M     g r`   )_dictr!  )r  r  r   r   s       r#   __init__FunctionRegistry.__init__	  s"    
LLNDAG #r%   c                      U R                   U   $ r`   rf  r  rn  s     r#   r]   FunctionRegistry.__getitem__	  s    zz#r%   c                     [        U[        5      (       d  [        SU< 35      eX;   a  [        SU 35      e[	        U5      (       d  [        S5      eX R
                  U'   g )Nzkey must be a string, got z%Cannot set new value to existing key z'Cannot set value which is not callable.)rX   rY   r4   KeyErrorcallablerf  )r  rn  values      r#   __setitem__FunctionRegistry.__setitem__	  sZ    #s##9#ABB;B3%HIIFGG

3r%   c                 ,    [        U R                  5      $ r`   )iterrf  r  s    r#   r^   FunctionRegistry.__iter__!	  s    DJJr%   c                 ,    [        U R                  5      $ r`   )r.   rf  ru  s    r#   __len__FunctionRegistry.__len__$	  s    4::r%   c                     XR                   ;   $ r`   rj  rk  s     r#   __contains__FunctionRegistry.__contains__'	  s    jj  r%   c                     X   " U0 UD6$ r`   rg   )r  rn  argsr  s       r#   __call__FunctionRegistry.__call__*	  s    y$)&))r%   rj  N)rS   r  r  r  __doc__rg  r]   rq  r^   rx  r{  r  r  rg   r%   r#   rd  rd  	  s*    
  !*r%   rd  c           	      "   [        U S5      (       d  U $  U R                  U5      n U $ ! [         a\    SSKJn  U" U SS 5      n[
        R                  SR                  XS   US   5      5        U R                  US   =(       d    US	S
9n  U $ f = f)aC  
Try to decode byte input as a string.

Tries initial guess (UTF-8) then if that fails it
uses charset_normalizer to try another guess before failing.

Parameters
------------
text : bytes
  Data that might be a string
initial : str
  Initial guess for text encoding.

Returns
------------
decoded : str
  Data as a string
r]  r   )detectNi  z&Data not {}! Trying {} (confidence {})rf  
confidenceignoreerrors)rK   r]  UnicodeDecodeErrorcharset_normalizerr  r  r  rI  )rv  initialcharset_normalizer_detectr  s       r#   decode_textr  .	  s    ( 4""K{{7#" K!  KJ +4;7		4;;
+VL-A	
 {{6*-8{JK!Ks   ( A"BBc                     [        U S5      (       a  U R                  SSS9R                  S5      $ [        U S5      (       a  U R                  SSS9$ [        U 5      $ )z
Force a string or other to ASCII text ignoring errors.

Parameters
-----------
text : any
  Input to be converted to ASCII string

Returns
-----------
ascii : str
  Input as an ASCII string
rB  asciir  r  r]  )rK   rB  r]  rY   )rv  s    r#   to_asciir  Z	  sY     tX{{78{4;;GDD	x	 	 {{78{44t9r%   c                    [         R                  " U [         R                  S9n [        U R                  5      S:w  d  U R                  S   S:w  a  [        S5      e[         R                  " U SS2SS/4   U SS -  R                  6 nUR                  5       S-  nUS	:  nU(       d  U$ U SS U SS -   UR                  S
5      -  R                  SS9SU-  -  nXCU4$ )ay  
Check if connected 2D points are counterclockwise.

Parameters
-----------
points : (n, 2) float
  Connected points on a plane
return_all : bool
  Return polygon area and centroid or just counter-clockwise.

Returns
----------
ccw : bool
  True if points are counter-clockwise
area : float
  Only returned if `return_centroid`
centroid : (2,) float
  Centroid of the polygon.
r   r'   r   z only defined for `(n, 2)` pointsNr)   r   g       @r   r*   r  g      @)
r+   r-  r   r.   r/   r4   subtractr   rC   r2   )r   
return_allproductareaccwcentroids         r#   is_ccwr  r	  s    ( XXfBJJ/F
6<<AaA!5;<< kkF3B3A;/&*<??@G;;=3D
*C
 vabz)W__W-EEJJPQJRd
H hr%   r   containscountsc                    U b  [        U 5      S:  a  X;  a  U $ Uc  SnOUR                  U S5      nU bT  [        U 5      S:  aE  U S-   nU R                  SS5      n[        U5      S:X  a  US:X  a   [        US   5      nUS   S-   nOOSn[        US-   SU-   [        U5      -   5       H$  nUR                  U5      nXq;  d  M  Ub  XbU '   Us  $    [        S5      e! [         a     N]f = f)a  
Deterministically generate a unique name not
contained in a dict, set or other grouping with
`__includes__` defined. Will create names of the
form "start_10" and increment accordingly.

Parameters
-----------
start : str
  Initial guess for name.
contains : dict, set, or list
  Bundle of existing names we can *not* use.
counts : None or dict
  Maps name starts encountered before to increments in
  order to speed up finding a unique name as otherwise
  it potentially has to iterate through all of contains.
  Should map to "how many times has this `start`
  been attempted, i.e. `counts[start]: int`.
  Note that this *will be mutated* in-place by this function!

Returns
---------
unique : str
  A name that is not contained in `contains`
r   z_{}_r   r'   zgeometry_{}z Unable to establish unique name!)r.   r  rsplitr   r  r  rI  r4   )r   r  r  	incrementr'  rt  rc   checks           r#   unique_namer  	  s   > SZ!^0E ~	JJua(	SZ!^EM	S!$u:?yA~  aM	!!Hu,	 "	 9q=!i-#h-"?@  # ! !uL A 7
88 ! s   1C 
C)(C))FN)Fr`   ) 
   rB  )Nr[  )r^  )#r  )r   )TFNF)T)r   )   )g-q=)r   )vr  abcr[  r   r   r  rN  shutilr   timerL  r@   r  collections.abcr   r3   r   ior   r   numpyr+   	iterationr   typedr	   r
   r   r   r   r   r   r   r   r  rS   r  ABCnowwhichfinfo
resolutionr-   r   r  rU  	_IDENTITYflagsrY   r0  r$   r;   ra  rG   rN   rV   rZ   rm   rr   ru   r   r   r   r   rT  r   AttributeErrorr  r   r   r   r   r   r   r   r   r   r   r   r  r  r+  r/  r;  rR  rY  rj  rp  r{  r  r  r  r  r  r  r  r   r  r  r  r  r   r  r&  r,  r8  r=  r2  rH  rR  r[  r_  rW  rd  r  r  r  r  rg   r%   r#   <module>r     s          
     #     
 
 
 !	gg
ii 88BJJ**S0	
FF1BJJ'	$	 'S 'T '24n)u )"9">& d  " @Td Tt Tn0GT839 31A 30&R 		##I"3-l94%P><,&&(3 * --26!$(w4 hw~~./w4 	w4
 w4 !w4 w4t.Gb$NK^ BFVr&RB*Z"eD)O4 " "J68@8 
E
JKEV !#'L L 	L
  L L^#66@*Z(VA*D"J$1N<$ $<:z!, 6DJ$:z5 54' ',&*w &*R)X0(\ "B9C=B9C(*+B9 TNB9e<  IIAB
s   J J32J3