"""Pinion support/retention architecture. No fitted gearset or bearing setting."""
from pathlib import Path
import json
import cadquery as cq
OUT=Path(__file__).parent;B=json.loads((OUT.parent/'body-data.json').read_text());R=B['rear_pinion_reference'];S=2032/2.4
Z=cq.Vector(0,0,1);O=cq.Vector(0,.39*S,-1.2*S);parts=[];contacts={};nodes={}
def cyl(r,l,z):return cq.Solid.makeCylinder(r,l,cq.Vector(0,0,z),Z)
def ring(ro,ri,l,z):return cyl(ro,l,z).cut(cyl(ri,l+2,z-1))
def put(name,s,color=(.50,.53,.47)):
 assert s.isValid() and s.Volume()>0,name
 parts.append((name,s.translate(O),color));return s
def bearing(ro,ri,low,high,reverse=False):
 # Two race-boundary solids and a deliberately empty roller region. All
 # internal sections are estimates; no roller contact or preload is modeled.
 length=high-low;a,b=(ro-13,ro-17) if reverse else (ro-17,ro-13)
 inner=cq.Solid.makeCone(a,b,length,cq.Vector(0,0,low),Z).cut(cyl(ri,length+2,low-1))
 a,b=(ro-6,ro-10) if reverse else (ro-10,ro-6)
 outer=cyl(ro,length,low).cut(cq.Solid.makeCone(a,b,length+2,cq.Vector(0,0,low-1),Z))
 assert inner.intersect(outer).Volume()<1e-5
 return cq.Compound.makeCompound([inner,outer])
front=.335*S;washer=R['display_depth_washer_thickness_mm'];head0,head1=R['estimated_head_z_mm'];shaft_end=front+11.5
pinion=cq.Solid.makeCone(35,22,head1-head0,cq.Vector(0,0,head0),Z).fuse(cyl(R['estimated_shaft_radius_mm'],shaft_end-head0,head0))
assert len(pinion.Solids())==1
put('pinion_toothless_head_and_shaft_boundary',pinion)
ib=R['estimated_inner_bearing_z_mm'];ob=R['estimated_outer_bearing_z_mm']
inner=put('pinion_inner_two_race_boundary_without_rollers',bearing(40,12,*ib))
outer=put('pinion_outer_two_race_boundary_without_rollers',bearing(32,12,*ob,reverse=True))
spacer=put('pinion_uncompressed_spacer_envelope',ring(17,12.2,ob[0]-ib[1],ib[1]))
depth=put('pinion_unselected_depth_washer_display_midpoint',ring(21,12.2,washer,head1),(.65,.65,.54))
dust=ring(46,20.2,1.5,258).fuse(ring(46,44.5,4,254))
put('pinion_dust_cover_open_skirt_comparison',dust)
lock=put('pinion_unloaded_retaining_lock_washer',ring(22,12.2,1.5,front),(.57,.59,.51))
nut=cq.Workplane('XY',origin=(0,0,front+1.5)).polygon(6,42).extrude(8).val().cut(cyl(12.2,10,front+.5))
put('pinion_unthreaded_retaining_nut_envelope',nut,(.57,.59,.51))
for shape in [inner,outer,spacer,depth,lock,nut]:assert shape.intersect(cyl(11.9,180,120)).Volume()<1e-5
assert inner.intersect(pinion).Volume()<1e-5 and outer.intersect(pinion).Volume()<1e-5
neighbors=[]
for filename,offset in [('rear-axle-reference.step',O),('propshaft-reference.step',cq.Vector())]:
 a=cq.Assembly.importStep(str(OUT/filename))
 for name,obj in a.objects.items():
  if obj.obj is None:continue
  s=obj.obj.val() if isinstance(obj.obj,cq.Workplane) else obj.obj
  if isinstance(s,cq.Shape):neighbors.append((name,s.translate(offset)))
carrier=next(s for n,s in neighbors if n=='differential_carrier_reference')
for s in [inner,outer,spacer,depth]:assert s.translate(O).intersect(carrier).Volume()<1e-5
rear_yoke=next(s for n,s in neighbors if n=='prop_rear_flange_yoke_with_shared_fixing_and_cross_bores')
for s in [nut,lock]:assert s.translate(O).intersect(rear_yoke).Volume()<1e-5
for name,a,_ in parts:
 for other,b in neighbors:
  ba,bb=a.BoundingBox(),b.BoundingBox()
  if not all(getattr(ba,k+'min')<=getattr(bb,k+'max') and getattr(bb,k+'min')<=getattr(ba,k+'max') for k in ['x','y','z']):continue
  v=round(a.intersect(b).Volume(),4)
  if v>1e-4:contacts[name+'__'+other]=v
