Skip to content

Commit

Permalink
start to add unittests
Browse files Browse the repository at this point in the history
  • Loading branch information
fbxiang committed Jan 30, 2023
1 parent 54a808d commit ae93ece
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 27 deletions.
21 changes: 2 additions & 19 deletions python/pysapien_content.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@ If after testing g2 and g3, the objects may collide, g0 and g1 come into play. g
.def("set_timestep", &SScene::setTimestep, py::arg("second"))
.def("get_timestep", &SScene::getTimestep)
.def_property("timestep", &SScene::getTimestep, &SScene::setTimestep)
.def("get_config", &SScene::getConfig)
.def_property("default_physical_material", &SScene::getDefaultMaterial,
&SScene::setDefaultMaterial)
.def("create_actor_builder", &SScene::createActorBuilder)
Expand Down Expand Up @@ -877,9 +878,6 @@ If after testing g2 and g3, the objects may collide, g0 and g1 come into play. g
"add_mounted_camera",
[](SScene &scene, std::string const &name, SActorBase *actor, PxTransform const &pose,
uint32_t width, uint32_t height, float fovx, float fovy, float near, float far) {
// spdlog::get("SAPIEN")->warn(
// "add_mounted_camera with fovx has been deprecated and will be "
// "removed in the next release.");
auto cam = scene.addCamera(name, width, height, fovy, near, far);
cam->setParent(actor);
cam->setLocalPose(pose);
Expand All @@ -889,23 +887,8 @@ If after testing g2 and g3, the objects may collide, g0 and g1 come into play. g
py::arg("fovx"), py::arg("fovy"), py::arg("near"), py::arg("far"),
py::return_value_policy::reference)
.def("get_cameras", &SScene::getCameras, py::return_value_policy::reference)
.def(
"get_mounted_cameras",
[](SScene &scene) {
// spdlog::get("SAPIEN")->warn("get_mounted_cameras has been deprecated and will be "
// "removed in the next release, "
// "please use equivalent function get_cameras instead.");
return scene.getCameras();
},
py::return_value_policy::reference)
.def("get_mounted_cameras", &SScene::getCameras, py::return_value_policy::reference)
.def("remove_camera", &SScene::removeCamera, py::arg("camera"))

// .def("remove_mounted_camera", &SScene::removeMountedCamera, py::arg("camera"))
// .def("get_mounted_actors", &SScene::getMountedActors,
// py::return_value_policy::reference) .def("find_mounted_camera",
// &SScene::findMountedCamera, py::arg("name"), py::arg("actor") = nullptr,
// py::return_value_policy::reference)

