"""Unregistered rear brake architecture; no loaded mechanism or service path."""
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());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=(.44,.46,.42)):
 assert s.isValid() and s.Volume()>0,n
 local.append((n,s,c));return s
def arc(ri,ro,a,b,width,u):
 def p(r,t):return (r*math.sin(math.radians(t)),r*math.cos(math.radians(t)))
 return cq.Workplane('YZ',origin=(u-width/2,0,0)).moveTo(*p(ro,a)).threePointArc(p(ro,(a+b)/2),p(ro,b)).lineTo(*p(ri,b)).threePointArc(p(ri,(a+b)/2),p(ri,a)).close().extrude(width).val()
shoe_holes=[]
for label,a,b,zsign in [('front',-65,65,1),('rear',115,245,-1)]:
 steel=arc(70,83,a,b,3,-2).fuse(arc(83,85,a,b,24,-2))
 for y in [-60,60]:
  q=cq.Vector(-2,y,zsign*49);shoe_holes.append(q);steel=steel.cut(cyl(2,10,q-X*5))
 # Slots in piston ends meet this thin web. No fitted shoe orientation selected.
 put('shoe_'+label+'_connected_web_rim_and_spring_eyes',steel.clean())
 put('shoe_'+label+'_nominal_contour_lining',arc(85,88.9,a,b,24,-2),(.17,.20,.16))
# Barrel shifts 6 mm outward from the old cue to the retained shoe-web plane.
# Inlet tip stays at u=-35, Y=68, Z=0, preserving the existing network boundary.
c=cq.Vector(-2,68,0);bore=17.4625/2
casting=cyl(12,50,c-Z*25,Z).cut(cyl(bore,54,c-Z*27,Z))
neck=ring(7,2.5,22,cq.Vector(-35,68,0));neck=neck.cut(ring(8,6.2,1.2,cq.Vector(-24.7,68,0)))
casting=casting.fuse(neck).fuse(ring(3,1.3,22,cq.Vector(-30,76,12)))
casting=casting.cut(cyl(bore,54,c-Z*27,Z))
assert len(casting.Solids())==1
assert casting.intersect(cyl(bore-.1,54,c-Z*27,Z)).Volume()<1e-5
assert casting.intersect(cyl(2.4,15,cq.Vector(-37,68,0))).Volume()<1e-5
put('wheel_cylinder_hollow_barrel_neck_and_bleed_boss',casting.clean())
pistons=[];boots=[]
for sign in [-1,1]:
 p=cyl(8.3,22.5,c+Z*sign*16,Z*sign)
 p=p.cut(box(3.8,20,12,c+Z*sign*35))
 pistons.append(p)
 boots.append(ring(14,9,8,c+Z*sign*24,Z*sign))
for sign in [-1,1]:
 assert comp(pistons).intersect(box(3.2,18,8,c+Z*sign*35)).Volume()<1e-5
