
    ^j8                        d Z ddlmZ ddlZddlmZmZmZmZm	Z	m
Z
 ddlmZmZmZmZmZ ddZddZddZd	 Zd
 Zd ZddZddZd Zy)u8  Shank builders — the composable base of RingSpec v3.

Every builder returns {"parts": [...], "inner_r", "outer_r_eff", "drop_r",
"wants_shoulders"}: outer_r_eff is where the head sits, drop_r the lowest
radial extent (sprue attachment), wants_shoulders whether cathedral shoulders
should bridge shank->head (band only; split branches ARE the shoulders;
helix/path strands rise to the head themselves via the basket fuse).

Helix/path recipe (proven): sample the path, Spline it, sweep a circle profile
in two overlapping halves, fuse — one valid solid per strand.
    )annotationsN)AxisCirclePlaneSplineloftsweep)MIN_WALLbandinner_radiusmetal_colorsegc           
        t        t        | d               }|d   }|d   }|dv r<|j                  dt        |d   t                    }|t        |t              z   }|||dS |dk(  r<|j                  d	d
      }|j                  dd      }	||d|z  z   |	z   |d|z  z   |	z   dS |j                  dd      }	t        d |d   D              }
|d   D cg c]S  }t        t        j                  |d   |d         t        j                  dz  z
        t        j                  d      k  r|U }}t        |d d      }|rt        j                  |d   |d         n|
}|||	dz  z   |
|	dz  z   dS c c}w )zBPure sizing: no solids built. Used by head_dims/supports/validate.sizeshanktype)r   splitthickness_mmband_thickness)inner_router_r_effdrop_rhelixamplitude_mm皙?
profile_mm?gffffff?   c              3  T   K   | ]   }t        j                  |d    |d          " ywr   r   Nmathhypot.0ps      /opt/ringagent/ringcli/shanks.py	<genexpr>zshank_meta.<locals>.<genexpr>)   s#     <!tzz!A$!%<   &(pointsr      c                t    t        t        j                  | d   | d         t        j                  dz  z
        S Nr   r   absr"   atan2pir&   s    r'   <lambda>zshank_meta.<locals>.<lambda>,   +    SAaD!A$)?$''A+)M%N     N)keydefault)r   floatgetmaxr
   r/   r"   r0   r1   radiansminr#   )speccompstr   shtthickouterampprofrmaxr&   topnearesttop_rs                  r'   
shank_metarJ      s   5f./G	gB
6
A~s2.>+?'JK#eX.."5EJJG|ff^S)vvlC( #&3d:!AG+d24 	4 66,$D<r(|<<Dl L$**QqT1Q4(477Q;67$,,r:JJ  LC L#N G29DJJwqz71:.tEutax/?TAXo' 'Ls   AE,c                   g }t        dt        |       |z        }t        dt        |       |      D ]  }| |   }|dz   t        |       k  r| |dz      n| |dz
     }|dz   t        |       k  r |d   |d   z
  |d   |d   z
  |d   |d   z
  fn|d   |d   z
  |d   |d   z
  |d   |d   z
  f}|j                  t	        ||      t        |      z          t        |d      S )zOpen tube: LOFT tangent-oriented circles along the polyline. OCC's
    pipe-shell sweep proved profile-radius-fragile (hollow shells / invalid
    solids / hangs at some radii); lofting sections is fast and robust.   r   r   originz_dirF)ruled)r:   lenrangeappendr   r   r   )	ptsprof_rsectionssecsstepir&   qrA   s	            r'   	_tube_arcr[   3   s    Dq#c(h&'D 1c#h% ?Fa%#c(*CAJAE
