
    hR                        S SK Jr  S SKJrJrJ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Jr  S SKJr  / SQrS	SS
S.           SS jjr S       SS jjrSS jrSS jrg)    )annotations)IteratorIterableOptionalN)defaultdict)reduce)AnyVecVec3spherical_envelope)RTree)dbscank_meansaverage_cluster_radiusaverage_intra_cluster_distance      )
min_pointsrtreemax_node_sizec               R   US:  a  [        S5      eUc  [        X5      n/ n[        U 5      n[        U5      (       a  UR	                  5       nU1nU1n	UR                  U	5        [        U5      (       a  UR	                  5       n
[        UR                  X5      5      n[        U5      U:  a  MK  U	R                  U
5        UR                  U
5        XR                  U5      -  n[        U5      (       a  M  [        U5      (       a  M  U V	s/ s H  n	[        U	5      PM     sn	$ s  sn	f )a  DBSCAN clustering.

https://en.wikipedia.org/wiki/DBSCAN

Args:
    points: list of points to cluster
    radius: radius of the dense regions
    min_points: minimum number of points that needs to be within the
        `radius` for a point to be a core point (must be >= 2)
    rtree: optional :class:`~ezdxf.math.rtree.RTree`
    max_node_size: max node size for internally created RTree

Returns:
    list of clusters, each cluster is a list of points

   zmin_points must be >= 2)
ValueErrorr   setlenpopappendpoints_in_sphereadddiscardintersectionlist)pointsradiusr   r   r   clusters	point_setpointtodocluster	chk_point	neighborss               G/var/www/html/env/lib/python3.13/site-packages/ezdxf/math/clustering.pyr   r      s    0 A~233}f,"$HFI
i..w' $ii
IE229EFI9~
*KK	"i(**955D $ii i.. *22gDM222s   D$c                F  ^ ^^ SU 4S jjnSUUU 4S jjnS nSTs=:  a  [        T 5      :  d  O  [        S5      eU" [        R                  " T T5      5      m[	        U5       H"  nU" U" 5       5      nU" TU5      (       a    OUmM$     [        TR                  5       5      $ )zK-means clustering.

https://en.wikipedia.org/wiki/K-means_clustering

Args:
    points: list of points to cluster
    k: number of clusters
    max_iter: max iterations

Returns:
    list of clusters, each cluster is a list of points

c                   > [        [        5      n[        U 5      nT H)  nUR                  U5      u  pEX   R	                  U5        M+     U$ N)r   r!   r   nearest_neighborr   )	centroidsnew_clusterstreer&   nn_r"   s         r+   classifyk_means.<locals>.classifyX   sL    3>t3DYE))%0EB##E*      c               3     >#    TR                  5        H'  n [        R                  " U 5      [        U 5      -  v   M)     [        T5      T:  a,  [        R
                  " TT[        T5      -
  5       S h  vN   g g  N7fr.   )valuesr
   sumr   randomsample)cluster_pointsr$   kr"   s    r+   recenterk_means.<locals>.recenter`   sa     &oo/N((>*S-@@@ 0x=1}}VQX->??? ?s   A1A=4A;5A=c                    S n[        [        X R                  5       5      5      n[        [        X!R                  5       5      5      nX4:H  $ )Nc                r    U R                  5         [        [        R                  [	        [
        U 5      5      $ r.   )sortr   operatorxormaphash)lsts    r+   	hash_list7k_means.<locals>.is_equal_clustering.<locals>.hash_listg   s"    HHJ(,,D#77r7   )sortedrF   r9   )old_clustersr1   rI   h1h2s        r+   is_equal_clustering$k_means.<locals>.is_equal_clusteringf   s@    	8 C	#6#6#89:C	#6#6#89:xr7      z7invalid argument k: must be in range [2, len(points)-1])r0   zIterable[AnyVec])returnzIterator[AnyVec])r   r   r;   r<   ranger!   r9   )	r"   r>   max_iterr5   r?   rO   r4   r1   r$   s	   ``      @r+   r   r   G   s    "@ @ CKE
 	
 ,4FMM&!4L+MH8_
+x66	 
 !""r7   c                    [         R                  " U  VVVs/ s H5  n[        R                  " US5        H  u  p#UR	                  U5      PM     M7     snnn5      $ s  snnnf )z:Returns the average point-to-point intra cluster distance.r   )
statisticsmean	itertoolscombinationsdistance)r$   r(   pqs       r+   r   r   |   s[     ?? $	
##00!< JJqM< #	
 	
s   <Ac                r    [         R                  " U  Vs/ s H  n[        U5      S   PM     sn5      $ s  snf )z#Returns the average cluster radius.rQ   )rV   rW   r   )r$   r(   s     r+   r   r      s5     ??7?@xG	G	$Q	'x@ @s   4)r"   list[AnyVec]r#   floatr   intr   zOptional[RTree]r   r`   rR   list[list[AnyVec]])
   )r"   r^   r>   r`   rT   r`   rR   ra   )r$   ra   rR   r_   )
__future__r   typingr   r   r   r;   rV   rX   rD   collectionsr   	functoolsr   
ezdxf.mathr	   r
   r   ezdxf.math.rtreer   __all__r   r   r   r    r7   r+   <module>rk      s    # / /     #  7 7 " !-3-3 -3 	-3
 -3 -3 -3b 352#2# 2#,/2#2#j	r7   