rushabhcodes/desk-fidget-counter

A desk knob assembly combining a circuit board with PETG-printed mechanical parts and vendor CAD-modeled hardware.

Version
5.6.1
License
MIT
Stars
0

mechanical/bearing.py

# SPDX-License-Identifier: MIT
"""6712ZZ model: verified boundary size, explicitly estimated internals.

This represents a purchased bearing, never a print or a bearing manufacture
specification. The component interfaces use separate inner/outer race solids.
"""
import math
import numpy as np
import trimesh
import manifold3d as m

def mesh(s):
    q=s.to_mesh64()
    return trimesh.Trimesh(np.asarray(q.vert_properties)[:,:3],np.asarray(q.tri_verts),process=True)

def ring(ro,ri,h,z):
    return m.Manifold.cylinder(h,ro,ro,192).translate((0,0,z))-m.Manifold.cylinder(h+.2,ri,ri,192).translate((0,0,z-.1))

def chamfered_ring(ro,ri,h,z,c):
    profile=[(ri+c,0),(ro-c,0),(ro,c),(ro,h-c),(ro-c,h),(ri+c,h),(ri,h-c),(ri,c)]
    return m.CrossSection([profile]).revolve(192).translate((0,0,z))

def build(parameters):
    b=parameters['bearing'];a=b['approximation'];z=b['bottom_z'];h=b['width']
    pitch=a['pitch_diameter']/2;groove=a['groove_radius'];count=a['ball_count']
    circle=[(pitch+groove*math.cos(t),h/2+groove*math.sin(t)) for t in np.linspace(0,2*math.pi,96,endpoint=False)]
    raceway=m.CrossSection([circle]).revolve(192).translate((0,0,z))
    inner=chamfered_ring(a['inner_race_od']/2,b['id']/2,h,z,a['edge_chamfer'])-raceway
    outer=chamfered_ring(b['od']/2,a['outer_race_id']/2,h,z,a['edge_chamfer'])-raceway
    shields=[]
    for level in [z+a['shield_recess'],z+h-a['shield_recess']-a['shield_thickness']]:
        # A shallow seat holds the outer shield rim; the inner rim has clearance.
        outer-=ring(a['shield_outer_diameter']/2+.05,a['outer_race_id']/2-.1,a['shield_thickness']+.06,level-.03)
        shields.append(ring(a['shield_outer_diameter']/2,a['shield_inner_diameter']/2,a['shield_thickness'],level))
    balls=[];bars=[]
    cage=ring(pitch+.35,pitch-.35,.2,z+1.25)+ring(pitch+.35,pitch-.35,.2,z+h-1.45)
    for i in range(count):
        theta=2*math.pi*i/count
        balls.append(m.Manifold.sphere(a['ball_diameter']/2,32).translate((pitch*math.cos(theta),pitch*math.sin(theta),z+h/2)))
        theta+=math.pi/count
        bars.append(m.Manifold.cube((.7,.35,h-2.5),True).rotate((0,0,math.degrees(theta))).translate((pitch*math.cos(theta),pitch*math.sin(theta),z+h/2)))
    cage+=m.Manifold.batch_boolean(bars,m.OpType.Add)
    solids={'bearing-inner-race':inner,'bearing-outer-race':outer,'bearing-shield-bottom':shields[0],'bearing-shield-top':shields[1],'bearing-balls':m.Manifold.batch_boolean(balls,m.OpType.Add),'bearing-cage':cage}
    # A visual approximation must still be mechanically self-consistent.
    for name,solid in solids.items():
        assert solid.volume()>0 and str(solid.status()).endswith('NoError'),name
    for i,(name,s) in enumerate(solids.items()):
        for other,t in list(solids.items())[i+1:]:
            assert (s^t).volume()<1e-4,(name,other,'bearing internal collision',(s^t).volume())
    report={'purchased_part':'Maysme 6712ZZ','verified_boundary_mm':{'id':b['id'],'od':b['od'],'width':h},'approximation':a,'assembly_object_count':1,'assembly_object_name':'bearing','internal_solid_count':len(solids),'internal_intersections':0,'status':'User-authorized best approximation. Race/shield geometry and ball/cage internals are not manufacturer CAD. All internals are combined into one bearing object in the product assembly.'}
    return {name:mesh(s) for name,s in solids.items()},solids,report