781us3x7GQqTAaD[!A$1+qtad{3qTAaD[!A$1+qtad{3 	
E!,vf~=>? E""r5   c                    	 | j                   }t        | j                               dk(  xr% t        |      r |       n|xr | j                  dkD  S # t
        $ r Y yw xY w)NrL   gMbP?F)is_validrQ   solidscallablevolume	Exception)sivs     r'   _tube_okrd   E   sZ    ZZAHHJ1$ $(2,"$B $HHtO	% s   AA 	AAc           	     L   ddl m} ddlm}m} g }d}t        t        dt        |       |            }t        |      D ]^  \  }}	| |	   }
| ||dz   t        |      z        }|j                  t        |
|||             |j                   ||
  ||dz        z         `  ||      S )zhBoolean-proof fallback: the loop as short cylinders + sphere joints.
    Slightly faceted, always fuses.r   )fuse_all)PosSpherer   rL   g{Gz?)ringcli.partsrf   	build123drg   rh   listrR   rQ   	enumeraterS   r   )rT   rU   rf   rg   rh   partsrX   idxsjrY   r&   rZ   s               r'   _segment_tuberp   N   s     '%EDaS4()D$ 61Fa!es4y()*SAvv./ 	S!Wvftm4456 E?r5   c                   t        j                  | d   | d         dk  r| dd } t        |       }| d|dz  dz    }| |dz  d | dd z   }	 d}||fD ]h  }t        | }t	        |d   |dz        t        |      z  }t        ||      }	|	j                         r|	j                  dk  rt        d      ||	n||	z   }j t        |      r|S 	 	 t        | d|dz  d	z    |      t        | |dz  d | dd	 z   |      z   }t        |      r|S 	 t        | |      }t        |      r|S t        d
      # t        $ r Y lw xY w# t        $ r Y >w xY w)u  Closed-loop tube, three strategies in order of surface quality:
    1) OCC pipe-sweep halves (smooth; radius-fragile — hollow shells/invalid
       solids at some profile radii), 2) lofted-section halves (smooth; the
       half-fuse can go null when overlap surfaces coincide at tight
       curvature), 3) segmented cylinders+spheres (always fuses). The result
    must be ONE valid solid or we raise — never ship broken geometry.r   g&.>Nr   rM   ư>zswept hollow   uK   loop tube failed to form one valid solid — adjust profile_mm or amplitude)r"   distrQ   r   r   r   r	   r^   r`   RuntimeErrorrd   ra   r[   rp   )
rT   rU   nh1h2outhppathrE   rb   s
             r'   _sweep_loopr}   `   s    yyQR!D(#2hCA	\qAvz	B	Q!VWBQ	Br( 	0B2;D1TAX6GDdD!A88:D">22{!aC	0 C=J La1fqj)62AFGs2Aw.78C=J  V
$C}

 8 9 9    s%   
A=D* 
:D9 *	D65D69	EEc                    |d   }|j                  d|d         }|j                  d|d         }t        | d   |||      \  }}}	|g||	|	|j                  dd      d	S )
Nr   r   r   width_mm
band_widthr   	cathedralTrm   r   r   r   wants_shoulders)r9   r   )
r=   r>   r?   metalr@   rB   widthbr   outer_rs
             r'   
shank_bandr      sv    	gBFF>2&6#78EFF:r,/0EtF|UE5AAwSWW"&&d2KM Mr5   c                   |d   }t        |j                  dd            }t        |j                  dd            t        |j                  dd            t        |j                  dd            }t        |t              dz  }	t        t        | d	               }
|
z   |	z   t        j                  |      fd
}g }g }t        |      D ]  }t        j                  |z  |z  }t         ||      |	      }t        |      |_        |j                  |       dt        j                  z  dz  }t        j                  |z  |z         z  z   }t        j                   |z  |z         z  }|j                  |t        j                  |      z  ||t        j                   |      z  f        dz  |dz
  k\  rs|dkD  rnt        t#        |      dz
        D ]T  }t%        ||   ||dz      t        d|	dz        t        d|	dz              }t        |      |_        |j                  |       V ||
z  z   |	z   z   |	z   ddS )ul  Torus-knot strands. mid_r = inner_r + amp + prof/2 keeps the finger
    clear BY CONSTRUCTION. The wave smoothsteps down to `floor`x amplitude
    within flat_top_deg of the head: strands converge (and merge — centers
    end up closer than the profile diameter) under the stone so the basket
    root lands on metal and the seat cutter can't sever a lone crest.r   strandsr   twistsr   r   r   r   r   c           	     Z   g }d}t        |      D ]  }t        j                  |z  |z  }t        |t        j                  dz  z
  t        j                  z   t        j                  z  t        j                  z
        }t        d|z        }