def point(z):return list((O+cq.Vector(0,0,z)).toTuple())
nodes=dict(axis_body=[0,0,1],shaft_radius_mm=12,shaft_bounds_body_mm=[point(head0),point(shaft_end)],inner_bearing_bounds_body_mm=[point(z) for z in ib],outer_bearing_bounds_body_mm=[point(z) for z in ob],depth_washer_bounds_body_mm=[point(head1),point(head1+washer)],flange_mating_center_body_mm=point(front),nut_bounds_body_mm=[point(front+1.5),point(front+9.5)],carrier_boss_radius_mm=45,rear_propshaft_recess_radius_mm=23)
limits='No owner dimensions, fitted pinion/ratio/bearing/seal identity or installed setting applied. Factory pp146/147 distinguish crown-wheel/pinion matched-set roles, selected-depth washer, inner ATA7166 and outer BTB440 bearings, BTA532 spacer, 88G320 seal, 1G7439 dust cover and flange/nut/spring-washer roles. Earlier AKD1775 H.6 describes shimmed-spacer/depth/preload measurement and is not transferred to the 1500/BTA532 configuration. Factory depth-washer range2.95..3.30 mm is retained separately; displayed3.125 mm is only its midpoint, not a factory offered size, measured/fitted washer or selected repair setting. All head/shaft, bearing race sections and stations, spacer/free length, pocket/web/boss, dust-cover/lock-washer/nut and retention geometry are estimates. Bearing rollers, gear teeth, hypoid offset, spline/thread engagement, loaded contact, crush/preload and lubrication paths are absent. The original long hollow flange stem shortens from240 to25.6333 mm at the retained Z250 seal plane; the new shaft/bearing stack explains architecture rather than reclassifying the old stem as a fitted pinion. Carrier receives an estimated R45 internal boss and annular web with matching bearing/washer pockets; six other axle meshes remain identical. Only the rear propshaft flange/root gets an R23 recess for this estimated nut/washer boundary; eighteen other propshaft meshes remain identical. Original flange mating plane, 70 mm-PCD/45-degree bolt maps, nominal track, axle ends and wheel-end sources are unchanged. No measured casting, factory drilling/machining template, operating clearance or nut-tool access is established. Cage/crown wheel/side-gear internals and actual tooth engagement remain unresolved; existing toothless side-gear sockets and halfshaft stations are preserved. Source contacts are reconstruction discrepancies, not fitted-car interference findings. No actual gearset pairing/ratio, selected shim, bearing preload, flange torque, safe support/release or verified overhaul/removal path. Inspection separation is illustrative.'
meta=dict(units='mm',source_frame='Canonical body mm +X vehicle-left +Y up +Z front.',factory_sources=['https://www.spridgets.de/downloads/Parts-1977.pdf#page=146',R['factory_source']],earlier_workshop_source=R['earlier_workshop_source'],earlier_workshop_limit=R['earlier_workshop_limit'],owner_dimensions_applied=False,basis='Factory pinion/bearing/spacer/depth-washer/seal/dust-cover/retention roles and earlier factory discussion of measured bearing settings. Existing owner evidence is not a calibrated hidden axle-internal survey; all new geometry and installed relationships remain construction estimates.',registration_status=R['registration_status'],factory_depth_washer_range_mm=R['factory_depth_washer_range_mm'],display_depth_washer_thickness_mm=washer,display_washer_basis=R['display_washer_basis'],nodes=nodes,sampled_source_contacts_mm3=contacts,limits=limits)
a=cq.Assembly(name='unregistered_pinion_support_and_retention_comparison');meshes=[]
for name,s,color in parts:
 a.add(s,name=name,color=cq.Color(*color));vs,ts=s.tessellate(.5,.16);meshes.append(dict(name=name,color=color,vertices=[round(v,4) for p in vs for v in p.toTuple()],triangles=[i for t in ts for i in t]))
a.export(str(OUT/'pinion-support-reference.step'));meta['parts']=meshes;(OUT/'pinion-support-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n');restored=cq.importers.importStep(str(OUT/'pinion-support-reference.step')).val();assert restored.isValid();print(json.dumps(dict(meshes=len(parts),solids=len(restored.Solids()),step_valid=True,open_reference_bearing_passages=True,source_pockets_and_recess_nonintersection=True,source_contacts=contacts)))
