"""Rear disc-wheel hub/shaft/drum/backplate relationships. Fitted identities unknown."""
from pathlib import Path
import json,math
import cadquery as cq
OUT=Path(__file__).parent;B=json.loads((OUT.parent/'body-data.json').read_text());R=B['rear_hub_reference'];TIP=B['differential_support_reference']['halfshaft_tip_body_x_abs_mm'];S=2032/2.4;W=B['published_reference_dimensions_mm']['rear_track_steel_wheels']/2
X=cq.Vector(1,0,0);Y=cq.Vector(0,1,0);Z=cq.Vector(0,0,1);local=[];parts=[];nodes={};contacts={}
def cyl(r,l,p,a=X):return cq.Solid.makeCylinder(r,l,p,a)
def ring(ro,ri,l,p,a=X):return cyl(ro,l,p,a).cut(cyl(ri,l+2,p-a,a))
def box(w,h,d,p):return cq.Workplane('XY').box(w,h,d).translate(p.toTuple()).val()
def comp(xs):return cq.Compound.makeCompound(xs)
def put(n,s,c=(.42,.45,.41)):
 assert s.isValid() and s.Volume()>0,n
 local.append((n,s,c));return s
def torus(major,minor,p):return cq.Solid.makeTorus(major,minor,p,X)
def bore(s,axes,r,x,length):
 for q in axes:s=s.cut(cyl(r,length,cq.Vector(x,q.y,q.z)))
 return s
wheel_axes=[cq.Vector(0,R['catalog_pcd_mm']/2*math.sin(math.pi/4+i*math.pi/2),R['catalog_pcd_mm']/2*math.cos(math.pi/4+i*math.pi/2)) for i in range(4)]
back_axes=[cq.Vector(-56,48*math.sin(math.pi/4+i*math.pi/2),48*math.cos(math.pi/4+i*math.pi/2)) for i in range(4)]
shaft_screw=cq.Vector(0,0,45);drum_screws=[cq.Vector(0,45,0),cq.Vector(0,-45,0)]
# Bridge the old 1 mm journal/flange drawing gap; mounting flange grows to R58.
journal=ring(17.5,13,43,cq.Vector(-50,0,0)).fuse(ring(28,13,6,cq.Vector(-56,0,0))).fuse(ring(58,17.5,2,cq.Vector(-58,0,0)))
journal=bore(journal,back_axes,4.5,-61,10);journal=put('connected_integral_role_axle_end_journal_and_mount_flange',journal.clean())
# One connected stepped backplate; separate cylinder, bleed and handbrake apertures.
plate=ring(94,50,2,cq.Vector(-22,0,0)).fuse(ring(50,48,34,cq.Vector(-55,0,0))).fuse(ring(58,17.5,2,cq.Vector(-56,0,0))).fuse(ring(96,93,5,cq.Vector(-23.5,0,0)))
plate=plate.cut(box(10,20,25,cq.Vector(-21,70,-23))).cut(cyl(7.5,10,cq.Vector(-26,68,0))).cut(cyl(3.5,10,cq.Vector(-26,76,12))).cut(cyl(5,10,cq.Vector(-26,-74,0)))
plate=bore(plate,back_axes,4.5,-61,10);plate=put('connected_dished_backplate_with_shared_mount_and_actuator_apertures',plate.clean())
fix=[]
for q in back_axes:
 shank=cyl(4,20,cq.Vector(-59.2,q.y,q.z));head=cq.Workplane(cq.Plane(origin=(-64.2,q.y,q.z),normal=X)).polygon(6,14).extrude(5).val();washer=ring(8,4.2,1.2,cq.Vector(-59.2,q.y,q.z));nut=cq.Workplane(cq.Plane(origin=(-54,q.y,q.z),normal=X)).polygon(6,14).extrude(6).val().cut(cyl(4.2,8,cq.Vector(-55,q.y,q.z)));fix.extend([shank,head,washer,nut])