put('wheel_cylinder_two_slotted_piston_envelopes',comp(pistons),(.60,.61,.55))
put('wheel_cylinder_two_unloaded_open_boots',comp(boots),(.12,.15,.13))
washer=cq.Solid.makeCone(11,10,1.5,cq.Vector(-23.5,68,0),X).cut(cyl(7.3,4,cq.Vector(-24,68,0)))
put('wheel_cylinder_unloaded_belleville_washer_comparison',washer,(.57,.58,.51))
clip=ring(8.8,6.4,1.2,cq.Vector(-24.7,68,0)).cut(box(4,7,8,cq.Vector(-24.1,76,0)))
put('wheel_cylinder_open_circlip_comparison',clip,(.57,.58,.51))
bleed=ring(2.8,1,8,cq.Vector(-38,76,12)).fuse(ring(4,1,4,cq.Vector(-34,76,12)))
put('wheel_cylinder_hollow_bleed_screw_envelope',bleed,(.58,.59,.52))
# Bracket, smooth screw/conical wedge and separate sliding tappets. Thread,
# wedge travel, contact loading and adjustment setting are not reconstructed.
ac=cq.Vector(-8,-74,0)
bracket=box(18,18,36,ac).cut(box(12,13,40,ac)).fuse(ring(4.8,4.2,8,cq.Vector(-24,-74,0)))
bracket=bracket.cut(cyl(4.2,32,cq.Vector(-36,-74,0)))
put('adjuster_open_bracket_and_backplate_neck',bracket.clean())
wedge=cyl(4,24,cq.Vector(-36,-74,0)).fuse(cq.Solid.makeCone(7,1,10,cq.Vector(-12,-74,0),X)).fuse(box(7,9,9,cq.Vector(-35,-74,0)))
put('adjuster_unthreaded_square_drive_and_conical_wedge',wedge,(.59,.60,.53))
for label,sign in [('front',1),('rear',-1)]:
 tappet=box(10,12,29,ac+Z*sign*18.5).cut(cq.Solid.makeCone(7.1,1.1,10,cq.Vector(-12,-74,0),X))
 put('adjuster_'+label+'_tappet_and_estimated_wedge_face',tappet,(.58,.60,.53))
# Connected unloaded wire envelopes with explicit web-eye endpoints. Their
# routing is an illustration of the two factory spring roles, not handed fit.
for label,ysign in [('cylinder_end',1),('adjuster_end',-1)]:
 helix=cq.Wire.makeHelix(5.5,44,3).translate((-8,ysign*50,-22))
 start,end=helix.startPoint(),helix.endPoint()
 coil=cq.Workplane(cq.Plane(origin=start,normal=helix.Edges()[0].tangentAt(0))).circle(.85).sweep(helix,isFrenet=True).val()
 paths=[[cq.Vector(-.5,ysign*60,-49),cq.Vector(-8,ysign*60,-49),cq.Vector(-8,ysign*50,-25),start],[end,cq.Vector(-8,ysign*50,25),cq.Vector(-8,ysign*60,49),cq.Vector(-.5,ysign*60,49)]]
 wires=[coil]
 for pts in paths:
  for a,b in zip(pts,pts[1:]):
   v=b-a;wires.append(cyl(.85,v.Length,a,v.normalized()))
  wires.extend(cq.Solid.makeSphere(.85,p) for p in pts[1:-1])
 put(label+'_unloaded_spring_with_web_eye_endpoints',comp(wires),(.56,.58,.50))
# Passage checks are source drawing checks only, with witness material nearby.
hubassembly=cq.Assembly.importStep(str(OUT/'rear-hub-reference.step'))
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(p):return [sign*(W+p.x),.39*S+p.y,-1.2*S+p.z]
 for name,s,color in local:parts.append(('rear_'+label+'_'+name,tr(s),color))
 plateobj=hubassembly.objects['rear_'+label+'_connected_dished_backplate_with_shared_mount_and_actuator_apertures'].obj
 plate=plateobj.val() if isinstance(plateobj,cq.Workplane) else plateobj
 for name,s,color in local:
  vol=round(tr(s).intersect(plate).Volume(),4)
  if vol>1e-4:contacts[label+'_'+name+'__backplate']=vol
 for center,radius in [(cq.Vector(-26,68,0),7.2),(cq.Vector(-26,76,12),3.2),(cq.Vector(-26,-74,0),4.8)]:
  assert plate.intersect(tr(cyl(radius,10,center))).Volume()<1e-5,('open backplate interface',label)
 assert plate.intersect(tr(box(10,19,24,cq.Vector(-21,70,-23)))).Volume()<1e-5
 for na,a,_ in local:
  for nb,b,_ in local:
   if na>=nb:continue
   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[label+'_'+na+'__'+nb]=v
 nodes[label]=dict(cylinder_axis_center_body_mm=point(c),cylinder_inlet_tip_body_mm=point(cq.Vector(-35,68,0)),cylinder_mount_axis_body_mm=point(cq.Vector(-21,68,0)),bleed_axis_body_mm=point(cq.Vector(-21,76,12)),handbrake_slot_center_body_mm=point(cq.Vector(-21,70,-23)),adjuster_axis_body_mm=point(cq.Vector(-21,-74,0)),shoe_spring_eye_axes_body_mm=[point(p) for p in shoe_holes],outward=[sign,0,0],cylinder_outward_construction_shift_mm=6)