dz
  d|z  |z  d|dz  z  z
  z  z   z  }|t        j                  |z  | z         z  z   }|t        j                  |z  | z         z  }	|j                  |t        j                  |      z  |	|t        j                  |      z  f        |S )N   r         ?rL      )	rR   r"   taur/   r1   r<   cossinrS   )phaserT   rw   rY   rA   dua_effryrD   flatfloormid_rr   s             r'   
strand_ptszshank_helix.<locals>.strand_pts   s   q 	>A1q AQ1_tww.$((:TWWDEACT"A5AI!a%!)a!q&j2H#IIJE!e); <<<A!e!344AJJDHHQKAO<=	> 
r5   r   g?rL   g      ?Fr   )intr9   r8   r:   r
   r   r"   r;   rR   r   r}   r   colorrS   r1   r   r   rQ   r   )r=   r>   r?   r   flat_top_degr   r@   r   rE   rU   r   r   rm   bottomskr   rb   t_botr   r   rY   lugrD   r   r   r   s        `                @@@@r'   shank_helixr      s8    
gB"&&A&'G!$%F
~s+
,Cc*+Dx 1$F5f./GcMF"E<<%D  EG7^ F1w&
5)62e$QDGGaC$((6E>E#9:::$((6E>E122DHHUO+QDHHUO0CDEF 	3w$*1 s7|a'( 	Agaj'!a%.#c6C<2H#v|,.C#E*CILL		
 w 53;.7ckF*uF Fr5   c                   ddl m}m}m} |d   }	t	        |	j                  d|d         t              }
t	        |	j                  d|d         d      }t        |	j                  d	d
            }t        |	j                  dd            }t	        t        |	j                  dd            t              }t        t        | d               }||
z   }||
dz  z   }|I|dz  |dz  z   |dz  kD  r8t        d| dd|dz  |dz  z
  z  ddt	        dd|dz  |dz  z
  z        d      t        j                   ||d      z   ||
|t        |
|      dz        z  } ||t        j                  dd|z  z
        }|j                  t        j                  d|z         j                  t        j                   d      }t#        |      |_        |g}||n|dz   }||nd}|dz
  }t	        |dz
  |dz         }d |d z
  ||z
  z  t	        |d!z
  |z
  d"      z  z   }t	        |d#z  d      }t'        j(                  |      }t        |dz  |dz  z   |dz        }d$D ]  }||z  t'        j*                  |d%z        z  d|t'        j,                  |d%z        z  f}d$D ]a  }t'        j.                  d&||z        } ||z  | z  ||z  | z  |f}!t1        ||!|
dz  |dz        }"t#        |      |"_        |j3                  |"       c  ||||d'd(S ))znSolid bottom arc + four rod branches (two per side) that ARE the
    shoulders, landing under the head basket.r   )rg   RectangleRoundedrevolver   r   r   r   r   g      ?fork_deg7   gap_mmg?	branch_mmg333333?r   r   g?zspec.shank.split.gap_mm = u>    — branches would land wide of the head basket (limit here ~.1fz). Fix: "gap_mm": r   g333333?g{Gz?ih  Z   g?g       @g      ?g333333?333333?g?gQ?rs   gq=
ףp?)rL   rr   gHzG?r   Fr   )rj   rg   r   r   r:   r9   r
   r8   r   
ValueErrorr   XZr<   r   ZrotateXr   r   r"   r;   r   r   r#   r   rS   )#r=   r>   r?   r   girdle_zrim_rrg   r   r   r@   rB   r   forkgapbranchr   r   midrE   arcrm   gzrrgbotz_landr_coner_landfryspreadsxendsynormtiprods#                                      r'   shank_splitr      s;    98	gB~r*:';<hGEz2l#34c:E
B'(D
x%
&Crvvk3/0(;F5f./GoG
EAI
CS1Wvz1ECK?( .,,-vz1I,J3+O P!#qEDL6A:,E'FGLNO 	O
 88c#qk!$4UE585F5M%O OD
$a$h
/C
**TVVR$Y
'
.
.tvvr
:CE"CIEE)w~B#B T>DdD4K(FR$Y6D=1CT	D8H$4OOOF$%F	d	B#'FQJ&5G 
 Cx$((29--q#d8K2KL 	B::c7V#34D;%rG|d':FCCc3fqj9C#E*CILL	
 ww%9 9r5   c                   |d   }t        t        |j                  dd            t              }|dz  }t	        t        | d               }|d   D cg c]  }t        t        t        |             }	}d}
