Skip to content

Commit e7a737c

Browse files
authored
Add test for non-contiguous case (newton-physics#1549)
1 parent 9e13713 commit e7a737c

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

newton/tests/test_selection.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,73 @@ def test_selection_shapes(self):
148148
multi_ant_per_world_view.get_attribute("shape_thickness", multi_ant_per_world_model).shape, (W, A, S)
149149
)
150150

151+
def test_selection_shape_values_noncontiguous(self):
152+
"""Test that shape attribute values are correct when shape selection is non-contiguous."""
153+
# Build a 3-link chain: base -> link1 -> link2
154+
# Each link has one shape with a distinct thickness value
155+
robot = newton.ModelBuilder()
156+
157+
thicknesses = [0.001, 0.002, 0.003]
158+
159+
base = robot.add_link(xform=wp.transform([0, 0, 0], wp.quat_identity()), mass=1.0, key="base")
160+
robot.add_shape_box(
161+
base,
162+
hx=0.1,
163+
hy=0.1,
164+
hz=0.1,
165+
cfg=newton.ModelBuilder.ShapeConfig(thickness=thicknesses[0]),
166+
key="shape_base",
167+
)
168+
169+
link1 = robot.add_link(xform=wp.transform([0, 0, 0.5], wp.quat_identity()), mass=0.5, key="link1")
170+
robot.add_shape_capsule(
171+
link1,
172+
radius=0.05,
173+
half_height=0.2,
174+
cfg=newton.ModelBuilder.ShapeConfig(thickness=thicknesses[1]),
175+
key="shape_link1",
176+
)
177+
178+
link2 = robot.add_link(xform=wp.transform([0, 0, 1.0], wp.quat_identity()), mass=0.3, key="link2")
179+
robot.add_shape_sphere(
180+
link2,
181+
radius=0.05,
182+
cfg=newton.ModelBuilder.ShapeConfig(thickness=thicknesses[2]),
183+
key="shape_link2",
184+
)
185+
186+
j0 = robot.add_joint_free(child=base)
187+
j1 = robot.add_joint_revolute(parent=base, child=link1, axis=[0, 1, 0])
188+
j2 = robot.add_joint_revolute(parent=link1, child=link2, axis=[0, 1, 0])
189+
robot.add_articulation([j0, j1, j2], key="robot")
190+
191+
W = 3
192+
scene = newton.ModelBuilder()
193+
# add a ground plane first so shape indices are offset
194+
scene.add_shape_plane()
195+
scene.replicate(robot, num_worlds=W)
196+
model = scene.finalize()
197+
198+
# exclude the middle link to make shape indices non-contiguous: [0, 2]
199+
view = ArticulationView(model, "robot", exclude_links=["link1"])
200+
self.assertFalse(view.shapes_contiguous, "Expected non-contiguous shape selection")
201+
self.assertEqual(view.shape_count, 2)
202+
203+
# read shape_thickness through ArticulationView and check values
204+
vals = view.get_attribute("shape_thickness", model)
205+
self.assertEqual(vals.shape, (W, 1, 2))
206+
vals_np = vals.numpy()
207+
208+
expected = [thicknesses[0], thicknesses[2]] # base and link2 (link1 excluded)
209+
for w in range(W):
210+
for s, expected_thickness in enumerate(expected):
211+
self.assertAlmostEqual(
212+
float(vals_np[w, 0, s]),
213+
expected_thickness,
214+
places=6,
215+
msg=f"world={w}, shape={s}",
216+
)
217+
151218
def test_selection_mask(self):
152219
# load articulation
153220
ant = newton.ModelBuilder()

0 commit comments

Comments
 (0)