put('four_backplate_to_axle_screw_retainer_envelopes',comp(fix),(.55,.56,.48))
hub=cyl(42,39,cq.Vector(-47,0,0)).fuse(cyl(40,10,cq.Vector(-8,0,0))).fuse(cyl(65,6.5,cq.Vector(2,0,0)))
hub=hub.cut(cyl(17.5,58,cq.Vector(-48,0,0))).cut(cyl(25,14.5,cq.Vector(-48,0,0))).cut(cyl(36,25.5,cq.Vector(-33.5,0,0))).cut(cyl(29,10,cq.Vector(-8,0,0)))
hub=bore(hub,wheel_axes,4.8,0,12);hub=bore(hub,[shaft_screw],2.7,0,12);hub=bore(hub,drum_screws,3.5,0,12)
oring=torus(37,1.5,cq.Vector(8.5,0,0));hub=hub.cut(oring);hub=put('connected_stepped_rear_hub_flange_and_estimated_face_groove',hub.clean())
bb=R['bearing_comparison_mm'];bearing=put('rear_bearing_boundary_comparison',ring(bb['outside']/2,bb['bore']/2,bb['width'],cq.Vector(-25-bb['width']/2,0,0)),(.56,.57,.50))
nut=cq.Workplane(cq.Plane(origin=(-15,0,0),normal=X)).polygon(6,54).extrude(8).val().cut(cyl(17.5,10,cq.Vector(-16,0,0)));put('unthreaded_handed_role_hub_nut_envelope',nut,(.55,.56,.48))
lock=ring(29,17.5,1.5,cq.Vector(-16.5,0,0)).fuse(box(1.5,10,8,cq.Vector(-15.75,30,0)));put('flat_bearing_retaining_lock_washer_tab_comparison',lock,(.55,.56,.48))
shaft=cyl(11.5,W+9-TIP,cq.Vector(TIP-W,0,0)).fuse(cyl(60,6,cq.Vector(9,0,0)))
shaft=bore(shaft,wheel_axes,5.2,8,9);shaft=bore(shaft,drum_screws,3.5,8,9);shaft=bore(shaft,[shaft_screw],2.7,8,9)
shaft=shaft.cut(cq.Solid.makeCone(2.7,5.1,3,cq.Vector(12,shaft_screw.y,shaft_screw.z),X))
put('halfshaft_and_bored_integral_flange_without_splines',shaft.clean())
put('unloaded_inboard_hub_lip_seal_envelope',ring(25,17.5,7,cq.Vector(-43.5,0,0)),(.13,.16,.13));put('uncompressed_halfshaft_face_o_ring_comparison',oring,(.13,.16,.13))
gasket=ring(61,29,.5,cq.Vector(8.5,0,0));gasket=bore(gasket,wheel_axes,5.2,7,4);gasket=bore(gasket,[shaft_screw],2.7,7,4);gasket=bore(gasket,drum_screws,3.5,7,4);put('uncompressed_halfshaft_face_gasket_comparison',gasket,(.75,.71,.55))
# Preserve nominal drum bore and existing brake radial/axial stations.
drum=cyl(100,37.5,cq.Vector(-17.5,0,0)).cut(cyl(B['brake_reference']['rear_drum_nominal_bore_mm']/2,33.5,cq.Vector(-18.5,0,0))).cut(cyl(32,8,cq.Vector(14,0,0)))
drum=bore(drum,wheel_axes,5.2,14,9);drum=bore(drum,drum_screws,3.5,14,9);drum=bore(drum,[shaft_screw],7,14,9)
for q in drum_screws:drum=drum.cut(cq.Solid.makeCone(3.5,6.5,3,cq.Vector(17,q.y,q.z),X))
put('hollow_seven_inch_drum_and_shared_fixing_access_openings',drum.clean(),(.40,.43,.39))
ss=cyl(2.5,11,cq.Vector(1,shaft_screw.y,shaft_screw.z)).fuse(cq.Solid.makeCone(2.5,5,3,cq.Vector(12,shaft_screw.y,shaft_screw.z),X))
put('separate_halfshaft_retaining_screw_envelope',ss,(.55,.56,.48))
ds=[]
for q in drum_screws:ds.append(cyl(3,10,cq.Vector(7,q.y,q.z)).fuse(cq.Solid.makeCone(3,6,3,cq.Vector(17,q.y,q.z),X)))
put('two_separate_drum_retaining_screw_envelopes',comp(ds),(.55,.56,.48))
studs=[];nuts=[]
for q in wheel_axes:
 studs.append(cyl(4.5,33,cq.Vector(2,q.y,q.z)).fuse(cyl(7,3,cq.Vector(-1,q.y,q.z))))
 n=cq.Workplane(cq.Plane(origin=(25,q.y,q.z),normal=X)).polygon(6,16).extrude(10).val().cut(cyl(4.7,12,cq.Vector(24,q.y,q.z)));nuts.append(n)