t        t        |	            D ]  }|	|   \  }}}|	|dz   t        |	      z     \  }}}t        j                  ||      t        j                  ||      }}||z
  t        j                  z   t        j                  z  t        j                  z
  }|
|z  }
 t        t        |
      t        j                  z
        t        j                  d	      kD  r#t        d
t        j                   |
      dd      t#        |	      D ]  \  }\  }}}t        j$                  ||      }|||z   k  r't        d| d| d| d| d|dd||z   dd|dd      ||dz   kD  st        |      dkD  sft        d| d| d| d| d|dz   dd       t'        |	ddi}g }t        d      D ]  }||dz  z  }|j)                  |j*                  |j,                  |j.                  f       t        j$                  |j*                  |j.                        }|||z   dz
  k  sst        d|dd||z   dd       t1        ||      }|j3                         r|j4                  d k  rt7        d!      t9        |      |_        |D cg c]d  }t        t        j                  |d   |d"         t        j                  dz  z
        t        j                  d#      k  rt        |d         d$k  r|f }}|st        d%      t=        |d& '      }t        j$                  |d"   |d         }t        d( |D              }|g|||z   ||z   d)d*S c c}w c c}w )+z@Escape hatch: model-supplied closed loop, CLI-validated + swept.r   r   r   r   r   r*   g        rL      u*   spec.shank.path.points — the loop winds z.0fz deg around the finger axis, must be ~360 (a single closed ring, no open C-shapes or figure-8s). Fix: order the points once around the fingerzspec.shank.path.points[z] = [z, u   ] — only r   z$mm from the finger axis; must be >= z (finger radius z# + profile/2). Fix: move it outwardg      @u+   ] — outside the ring envelope (radial <=    z, |y| <= 6). Fix: pull it inperiodicT   g?u:   spec.shank.path — the curve between your points dips to z"mm from the finger axis (needs >= z"). Fix: move nearby points outwardr   u   spec.shank.path — the swept loop did not produce a solid (likely a kink or self-intersection). Fix: smooth the points, increase spacing, or reduce profile_mmr      g      @uv   spec.shank.path — no part of the loop passes near the top (+Z) where the head sits. Fix: route the loop over the topc                t    t        t        j                  | d   | d         t        j                  dz  z
        S r-   r.   r2   s    r'   r3   zshank_path.<locals>.<lambda>@  r4   r5   )r6   c              3  T   K   | ]   }t        j                  |d    |d          " ywr    r!   r$   s     r'   r(   zshank_path.<locals>.<genexpr>B  s#     5!tzz!A$!%5r)   Fr   )r:   r8   r9   r
   r   tuplemaprR   rQ   r"   r0   r1   r   r/   r;   r   degreesrl   r#   r   rS   r   Yr   r}   r^   r`   rv   r   r   r<   )r=   r>   r?   r   r@   rE   rU   r   r&   rT   totalrY   x0_z0x1z1a0a1r   xr   zr   masterdenserb   rG   rH   rI   rF   s                                  r'   
shank_pathr      s   	gBuRVVL#./:DAXF5f./G)+H
6A5UA
6C
6 E3s8_ F	ArQ#c(*+	ArB#TZZB%7B"Wtww$((*TWW4
 3u: !DLL$448e9LS8Q R   ! 	!
 "# #9Aq!JJq!w)!E!BqcA3kS'=V#C((8 F345 5
 w}A)!E!BqcA3 ?007!C/@ A""# ##  S(4(F E3Z Pa#gacc133_%JJqssACC w$&&LS' &s++MOP PP 	E6"A88:C56 	6 % AG   $**QqT1Q4(477Q;67$,,r:JJ1Q4y3   C   IJ 	J
 #NOGJJwqz71:.E5u55DSWUV^Vm? ?E 7l s    N85A)N=)r=   dictr>   r   r?   r   returnr   )(   )r   bool)g      I@r   )NN)__doc__
__future__r   r"   rj   r   r   r   r   r   r	   ri   r
   r   r   r   r   rJ   r[   rd   rp   r}   r   r   r   r    r5   r'   <module>r      sQ   
 #  > > J J':#$$$9NM3Fl29jI?r5   