"""Tests for the standard-parts library bearing assemblies.""" import inspect import json import unittest import simplecadapi as scad class TestStdBearingSurface(unittest.TestCase): def test_preferred_nested_std_export_surface(self): assembly = scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 7, 0.05, 0.0, "bearing_surface_test", ) self.assertIsInstance(assembly, scad.Assembly) def test_public_bearing_factories_follow_make_rtype_naming(self): factory_names = [ name for name in scad.std.bearing.__all__ if callable(getattr(scad.std.bearing, name, None)) ] self.assertGreater(len(factory_names), 0) for name in factory_names: self.assertTrue(name.startswith("make_"), name) self.assertIn("_r", name, name) def test_ball_bearing_signature_has_no_keyword_only_separator(self): signature = inspect.signature(scad.std.bearing.make_ball_bearing_rassembly) self.assertNotIn( inspect.Parameter.KEYWORD_ONLY, {parameter.kind for parameter in signature.parameters.values()}, ) class TestBallBearingAssembly(unittest.TestCase): def test_basic_ball_bearing_assembly(self): with scad.GraphSession() as session: bearing = scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 7, 0.05, 0.05, "bearing_basic_test", 30.0, ) preview = scad.make_compound_from_assembly_rcompound(bearing) model_json = scad.export_model_json(session) meta = bearing.get_metadata("std.bearing.ball_bearing") self.assertEqual(meta["ball_count"], 7) self.assertEqual(meta["outer_component_id"], "outer_ring") self.assertEqual(meta["inner_component_id"], "inner_ring") self.assertEqual(len(meta["ball_component_ids"]), 7) self.assertIn("ball_00", meta["ball_component_ids"]) self.assertEqual( bearing.component_ids(), ( "outer_ring", "inner_ring", "ball_00", "ball_01", "ball_02", "ball_03", "ball_04", "ball_05", "ball_06", ), ) self.assertEqual(bearing.constraint_ids(), ("inner_outer_revolute",)) self.assertEqual(bearing.connector_ids(), ("outer_axis", "inner_axis")) self.assertEqual(bearing.grounded_component_ids, ()) constraint = bearing.get_constraint("inner_outer_revolute") self.assertEqual(constraint.constraint_kind, "revolute") self.assertEqual(constraint.connector_a.component_id, "outer_ring") self.assertEqual(constraint.connector_b.component_id, "inner_ring") self.assertEqual(constraint.connector_a.connector_id, "axis") self.assertEqual(constraint.connector_b.connector_id, "axis") self.assertEqual(constraint.drive_angle_degrees, 30.0) outer_part = bearing.get_component("outer_ring").item inner_part = bearing.get_component("inner_ring").item ball_part = bearing.get_component("ball_00").item self.assertEqual(outer_part.connector_ids(), ("axis",)) self.assertEqual(inner_part.connector_ids(), ("axis",)) self.assertEqual(ball_part.connector_ids(), ()) self.assertGreater(outer_part.body.get_volume(), 0.0) self.assertGreater(inner_part.body.get_volume(), 0.0) outer_ring_meta = outer_part.body.get_metadata("std.bearing.ring") inner_ring_meta = inner_part.body.get_metadata("std.bearing.ring") ball_radius = meta["ball_diameter"] / 2.0 self.assertAlmostEqual( outer_ring_meta["inner_radius"], meta["ball_pitch_radius"] + ball_radius * 0.75, ) self.assertAlmostEqual( inner_ring_meta["outer_radius"], meta["ball_pitch_radius"] - ball_radius * 0.75, ) self.assertLess(outer_ring_meta["raceway_mouth_z"], outer_ring_meta["groove_radius"]) self.assertLess(inner_ring_meta["raceway_mouth_z"], inner_ring_meta["groove_radius"]) from OCP.BRepAdaptor import BRepAdaptor_Surface from OCP.GeomAbs import GeomAbs_Sphere, GeomAbs_Torus self.assertTrue( any( BRepAdaptor_Surface(face.wrapped).GetType() == GeomAbs_Torus for face in outer_part.body.get_faces() ) ) self.assertTrue( any( BRepAdaptor_Surface(face.wrapped).GetType() == GeomAbs_Torus for face in inner_part.body.get_faces() ) ) self.assertTrue( all( BRepAdaptor_Surface(face.wrapped).GetType() == GeomAbs_Sphere for face in ball_part.body.get_faces() ) ) self.assertEqual(len(preview.get_solids()), 9) self.assertGreater(preview.get_volume(), 0.0) self.assertTrue(scad.measure_constraint_residual_rconstraintresidual( bearing, "inner_outer_revolute", ).within_tolerance) payload = json.loads(model_json) ops = [node["op"] for node in payload["graph"]["nodes"]] self.assertIn("make_sphere_rsolid", ops) self.assertEqual(ops.count("make_three_point_arc_redge"), 2) self.assertEqual(ops.count("make_revolve_rsolid"), 2) self.assertIn("make_revolute_constraint_rassembly", ops) self.assertEqual(ops.count("make_forward_connector_rassembly"), 2) self.assertIn("make_compound_from_assembly_rcompound", ops) def test_inferred_ball_count_is_recorded(self): bearing = scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, ) meta = bearing.get_metadata("std.bearing.ball_bearing") self.assertGreaterEqual(meta["ball_count"], 3) self.assertEqual(len(meta["ball_component_ids"]), meta["ball_count"]) def test_external_constraints_can_bind_to_internal_connectors(self): bearing = scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 7, 0.05, 0.0, "bearing_bind_test", ) shaft = scad.make_cylinder_rsolid( radius=3.8, height=14.0, bottom_face_center=(0.0, 0.0, -7.0), axis=(0.0, 0.0, 1.0), ) shaft_part = scad.make_part_rpart("shaft", shaft) top_face = max( shaft.get_faces(), key=lambda face: face.get_center().z if face.get_normal_at().z > 0.7 else -999.0, ) shaft_axis = scad.make_face_connector_rconnector("axis", top_face) shaft_part = scad.add_connector_rpart(shaft_part, shaft_axis) bearing = scad.add_component_rassembly( bearing, shaft_part, component_id="shaft", placement=scad.identity_placement_rplacement(), ) bearing = scad.add_fixed_constraint_rassembly( bearing, "shaft_to_inner_ring", scad.make_connector_ref_rconnectorref("inner_ring", "axis"), scad.make_connector_ref_rconnectorref("shaft", "axis"), ) bearing = scad.ground_component_rassembly( assembly=bearing, component_id="outer_ring", ) bearing = scad.solve_assembly_constraints_rassembly(bearing, strict=False) self.assertIn("shaft", bearing.component_ids()) self.assertTrue(scad.measure_constraint_residual_rconstraintresidual( bearing, "shaft_to_inner_ring", ).within_tolerance) def test_parent_assembly_can_bind_to_bearing_forwarded_connectors(self): bearing = scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 7, 0.05, 0.0, "bearing_parent_bind_test", ) shaft = scad.make_cylinder_rsolid( radius=3.8, height=14.0, bottom_face_center=(0.0, 0.0, -7.0), axis=(0.0, 0.0, 1.0), ) shaft_part = scad.make_part_rpart("parent_bind_shaft", shaft) top_face = max( shaft.get_faces(), key=lambda face: face.get_center().z if face.get_normal_at().z > 0.7 else -999.0, ) shaft_axis = scad.make_face_connector_rconnector("axis", top_face) shaft_part = scad.add_connector_rpart(shaft_part, shaft_axis) parent = scad.make_assembly_rassembly("bearing_parent_bind_asm") parent = scad.add_component_rassembly( parent, shaft_part, component_id="shaft", placement=scad.identity_placement_rplacement(), ) parent = scad.add_component_rassembly( parent, bearing, component_id="bearing", placement=scad.identity_placement_rplacement(), ) parent = scad.ground_component_rassembly(parent, "shaft") parent = scad.add_fixed_constraint_rassembly( parent, "shaft_to_bearing_inner_axis", scad.make_connector_ref_rconnectorref("shaft", "axis"), scad.make_connector_ref_rconnectorref("bearing", "inner_axis"), ) parent = scad.solve_assembly_constraints_rassembly(parent) self.assertTrue(scad.measure_constraint_residual_rconstraintresidual( parent, "shaft_to_bearing_inner_axis", ).within_tolerance) def test_invalid_params(self): with self.assertRaises(Exception): scad.std.bearing.make_ball_bearing_rassembly(22.0, 8.0, 7.0, 3.5) with self.assertRaises(Exception): scad.std.bearing.make_ball_bearing_rassembly(8.0, 22.0, 3.0, 3.5) with self.assertRaises(Exception): scad.std.bearing.make_ball_bearing_rassembly(8.0, 22.0, 7.0, 8.0) with self.assertRaises(Exception): scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 100, ) with self.assertRaises(Exception): scad.std.bearing.make_ball_bearing_rassembly( 8.0, 22.0, 7.0, 3.5, 7, 0.05, 2.0, ) if __name__ == "__main__": unittest.main()