@@ -556,6 +556,80 @@ def hook(builder, *_):
556556 [f"/World/envs/env_{ env_id } /Robot/{ label } " for env_id in env_ids for label in ("base" , "hook" )],
557557 )
558558
559+ def test_half_replication_hook_is_rejected_on_non_homogeneous_plan (self ):
560+ def hook (* _ ):
561+ pass
562+
563+ hook ._can_replicate_builder = lambda _ : True
564+ sources = (f"{ self ._SRC } /a" , f"{ self ._SRC } /b" )
565+ with self .assertRaisesRegex (TypeError , "has no callable '_prepare_builder_replication'" ):
566+ replicate_builder_mapping (
567+ newton .ModelBuilder (),
568+ sources ,
569+ np .eye (2 , dtype = np .bool_ ),
570+ np .zeros ((2 , 3 ), dtype = np .float32 ),
571+ np .array ([[0.0 , 0.0 , 0.0 , 1.0 ]] * 2 , dtype = np .float32 ),
572+ {source : newton .ModelBuilder () for source in sources },
573+ destinations = (f"{ self ._ENV } /a" , f"{ self ._ENV } /b" ),
574+ env_ids = np .arange (2 , dtype = np .int64 ),
575+ per_world_builder_hooks = (hook ,),
576+ )
577+
578+ def test_hook_added_particles_inherit_destination_max_velocity (self ):
579+ source = newton .ModelBuilder ()
580+ builder = newton .ModelBuilder ()
581+ builder .particle_max_velocity = 17.0
582+
583+ def hook (* _ ):
584+ pass
585+
586+ def prepare (_builder , source_builder , * _ ):
587+ self .assertEqual (source_builder .particle_max_velocity , 17.0 )
588+ source_builder .add_particle (pos = (0.0 , 0.0 , 0.0 ), vel = (0.0 , 0.0 , 0.0 ), mass = 1.0 , radius = 0.1 )
589+ return lambda : None
590+
591+ hook ._can_replicate_builder = lambda _ : True
592+ hook ._prepare_builder_replication = prepare
593+ replicate_builder_mapping (
594+ builder ,
595+ (self ._SRC ,),
596+ np .ones ((1 , 2 ), dtype = np .bool_ ),
597+ np .zeros ((2 , 3 ), dtype = np .float32 ),
598+ np .array ([[0.0 , 0.0 , 0.0 , 1.0 ]] * 2 , dtype = np .float32 ),
599+ {self ._SRC : source },
600+ destinations = (f"{ self ._ENV } /Robot" ,),
601+ env_ids = np .arange (2 , dtype = np .int64 ),
602+ per_world_builder_hooks = (hook ,),
603+ )
604+ self .assertEqual (source .particle_max_velocity , 17.0 )
605+ self .assertEqual (builder .particle_max_velocity , 17.0 )
606+
607+ def test_particle_source_keeps_own_max_velocity_with_replication_hook (self ):
608+ source = newton .ModelBuilder ()
609+ source .add_particle (pos = (0.0 , 0.0 , 0.0 ), vel = (0.0 , 0.0 , 0.0 ), mass = 1.0 , radius = 0.1 )
610+ source .particle_max_velocity = 23.0
611+ builder = newton .ModelBuilder ()
612+ builder .particle_max_velocity = 17.0
613+
614+ def hook (* _ ):
615+ pass
616+
617+ hook ._can_replicate_builder = lambda _ : True
618+ hook ._prepare_builder_replication = lambda * _ : lambda : None
619+ replicate_builder_mapping (
620+ builder ,
621+ (self ._SRC ,),
622+ np .ones ((1 , 2 ), dtype = np .bool_ ),
623+ np .zeros ((2 , 3 ), dtype = np .float32 ),
624+ np .array ([[0.0 , 0.0 , 0.0 , 1.0 ]] * 2 , dtype = np .float32 ),
625+ {self ._SRC : source },
626+ destinations = (f"{ self ._ENV } /Robot" ,),
627+ env_ids = np .arange (2 , dtype = np .int64 ),
628+ per_world_builder_hooks = (hook ,),
629+ )
630+ self .assertEqual (source .particle_max_velocity , 23.0 )
631+ self .assertEqual (builder .particle_max_velocity , 23.0 )
632+
559633
560634class TestRootJointNaming (unittest .TestCase ):
561635 """The importer leaves a floating base's root joint unnamed; every other entity is named."""
0 commit comments