limits='No new owner dimensions or fitted brake identity applied. Factory p148 supports GAN6 GWC1129, GBS625, 37H8804/5, separate 17H7613 Belleville/17H7622 clip, 17H7618 tappets/17H7619 wedge, upper handed 17H7947/48 and lower 17H7621 spring roles. GWC1102 remains an earlier branch. Mini Spares GWC1129 11/16 inch bore is a supplier component comparison, not a measured/factory fitted-car bore or wholesale Mini brake configuration. All body profiles, lengths, slots, seats, necks, spring-eye positions, section sizes, drum-relative pose and contacts are estimates. Barrel/piston/boot axis moves 6 mm outward to the existing shoe-web plane; the inlet tip and external handbrake rod/lever stations remain unchanged. Two separate upper backplate apertures plus bleed aperture replace the old combined slot; lower screw aperture remains. The two modified backplate meshes alone change in the retained hub source. No new factory drilling template or confirmed hydraulic splice is claimed. The hollow barrel, open neck/bleed boundaries and slotted piston ends expose architecture; hidden fluid passages, pressure seals and piston operating stroke are absent. Belleville/clip/boots are unloaded comparisons; groove fit and spring compression are unverified. Shoe lining outer arcs retain nominal 177.8 mm drum contour, without running clearance, wear setting, leading/trailing or handed lining assignment. Adjuster screw is unthreaded; taper/tappet faces and open bracket do not establish travel, loading or setting. Spring segments and web-eye endpoints are unloaded illustrative routing, not exact handed hooks, preload, stored force or safe release. The retained external handbrake lever/boot is not connected to a verified internal shoe-expansion linkage. Positive source contacts remain documented drawing discrepancies, not fitted-car interference/damage. No complete pressure circuit, drum/shoe withdrawal, support or replacement/bleeding/adjustment procedure is verified; separation is illustrative.'
meta=dict(units='mm',source_frame='Canonical body mm +X vehicle-left +Y up +Z front.',factory_source='https://www.spridgets.de/downloads/Parts-1977.pdf#page=148',supplier_sources=['https://www.minispares.com/gwc1129-rear-wheel-cylinder-11-16-bore','https://www.minispares.com/17h7618-brake-adjuster-tappet-see-17h7620-for-full-cheaper-kit'],supplier_bore_comparison_mm=17.4625,owner_dimensions_applied=False,basis='Factory 1500 rear-brake component roles and distinct backplate apertures; supplier wheel-cylinder/slotted piston and adjuster-component photographs support architecture. Existing owner photographs remain context, without a calibrated hidden brake-internal survey. New dimensions and all installed relationships are estimated.',registration_status='unregistered_rear_drum_brake_mechanism_comparison',nodes=nodes,sampled_source_contacts_mm3=contacts,limits=limits)
a=cq.Assembly(name='unregistered_rear_drum_brake_mounts_and_actuators');meshes=[]
for name,s,color in parts:
 a.add(s,name=name,color=cq.Color(*color));vs,ts=s.tessellate(.5,.18);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/'rear-brake-reference.step'));meta['parts']=meshes;(OUT/'rear-brake-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n');restored=cq.importers.importStep(str(OUT/'rear-brake-reference.step')).val();assert restored.isValid();print(json.dumps(dict(meshes=len(parts),solids=len(restored.Solids()),step_valid=True,separate_backplate_interface_passages=True,source_contacts=contacts)))