.def("step", &SScene::step)
.def("step_async",
[](SScene &scene) {
Expand Down
2 changes: 2 additions & 0 deletions unittest/all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from engine import *
from scene import *
Binary file added unittest/assets/cone.stl
Binary file not shown.
Binary file added unittest/assets/torus.stl
Binary file not shown.
8 changes: 0 additions & 8 deletions unittest/basic.py

This file was deleted.

18 changes: 18 additions & 0 deletions unittest/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import sapien.core as sapien
import numpy as np


def rand_p():
return np.random.randn(3)


def rand_q():
q = np.random.randn(4)
q /= np.linalg.norm(q)
return q

def rand_size():
return np.random.rand(3) * 10

def rand_pose():
return sapien.Pose(rand_p(), rand_q())
23 changes: 23 additions & 0 deletions unittest/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import unittest
import sapien.core as sapien


class TestEngine(unittest.TestCase):
def test_create_engine(self):
engine = sapien.Engine()

def test_duplicate(self):
engine = sapien.Engine()
engine1 = sapien.Engine()
engine = sapien.Engine()
engine = sapien.Engine()
engine = None
engine = sapien.Engine()

def test_create_physical_material(self):
engine = sapien.Engine()
mat = engine.create_physical_material(0.15, 0.14, 0.45)
self.assertAlmostEqual(mat.static_friction, 0.15)
self.assertAlmostEqual(mat.dynamic_friction, 0.14)
self.assertAlmostEqual(mat.restitution, 0.45)
# TODO: invalid value validation?
164 changes: 164 additions & 0 deletions unittest/scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import unittest
import sapien.core as sapien
from common import *
import numpy as np


class TestScene(unittest.TestCase):
def test_create_scene(self):
engine = sapien.Engine()
scene = engine.create_scene()

def test_config(self):
engine = sapien.Engine()
config = sapien.SceneConfig()
config.gravity = [0, 0, -1]
config.default_static_friction = 0.2
config.default_dynamic_friction = 0.19
config.default_restitution = 0.05
config.bounce_threshold = 1.0
config.sleep_threshold = 0.001
config.contact_offset = 0.02
config.solver_iterations = 4
config.solver_velocity_iterations = 2
config.enable_pcm = True
config.enable_tgs = True
config.enable_ccd = True
config.enable_enhanced_determinism = True
config.enable_friction_every_iteration = False
config.enable_adaptive_force = True
config.disable_collision_visual = True
scene = engine.create_scene(config)
config1 = scene.get_config()

self.assertAlmostEqual(tuple(config.gravity), (0, 0, -1))
self.assertAlmostEqual(config.default_static_friction, 0.2)
self.assertAlmostEqual(config.default_dynamic_friction, 0.19)
self.assertAlmostEqual(config.default_restitution, 0.05)
self.assertAlmostEqual(config.bounce_threshold, 1.0)
self.assertAlmostEqual(config.sleep_threshold, 0.001)
self.assertAlmostEqual(config.contact_offset, 0.02)
self.assertEqual(config.solver_iterations, 4)
self.assertEqual(config.solver_velocity_iterations, 2)
self.assertEqual(config.enable_pcm, True)
self.assertEqual(config.enable_tgs, True)
self.assertEqual(config.enable_ccd, True)
self.assertEqual(config.enable_enhanced_determinism, True)
self.assertEqual(config.enable_friction_every_iteration, False)
self.assertEqual(config.enable_adaptive_force, True)
self.assertEqual(config.disable_collision_visual, True)

def test_actor_builder(self):
engine = sapien.Engine()
scene = engine.create_scene()
builder = scene.create_actor_builder()

poses = [rand_pose() for _ in range(10)]
sizes = [rand_size() for _ in range(10)]
mats = [engine.create_physical_material(*np.random.rand(3)) for _ in range(10)]
densities = [np.random.rand() * 1000 for _ in range(10)]
patch_radii = [np.random.rand() for _ in range(10)]
min_patch_radii = [np.random.rand() for _ in range(10)]

idx = 0
builder.add_box_collision(
pose=poses[idx],
half_size=sizes[idx],
material=mats[idx],
density=densities[idx],
patch_radius=patch_radii[idx],
min_patch_radius=min_patch_radii[idx],
is_trigger=False,
)
idx += 1

builder.add_sphere_collision(
pose=poses[idx],
radius=sizes[idx][0],
material=mats[idx],
density=densities[idx],
patch_radius=patch_radii[idx],
min_patch_radius=min_patch_radii[idx],
is_trigger=False,
)
idx += 1

builder.add_capsule_collision(
pose=poses[idx],
radius=sizes[idx][1],
half_length=sizes[idx][0],
material=mats[idx],
density=densities[idx],
patch_radius=patch_radii[idx],
min_patch_radius=min_patch_radii[idx],
is_trigger=False,
)
idx += 1

builder.add_collision_from_file(
filename="assets/cone.stl",
pose=poses[idx],
scale=sizes[idx],
material=mats[idx],
density=densities[idx],
patch_radius=patch_radii[idx],
min_patch_radius=min_patch_radii[idx],
is_trigger=False,
)
idx += 1

builder.add_nonconvex_collision_from_file(
filename="assets/torus.stl",
pose=poses[idx],
scale=sizes[idx],
material=mats[idx],
patch_radius=patch_radii[idx],
min_patch_radius=min_patch_radii[idx],
is_trigger=False,
)

collisions = builder.get_collisions()
self.assertEqual(len(collisions), 5)
for i, c in enumerate(collisions):
self.assertTrue(np.allclose(c.pose.p, poses[i].p))
self.assertTrue(np.allclose(c.pose.q, poses[i].q))
self.assertEqual(c.material, mats[i])
if c.type != "Nonconvex":
self.assertTrue(np.allclose(c.density, densities[i]))

self.assertEqual(collisions[0].type, "Box")
self.assertEqual(collisions[1].type, "Sphere")
self.assertEqual(collisions[2].type, "Capsule")
self.assertEqual(collisions[3].type, "Mesh")
self.assertEqual(collisions[4].type, "Nonconvex")

self.assertTrue(np.allclose(collisions[0].scale, sizes[0]))
self.assertTrue(np.allclose(collisions[1].radius, sizes[1][0]))
self.assertTrue(np.allclose(collisions[2].radius, sizes[2][1]))
self.assertTrue(np.allclose(collisions[2].length, sizes[2][0]))
self.assertTrue(np.allclose(collisions[3].scale, sizes[3]))
self.assertTrue(np.allclose(collisions[4].scale, sizes[4]))

actor = builder.build_kinematic()
self.assertEqual(actor.type, "kinematic")
self.assertEqual(
tuple(c.type for c in actor.get_collision_shapes()),
("box", "sphere", "capsule", "convex_mesh", "nonconvex_mesh"),
)

actor = builder.build_static()
self.assertEqual(actor.type, "static")
self.assertEqual(
tuple(c.type for c in actor.get_collision_shapes()),
("box", "sphere", "capsule", "convex_mesh", "nonconvex_mesh"),
)

builder.remove_collision_at(4)
actor = builder.build()
self.assertEqual(actor.type, "dynamic")
self.assertEqual(
tuple(c.type for c in actor.get_collision_shapes()),
("box", "sphere", "capsule", "convex_mesh"),
)

# TODO: check details of the built shapes

0 comments on commit ae93ece

Please sign in to comment.