put('four_rear_wheel_stud_head_shank_envelopes',comp(studs),(.55,.56,.48));put('four_separate_rear_wheel_nut_envelopes',comp(nuts),(.55,.56,.48))
for q in back_axes:
 for s in [journal,plate]:assert s.intersect(cyl(4.3,10,cq.Vector(-61,q.y,q.z))).Volume()<1e-5
for q in wheel_axes:
 for s in [hub,shaft,drum,gasket]:assert s.intersect(cyl(4.6,26,cq.Vector(-1,q.y,q.z))).Volume()<1e-5
for s in [hub,bearing,journal]:assert s.intersect(cyl(11.2,100,cq.Vector(-70,0,0))).Volume()<1e-5
assert hub.intersect(bearing).Volume()<1e-5
assert comp(fix).intersect(hub).Volume()<1e-5
assert drum.intersect(comp(ds)).Volume()<1e-5
axle=cq.Assembly.importStep(str(OUT/'rear-axle-reference.step'));ob=axle.objects['rear_axle_housing_reference'].obj;axlesh=ob.val() if isinstance(ob,cq.Workplane) else ob
for sign,label in [(1,'left'),(-1,'right')]:
 def tr(s):return (s if sign==1 else s.mirror('YZ')).translate((sign*W,.39*S,-1.2*S))
 def point(q):return [sign*(W+q.x),.39*S+q.y,-1.2*S+q.z]
 for n,s,c in local:parts.append(('rear_'+label+'_'+n,tr(s),c))
 # Axle export uses its own centered frame; translate once for limited source sampling.
 neighbor=axlesh.translate((0,.39*S,-1.2*S))
 for n,s,c in local:
  a,b=tr(s).BoundingBox(),neighbor.BoundingBox()
  if all(getattr(a,k+'min')<=getattr(b,k+'max') and getattr(b,k+'min')<=getattr(a,k+'max') for k in ['x','y','z']):
   vol=round(tr(s).intersect(neighbor).Volume(),4)
   if vol>1e-4:contacts[label+'_'+n+'__axle_housing']=vol
 for n,a,b in [('uncompressed_ring_gasket',oring,gasket),('uncompressed_ring_shaft',oring,shaft),('plate_hub',plate,hub),('journal_hub',journal,hub)]:
  v=round(a.intersect(b).Volume(),4)
  if v>1e-4:contacts[label+'_'+n]=v
 nodes[label]=dict(wheel_axes_body_mm=[point(q) for q in wheel_axes],backplate_axes_body_mm=[point(q) for q in back_axes],shaft_screw_axis_body_mm=point(shaft_screw),drum_screw_axes_body_mm=[point(q) for q in drum_screws],axis_center_body_mm=point(cq.Vector()),axis_outward=[sign,0,0],bearing_center_local_x_mm=-25,bearing_seat_construction_start_mm=-33.5,axle_flange_outer_construction_radius_mm=58,backplate_pattern_construction_pcd_mm=96,halfshaft_inboard_body_x_mm=sign*TIP)
limits='No new owner dimensions or fitted axle/hub/shaft/bearing/seal/drum identity is applied. Factory pp146-148 support disc-wheel hub/shaft, bearing/seal/ring/gasket, right/left-hand nut and four backplate screws plus two drum screws per side; wire-wheel/late axle/differential ratio branches stay distinct. Supplier shaft-retaining screw role and repair-seal publication support architecture only, not a fitted repair or withdrawal procedure. NSK 6207 35x72x17 mm is an independent boundary comparison, not approved GHB130/fitted identity, internal clearance or loaded seating. Original nominal track, wheel mounting +20, shaft flange outer +15, gasket 8.5..9, hub outer +8.5 and drum bore177.8 remain source datums/comparisons. New journal/backplate flange R58 and PCD96/4.5 mm-radius mount bores are estimated, not factory/fitted dimensions. The original axle source tubes shorten 3 mm to the retained flange inboard plane (-55 to -58 mm relative to the nominal wheel center), reconciling source division/plate contacts without claiming a fitted axle length. Journal shoulder extends 1 mm to connect the old drawing gap; bearing-pocket start moves -36 to -33.5 mm to meet the declared bearing boundary, not measured seating/preload. Hub/groove sections, nut/washer/tab geometry, all bores, shaft/drum screw maps/countersinks, wheel studs/nuts and thread/knurl/locking details are estimates. Handed thread assignment to this fitted car is unverified and threads are not modeled. Halfshaft tip moves from +/-90 to +/-40 mm in the independent differential-support update, extending each shaft inward50 mm to the revised toothless side-gear boundary. Only those two shaft meshes change; thirty other hub meshes remain identical. This is an unmeasured assembly construction, not a fitted shaft length or verified spline engagement. Face groove follows the uncompressed ring envelope; ring/gasket/shaft overlap remains, without seal compression/material/loaded-fit inference. Drum access aperture is a construction relationship, not verified tool clearance or withdrawal path. Independent rear-brake source now replaces coarse shoes/cylinder/adjuster/springs. The backplates retain a handbrake slot centered Y70/Z-23 (20x25 mm), cylinder R7.5 aperture at Y68/Z0 and bleed R3.5 at Y76/Z12 replace the old combined slot. These are estimated interface stations and sizes, not factory drilling dimensions. External handbrake source and the earlier declared axle tube-end cutback are retained. Positive source contacts are reconstruction discrepancies or overlapping integral-role source divisions, not physical interference/damage. No axle oil route, leak-free sealing, loaded retention, wheel offset, brake setting, pressure circuit, thread loosening/torque, end float, safe release/support procedure or verified replacement/removal path. 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','https://www.spridgets.de/downloads/Parts-1977.pdf#page=147','https://www.spridgets.de/downloads/Parts-1977.pdf#page=148'],manufacturer_source=R['bearing_dimension_source'],supplier_sources=['https://www.mgocspares.co.uk/DocumentHandler/181670/z150-z151-banjo-axle-hub-repa-pdf','https://www.ahspares.co.uk/sprite/rear-axle/default.aspx'],owner_dimensions_applied=False,basis='Factory rear disc-wheel axle/hub/shaft/drum/backplate and independent bearing-size comparison. Existing owner evidence gives context rather than calibrated wheel-end survey; all new profiles/maps/sections remain estimated.',registration_status='unregistered_rear_hub_axle_end_backplate_and_drum_comparison',nodes=nodes,sampled_source_contacts_mm3=contacts,limits=limits)
a=cq.Assembly(name='unregistered_rear_axle_end_hub_shaft_and_brake_mounting_relationships');meshes=[]
for n,s,c in parts:
 a.add(s,name=n,color=cq.Color(*c));vs,ts=s.tessellate(.5,.16);meshes.append(dict(name=n,color=c,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/'rear-hub-reference.step'));meta['parts']=meshes;(OUT/'rear-hub-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n');s=cq.importers.importStep(str(OUT/'rear-hub-reference.step')).val();assert s.isValid();print(json.dumps(dict(meshes=len(parts),solids=len(s.Solids()),step_valid=True,shared_backplate_wheel_bores=True,open_shaft_journal_bearing_passage=True,reference_bearing_seat_nonintersection=True,source_contacts=contacts)))
