differt-core 0.9.1__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {differt_core-0.9.1 → differt_core-0.11.0}/Cargo.lock +1 -1
  2. {differt_core-0.9.1 → differt_core-0.11.0}/PKG-INFO +1 -1
  3. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/Cargo.toml +1 -1
  4. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/benchmarks/graph_iterators.rs +1 -1
  5. differt_core-0.9.1/differt-core/src/geometry/triangle_mesh.rs → differt_core-0.11.0/differt-core/src/geometry/mesh.rs +52 -36
  6. differt_core-0.11.0/differt-core/src/geometry/mod.rs +16 -0
  7. differt_core-0.11.0/differt-core/src/geometry/scene.rs +78 -0
  8. {differt_core-0.9.1/src/scene → differt_core-0.11.0/differt-core/src/geometry}/sionna.rs +92 -7
  9. {differt_core-0.9.1 → differt_core-0.11.0/differt-core}/src/lib.rs +0 -4
  10. differt_core-0.11.0/differt-core/tests/geometry/test_core_mesh.py +11 -0
  11. differt_core-0.11.0/differt-core/tests/geometry/test_core_scene.py +11 -0
  12. {differt_core-0.9.1/differt-core/tests/rt → differt_core-0.11.0/differt-core/tests/geometry}/test_graph.py +3 -30
  13. {differt_core-0.9.1/python/differt_core/_differt_core/rt → differt_core-0.11.0/python/differt_core/_differt_core/geometry}/graph.pyi +0 -1
  14. differt_core-0.9.1/python/differt_core/_differt_core/geometry/triangle_mesh.pyi → differt_core-0.11.0/python/differt_core/_differt_core/geometry/mesh.pyi +6 -5
  15. differt_core-0.11.0/python/differt_core/_differt_core/geometry/scene.pyi +9 -0
  16. {differt_core-0.9.1/python/differt_core/_differt_core/scene → differt_core-0.11.0/python/differt_core/_differt_core/geometry}/sionna.pyi +3 -1
  17. {differt_core-0.9.1/python/differt_core/rt → differt_core-0.11.0/python/differt_core/geometry}/__init__.py +11 -1
  18. differt_core-0.11.0/python/differt_core/geometry/_graph.py +23 -0
  19. differt_core-0.11.0/python/differt_core/geometry/_mesh.py +28 -0
  20. differt_core-0.11.0/python/differt_core/geometry/_scene.py +27 -0
  21. differt_core-0.11.0/python/differt_core/geometry/_sionna.py +7 -0
  22. differt_core-0.9.1/src/geometry/triangle_mesh.rs → differt_core-0.11.0/src/geometry/mesh.rs +52 -36
  23. differt_core-0.11.0/src/geometry/mod.rs +16 -0
  24. differt_core-0.11.0/src/geometry/scene.rs +78 -0
  25. {differt_core-0.9.1/differt-core/src/scene → differt_core-0.11.0/src/geometry}/sionna.rs +92 -7
  26. {differt_core-0.9.1/differt-core → differt_core-0.11.0}/src/lib.rs +0 -4
  27. differt_core-0.9.1/differt-core/src/geometry/mod.rs +0 -10
  28. differt_core-0.9.1/differt-core/src/rt/mod.rs +0 -10
  29. differt_core-0.9.1/differt-core/src/scene/mod.rs +0 -12
  30. differt_core-0.9.1/differt-core/src/scene/triangle_scene.rs +0 -83
  31. differt_core-0.9.1/python/differt_core/_differt_core/rt/__init__.pyi +0 -0
  32. differt_core-0.9.1/python/differt_core/_differt_core/scene/triangle_scene.pyi +0 -7
  33. differt_core-0.9.1/python/differt_core/geometry/__init__.py +0 -5
  34. differt_core-0.9.1/python/differt_core/geometry/_triangle_mesh.py +0 -5
  35. differt_core-0.9.1/python/differt_core/rt/_graph.py +0 -19
  36. differt_core-0.9.1/python/differt_core/scene/__init__.py +0 -6
  37. differt_core-0.9.1/python/differt_core/scene/_sionna.py +0 -7
  38. differt_core-0.9.1/python/differt_core/scene/_triangle_scene.py +0 -5
  39. differt_core-0.9.1/src/geometry/mod.rs +0 -10
  40. differt_core-0.9.1/src/rt/mod.rs +0 -10
  41. differt_core-0.9.1/src/scene/mod.rs +0 -12
  42. differt_core-0.9.1/src/scene/triangle_scene.rs +0 -83
  43. {differt_core-0.9.1 → differt_core-0.11.0}/Cargo.toml +0 -0
  44. {differt_core-0.9.1 → differt_core-0.11.0}/LICENSE.md +0 -0
  45. {differt_core-0.9.1 → differt_core-0.11.0}/README.md +0 -0
  46. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/LICENSE.md +0 -0
  47. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/README.md +0 -0
  48. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/bench_main.rs +0 -0
  49. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/benchmarks/mod.rs +0 -0
  50. {differt_core-0.9.1/differt-core/src/rt → differt_core-0.11.0/differt-core/src/geometry}/graph.rs +0 -0
  51. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/tests/__init__.py +0 -0
  52. {differt_core-0.9.1/differt-core/tests/rt → differt_core-0.11.0/differt-core/tests/geometry}/__init__.py +0 -0
  53. {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/tests/test_version.py +0 -0
  54. {differt_core-0.9.1 → differt_core-0.11.0}/pyproject.toml +0 -0
  55. {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/__init__.py +2 -2
  56. {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/_differt_core/__init__.pyi +0 -0
  57. {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/py.typed +0 -0
  58. {differt_core-0.9.1/src/rt → differt_core-0.11.0/src/geometry}/graph.rs +0 -0
@@ -247,7 +247,7 @@ checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
247
247
 
248
248
  [[package]]
249
249
  name = "differt-core"
250
- version = "0.9.1"
250
+ version = "0.11.0"
251
251
  dependencies = [
252
252
  "criterion",
253
253
  "indexmap",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: differt-core
3
- Version: 0.9.1
3
+ Version: 0.11.0
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -37,5 +37,5 @@ unexpected_cfgs = {level = "warn", check-cfg = ['cfg(tarpaulin_include)']}
37
37
  edition = "2021"
38
38
  name = "differt-core"
39
39
  rust-version = "1.78.0"
40
- version = "0.9.1"
40
+ version = "0.11.0"
41
41
  readme = "README.md"
@@ -1,5 +1,5 @@
1
1
  use criterion::{Criterion, Throughput, black_box, criterion_group};
2
- use differt_core::rt::graph::{complete::CompleteGraph, directed::DiGraph};
2
+ use differt_core::geometry::graph::{complete::CompleteGraph, directed::DiGraph};
3
3
 
4
4
  const NUM_NODES: usize = 1000;
5
5
  const DIRECT_PATH: bool = true;
@@ -1,4 +1,8 @@
1
- use std::{fs::File, io::BufReader, path::PathBuf};
1
+ use std::{
2
+ fs::File,
3
+ io::BufReader,
4
+ path::{Path, PathBuf},
5
+ };
2
6
 
3
7
  use indexmap::IndexMap;
4
8
  use numpy::{PyArray1, PyArray2, ndarray::arr2};
@@ -9,10 +13,10 @@ use pyo3::{exceptions::PyValueError, prelude::*, types::PyType};
9
13
  /// A lower-level mesh made of triangles.
10
14
  ///
11
15
  /// This class should not be used directly, prefer its higher-level version
12
- /// :class:`differt.geometry.TriangleMesh` instead.
16
+ /// :class:`differt.geometry.Mesh` instead.
13
17
  #[derive(Clone, Debug, Default)]
14
- #[pyclass]
15
- pub(crate) struct TriangleMesh {
18
+ #[pyclass(subclass)]
19
+ pub(crate) struct Mesh {
16
20
  vertices: Vec<[f32; 3]>,
17
21
  triangles: Vec<[usize; 3]>,
18
22
  face_colors: Option<Vec<[f32; 3]>>,
@@ -77,7 +81,7 @@ impl ply::PropertyAccess for PlyFace {
77
81
  }
78
82
  }
79
83
 
80
- impl TriangleMesh {
84
+ impl Mesh {
81
85
  pub fn get_material_index(&mut self, material_name: Option<String>) -> isize {
82
86
  if let Some(material_name) = material_name {
83
87
  return self
@@ -122,7 +126,7 @@ impl TriangleMesh {
122
126
  }
123
127
 
124
128
  #[pymethods]
125
- impl TriangleMesh {
129
+ impl Mesh {
126
130
  /// :class:`Float[np.ndarray, 'num_vertices 3']<jaxtyping.Float>`: The array of triangle vertices.
127
131
  #[getter]
128
132
  fn vertices<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray2<f32>> {
@@ -184,7 +188,7 @@ impl TriangleMesh {
184
188
  /// After calling this method, ``other`` will be empty.
185
189
  ///
186
190
  /// Args:
187
- /// other(TriangleMesh): The mesh to be appended to ``self``.
191
+ /// other(Mesh): The mesh to be appended to ``self``.
188
192
  pub(crate) fn append(&mut self, other: &mut Self) {
189
193
  match (&self.face_colors, &other.face_colors) {
190
194
  (None, None) => {},
@@ -268,27 +272,53 @@ impl TriangleMesh {
268
272
  ///
269
273
  /// Currently, only vertices and triangles are loaded. Triangle normals
270
274
  /// are ignored because they are computed with
271
- /// :attr:`differt.geometry.TriangleMesh.normals` using
275
+ /// :attr:`differt.geometry.Mesh.normals` using
272
276
  /// JAX so that they can be differentiated with respect to triangle
273
277
  /// vertices.
274
278
  ///
275
279
  /// Args:
276
- /// file (str): The path to the Wavefront .obj file.
280
+ /// file (str | os.PathLike[str]): The path to the Wavefront .obj file.
277
281
  ///
278
282
  /// Returns:
279
- /// TriangleMesh: The corresponding mesh containing only triangles.
283
+ /// Mesh: The corresponding mesh containing only triangles.
280
284
  #[classmethod]
281
- pub(crate) fn load_obj(_: &Bound<'_, PyType>, filename: &str) -> PyResult<Self> {
282
- let input = BufReader::new(File::open(filename)?);
285
+ #[pyo3(name = "load_obj")]
286
+ pub(crate) fn py_load_obj(_: &Bound<'_, PyType>, file: PathBuf) -> PyResult<Self> {
287
+ Self::load_obj(&file)
288
+ }
289
+
290
+ /// Load a triangle mesh from a Stanford PLY .ply file.
291
+ ///
292
+ /// Currently, only vertices and triangles are loaded. Triangle normals
293
+ /// are ignored because they are computed with
294
+ /// :attr:`differt.geometry.Mesh.normals` using
295
+ /// JAX so that they can be differentiated with respect to triangle
296
+ /// vertices.
297
+ ///
298
+ /// Args:
299
+ /// file (str | os.PathLike[str]): The path to the Stanford PLY .ply file.
300
+ ///
301
+ /// Returns:
302
+ /// Mesh: The corresponding mesh containing only triangles.
303
+ #[classmethod]
304
+ #[pyo3(name = "load_ply")]
305
+ pub(crate) fn py_load_ply(_: &Bound<'_, PyType>, file: PathBuf) -> PyResult<Self> {
306
+ Self::load_ply(&file)
307
+ }
308
+ }
309
+
310
+ impl Mesh {
311
+ pub(crate) fn load_obj(file: &Path) -> PyResult<Self> {
312
+ let input = BufReader::new(File::open(file)?);
283
313
  let mut obj: RawObj = parse_obj(input).map_err(|err| {
284
314
  PyValueError::new_err(format!(
285
- "An error occurred while reading obj file {filename:#?}: {}",
315
+ "An error occurred while reading obj file {file:#?}: {}",
286
316
  err
287
317
  ))
288
318
  })?;
289
319
 
290
320
  for material_file in obj.material_libraries.iter_mut() {
291
- let mut path = PathBuf::from(filename);
321
+ let mut path = file.to_path_buf();
292
322
  path.set_file_name(&material_file);
293
323
  if let Some(path_str) = path.to_str() {
294
324
  *material_file = path_str.to_string();
@@ -298,29 +328,15 @@ impl TriangleMesh {
298
328
  Ok(obj.into())
299
329
  }
300
330
 
301
- /// Load a triangle mesh from a Stanford PLY .ply file.
302
- ///
303
- /// Currently, only vertices and triangles are loaded. Triangle normals
304
- /// are ignored because they are computed with
305
- /// :attr:`differt.geometry.TriangleMesh.normals` using
306
- /// JAX so that they can be differentiated with respect to triangle
307
- /// vertices.
308
- ///
309
- /// Args:
310
- /// file (str): The path to the Stanford PLY .ply file.
311
- ///
312
- /// Returns:
313
- /// TriangleMesh: The corresponding mesh containing only triangles.
314
- #[classmethod]
315
- pub(crate) fn load_ply(_: &Bound<'_, PyType>, filename: &str) -> PyResult<Self> {
316
- let mut input = BufReader::new(File::open(filename)?);
331
+ pub(crate) fn load_ply(file: &Path) -> PyResult<Self> {
332
+ let mut input = BufReader::new(File::open(file)?);
317
333
 
318
334
  let vertex_parser = parser::Parser::<PlyVertex>::new();
319
335
  let face_parser = parser::Parser::<PlyFace>::new();
320
336
 
321
337
  let header = vertex_parser.read_header(&mut input).map_err(|err| {
322
338
  PyValueError::new_err(format!(
323
- "An error occurred while reading the header of ply file {filename:#?}: {}",
339
+ "An error occurred while reading the header of ply file {file:#?}: {}",
324
340
  err
325
341
  ))
326
342
  })?;
@@ -337,7 +353,7 @@ impl TriangleMesh {
337
353
  .map_err(|err| {
338
354
  PyValueError::new_err(format!(
339
355
  "An error occurred while reading the vertex elements of ply \
340
- file {filename:#?}: {}",
356
+ file {file:#?}: {}",
341
357
  err
342
358
  ))
343
359
  })?
@@ -352,7 +368,7 @@ impl TriangleMesh {
352
368
  .map_err(|err| {
353
369
  PyValueError::new_err(format!(
354
370
  "An error occurred while reading the face elements of PLY \
355
- file {filename:#?}: {}",
371
+ file {file:#?}: {}",
356
372
  err
357
373
  ))
358
374
  })?
@@ -380,7 +396,7 @@ impl TriangleMesh {
380
396
  }
381
397
  }
382
398
 
383
- impl From<RawObj> for TriangleMesh {
399
+ impl From<RawObj> for Mesh {
384
400
  fn from(mut raw_obj: RawObj) -> Self {
385
401
  use obj::raw::object::Polygon::*;
386
402
 
@@ -503,7 +519,7 @@ impl From<RawObj> for TriangleMesh {
503
519
 
504
520
  #[cfg(not(tarpaulin_include))]
505
521
  #[pymodule(gil_used = false)]
506
- pub(crate) fn triangle_mesh(m: Bound<'_, PyModule>) -> PyResult<()> {
507
- m.add_class::<TriangleMesh>()?;
522
+ pub(crate) fn mesh(m: Bound<'_, PyModule>) -> PyResult<()> {
523
+ m.add_class::<Mesh>()?;
508
524
  Ok(())
509
525
  }
@@ -0,0 +1,16 @@
1
+ use pyo3::{prelude::*, wrap_pymodule};
2
+
3
+ pub mod graph;
4
+ pub mod mesh;
5
+ pub mod scene;
6
+ pub mod sionna;
7
+
8
+ #[cfg(not(tarpaulin_include))]
9
+ #[pymodule(gil_used = false)]
10
+ pub(crate) fn geometry(m: Bound<'_, PyModule>) -> PyResult<()> {
11
+ m.add_wrapped(wrap_pymodule!(graph::graph))?;
12
+ m.add_wrapped(wrap_pymodule!(mesh::mesh))?;
13
+ m.add_wrapped(wrap_pymodule!(scene::scene))?;
14
+ m.add_wrapped(wrap_pymodule!(sionna::sionna))?;
15
+ Ok(())
16
+ }
@@ -0,0 +1,78 @@
1
+ use std::path::{Path, PathBuf};
2
+
3
+ use pyo3::{exceptions::PyValueError, prelude::*, types::PyType};
4
+
5
+ use super::sionna::SionnaScene;
6
+ use crate::geometry::mesh::Mesh;
7
+
8
+ /// A scene that contains one mesh, usually being the results of multiple call to :meth:`Mesh.append<differt_core.geometry.Mesh.append>`.
9
+ ///
10
+ /// This class is only useful to provide a fast constructor for scenes
11
+ /// created using the Sionna file format.
12
+ #[derive(Clone)]
13
+ #[pyclass(subclass)]
14
+ struct Scene {
15
+ /// differt_core.geometry.Mesh: The scene mesh.
16
+ #[pyo3(get)]
17
+ mesh: Mesh,
18
+ }
19
+
20
+ #[pymethods]
21
+ impl Scene {
22
+ /// Load a scene from a Sionna-compatible XML file.
23
+ ///
24
+ /// Args:
25
+ /// file (str | os.PathLike[str]): The path to the XML file.
26
+ ///
27
+ /// Returns:
28
+ /// Scene: The corresponding scene.
29
+ #[classmethod]
30
+ #[pyo3(name = "load_xml")]
31
+ fn py_load_xml(_cls: &Bound<'_, PyType>, file: PathBuf) -> PyResult<Self> {
32
+ Self::load_xml(&file)
33
+ }
34
+ }
35
+ impl Scene {
36
+ fn load_xml(file: &Path) -> PyResult<Self> {
37
+ let sionna = SionnaScene::load_xml(&file)?;
38
+
39
+ let folder = file.parent().ok_or_else(|| {
40
+ PyValueError::new_err(format!(
41
+ "Could not determine parent folder of file: {file:#?}",
42
+ ))
43
+ })?;
44
+
45
+ let mut mesh = Mesh::default();
46
+
47
+ for (_, shape) in sionna.shapes.into_iter() {
48
+ let mesh_file_path = folder.join(shape.file);
49
+ let mut other_mesh = match shape.r#type.as_str() {
50
+ "obj" => Mesh::load_obj(&mesh_file_path)?,
51
+ "ply" => Mesh::load_ply(&mesh_file_path)?,
52
+ ty => {
53
+ log::warn!("Unsupported shape type {ty}, skipping.");
54
+ continue;
55
+ },
56
+ };
57
+
58
+ let material = sionna.materials.get(&shape.material_id);
59
+
60
+ let color = material.map(|mat| mat.color);
61
+
62
+ let material_name = material.map(|mat| mat.name.clone());
63
+
64
+ other_mesh.set_face_color(color.as_ref());
65
+ other_mesh.set_face_material(material_name);
66
+
67
+ mesh.append(&mut other_mesh);
68
+ }
69
+ Ok(Self { mesh })
70
+ }
71
+ }
72
+
73
+ #[cfg(not(tarpaulin_include))]
74
+ #[pymodule(gil_used = false)]
75
+ pub(crate) fn scene(m: Bound<'_, PyModule>) -> PyResult<()> {
76
+ m.add_class::<Scene>()?;
77
+ Ok(())
78
+ }
@@ -1,4 +1,8 @@
1
- use std::{fs::File, io::BufReader};
1
+ use std::{
2
+ fs::File,
3
+ io::BufReader,
4
+ path::{Path, PathBuf},
5
+ };
2
6
 
3
7
  use indexmap::IndexMap;
4
8
  use pyo3::{exceptions::PyValueError, prelude::*, types::PyType};
@@ -8,9 +12,9 @@ use serde::{Deserialize, de};
8
12
  ///
9
13
  /// Only a subset of the XML file is actually used.
10
14
  ///
11
- /// This class is useless unless converted
12
- /// to another scene type, like
13
- /// :class:`TriangleScene<differt.scene.TriangleScene>`.
15
+ /// This scene acts as a bridge between Sionna and DiffeRT,
16
+ /// and is essentially used to quickly load scenes from Sionna
17
+ /// and export them as a :class:`Scene<differt.geometry.Scene>`.
14
18
  ///
15
19
  /// Warning:
16
20
  /// We are still open to better ways to parse those XML files,
@@ -248,7 +252,7 @@ impl<'de> Deserialize<'de> for Material {
248
252
  let color = match r#type.as_str() {
249
253
  // Copied from Sionna-RT's code, to match their colors:
250
254
  // https://github.com/NVlabs/sionna-rt/blob/main/src/sionna/rt/radio_materials/itu_material.py
251
- // v1.0.0
255
+ "vacuum" => [0.8, 0.8, 0.8],
252
256
  "marble" => [0.701, 0.644, 0.485],
253
257
  "concrete" => [0.539, 0.539, 0.539],
254
258
  "wood" => [0.266, 0.109, 0.060],
@@ -263,6 +267,10 @@ impl<'de> Deserialize<'de> for Material {
263
267
  "very_dry_ground" => [0.539, 0.319, 0.223],
264
268
  "medium_dry_ground" => [0.539, 0.181, 0.076],
265
269
  "wet_ground" => [0.539, 0.027, 0.147],
270
+ "clear_acrylic" => [0.8, 0.9, 0.95],
271
+ "vinyl_tile" => [0.75, 0.75, 0.72],
272
+ "carpet_tile" => [0.32, 0.35, 0.42],
273
+ "asphalt_concrete" => [0.18, 0.18, 0.18],
266
274
  _ => {
267
275
  log::warn!(
268
276
  "unknown material type: {type:#?}, using default color, i.e., black",
@@ -353,12 +361,19 @@ impl SionnaScene {
353
361
  /// Load a Sionna scene from a XML file.
354
362
  ///
355
363
  /// Args:
356
- /// file (str): The path to the XML file.
364
+ /// file (os.PathLike[str] | str): The path to the XML file.
357
365
  ///
358
366
  /// Returns:
359
367
  /// SionnaScene: The corresponding scene.
360
368
  #[classmethod]
361
- pub(crate) fn load_xml(_: &Bound<'_, PyType>, file: &str) -> PyResult<Self> {
369
+ #[pyo3(name = "load_xml")]
370
+ pub(crate) fn py_load_xml(_: &Bound<'_, PyType>, file: PathBuf) -> PyResult<Self> {
371
+ Self::load_xml(&file)
372
+ }
373
+ }
374
+
375
+ impl SionnaScene {
376
+ pub(crate) fn load_xml(file: &Path) -> PyResult<Self> {
362
377
  let input = BufReader::new(File::open(file)?);
363
378
  quick_xml::de::from_reader(input).map_err(|err| {
364
379
  PyValueError::new_err(format!(
@@ -662,6 +677,76 @@ mod tests {
662
677
  assert_eq!(material.color, [0.539, 0.027, 0.147]);
663
678
  }
664
679
 
680
+ #[test]
681
+ fn deserializes_itu_vacuum() {
682
+ let xml = r#"
683
+ <bsdf type="itu-radio-material" id="vacuum">
684
+ <string name="type" value="vacuum"/>
685
+ </bsdf>
686
+ "#;
687
+
688
+ let material: Material = quick_xml::de::from_str(xml).expect("material should parse");
689
+
690
+ assert_eq!(material.name, "itu_vacuum");
691
+ assert_eq!(material.color, [0.8, 0.8, 0.8]);
692
+ }
693
+
694
+ #[test]
695
+ fn deserializes_itu_clear_acrylic() {
696
+ let xml = r#"
697
+ <bsdf type="itu-radio-material" id="acrylic">
698
+ <string name="type" value="clear_acrylic"/>
699
+ </bsdf>
700
+ "#;
701
+
702
+ let material: Material = quick_xml::de::from_str(xml).expect("material should parse");
703
+
704
+ assert_eq!(material.name, "itu_clear_acrylic");
705
+ assert_eq!(material.color, [0.8, 0.9, 0.95]);
706
+ }
707
+
708
+ #[test]
709
+ fn deserializes_itu_vinyl_tile() {
710
+ let xml = r#"
711
+ <bsdf type="itu-radio-material" id="vinyl">
712
+ <string name="type" value="vinyl_tile"/>
713
+ </bsdf>
714
+ "#;
715
+
716
+ let material: Material = quick_xml::de::from_str(xml).expect("material should parse");
717
+
718
+ assert_eq!(material.name, "itu_vinyl_tile");
719
+ assert_eq!(material.color, [0.75, 0.75, 0.72]);
720
+ }
721
+
722
+ #[test]
723
+ fn deserializes_itu_carpet_tile() {
724
+ let xml = r#"
725
+ <bsdf type="itu-radio-material" id="carpet">
726
+ <string name="type" value="carpet_tile"/>
727
+ </bsdf>
728
+ "#;
729
+
730
+ let material: Material = quick_xml::de::from_str(xml).expect("material should parse");
731
+
732
+ assert_eq!(material.name, "itu_carpet_tile");
733
+ assert_eq!(material.color, [0.32, 0.35, 0.42]);
734
+ }
735
+
736
+ #[test]
737
+ fn deserializes_itu_asphalt_concrete() {
738
+ let xml = r#"
739
+ <bsdf type="itu-radio-material" id="asphalt">
740
+ <string name="type" value="asphalt_concrete"/>
741
+ </bsdf>
742
+ "#;
743
+
744
+ let material: Material = quick_xml::de::from_str(xml).expect("material should parse");
745
+
746
+ assert_eq!(material.name, "itu_asphalt_concrete");
747
+ assert_eq!(material.color, [0.18, 0.18, 0.18]);
748
+ }
749
+
665
750
  #[test]
666
751
  fn deserializes_itu_unknown_type() {
667
752
  let xml = r#"
@@ -1,8 +1,6 @@
1
1
  use pyo3::{prelude::*, wrap_pymodule};
2
2
 
3
3
  pub mod geometry;
4
- pub mod rt;
5
- pub mod scene;
6
4
 
7
5
  /// Core of DiffeRT module, implemented in Rust.
8
6
  #[cfg(not(tarpaulin_include))]
@@ -20,7 +18,5 @@ fn _differt_core(m: Bound<'_, PyModule>) -> PyResult<()> {
20
18
  );
21
19
  m.add("__version_info__", version_info)?;
22
20
  m.add_wrapped(wrap_pymodule!(geometry::geometry))?;
23
- m.add_wrapped(wrap_pymodule!(rt::rt))?;
24
- m.add_wrapped(wrap_pymodule!(scene::scene))?;
25
21
  Ok(())
26
22
  }
@@ -0,0 +1,11 @@
1
+ import pytest
2
+
3
+
4
+ def test_triangle_mesh_deprecated() -> None:
5
+ from differt_core.geometry import ( # ruff:ignore[import-outside-top-level]
6
+ TriangleMesh,
7
+ )
8
+
9
+ with pytest.warns(DeprecationWarning, match="TriangleMesh is deprecated"):
10
+ with pytest.raises(TypeError):
11
+ TriangleMesh()
@@ -0,0 +1,11 @@
1
+ import pytest
2
+
3
+
4
+ def test_triangle_scene_deprecated() -> None:
5
+ from differt_core.geometry import ( # ruff:ignore[import-outside-top-level]
6
+ TriangleScene,
7
+ )
8
+
9
+ with pytest.warns(DeprecationWarning, match="TriangleScene is deprecated"):
10
+ with pytest.raises(TypeError):
11
+ TriangleScene()
@@ -1,10 +1,9 @@
1
- import logging
2
1
  import re
3
2
 
4
3
  import numpy as np
5
4
  import pytest
6
5
 
7
- from differt_core.rt._graph import (
6
+ from differt_core.geometry._graph import (
8
7
  CompleteGraph,
9
8
  DiGraph,
10
9
  )
@@ -27,7 +26,7 @@ class TestDiGraph:
27
26
  TypeError,
28
27
  match="takes 0 positional arguments but 1 was given",
29
28
  ):
30
- _ = graph.insert_from_and_to_nodes(True) # noqa: FBT003
29
+ _ = graph.insert_from_and_to_nodes(True) # ruff:ignore[boolean-positional-value-in-call]
31
30
 
32
31
  @pytest.mark.parametrize("fast_mode", [True, False])
33
32
  def test_disconnect_nodes(self, fast_mode: bool) -> None:
@@ -154,7 +153,7 @@ class TestDiGraph:
154
153
  TypeError,
155
154
  match="takes 3 positional arguments but 4 were given",
156
155
  ):
157
- _ = graph.all_paths(0, 1, 0, True) # noqa: FBT003
156
+ _ = graph.all_paths(0, 1, 0, True) # ruff:ignore[boolean-positional-value-in-call]
158
157
 
159
158
  @pytest.mark.parametrize(
160
159
  ("num_nodes", "depth"),
@@ -197,29 +196,3 @@ class TestDiGraph:
197
196
  assert num_paths == num_nodes * (num_nodes - 1) ** (depth - 1)
198
197
  array = graph.all_paths_array(from_, to, depth + 2, include_from_and_to=False)
199
198
  assert array.shape == (num_paths, depth)
200
-
201
- @pytest.mark.parametrize(
202
- ("num_nodes", "depth"),
203
- [
204
- (10, 100),
205
- (50_000, 10),
206
- ],
207
- )
208
- def test_all_paths_count_from_complete_graph_overflow(
209
- self,
210
- num_nodes: int,
211
- depth: int,
212
- caplog: pytest.LogCaptureFixture,
213
- ) -> None:
214
- graph = CompleteGraph(num_nodes)
215
- from_, to = num_nodes, num_nodes + 1
216
-
217
- caplog.clear()
218
-
219
- with caplog.at_level(logging.WARNING):
220
- _ = graph.all_paths(from_, to, depth + 2, include_from_and_to=False)
221
-
222
- assert (
223
- "OverflowError: overflow occurred when computing the total number of paths"
224
- in caplog.text
225
- )
@@ -1,4 +1,3 @@
1
- # pyright: reportMissingTypeArgument=false
2
1
  from collections.abc import Iterator, Sized
3
2
 
4
3
  import numpy as np
@@ -1,8 +1,9 @@
1
- # pyright: reportMissingTypeArgument=false
1
+ from os import PathLike
2
+
2
3
  import numpy as np
3
4
  from jaxtyping import Float, Int, UInt
4
5
 
5
- class TriangleMesh:
6
+ class Mesh:
6
7
  vertices: Float[np.ndarray, "num_vertices 3"]
7
8
  triangles: UInt[np.ndarray, "num_triangles 3"]
8
9
  face_colors: Float[np.ndarray, "num_triangles 3"] | None
@@ -10,8 +11,8 @@ class TriangleMesh:
10
11
  material_names: list[str]
11
12
  object_bounds: UInt[np.ndarray, "num_objects 2"] | None
12
13
 
13
- def append(self, other: TriangleMesh) -> None: ...
14
+ def append(self, other: Mesh) -> None: ...
14
15
  @classmethod
15
- def load_obj(cls, file: str) -> TriangleMesh: ...
16
+ def load_obj(cls, file: str | PathLike[str]) -> Mesh: ...
16
17
  @classmethod
17
- def load_ply(cls, file: str) -> TriangleMesh: ...
18
+ def load_ply(cls, file: str | PathLike[str]) -> Mesh: ...
@@ -0,0 +1,9 @@
1
+ from os import PathLike
2
+
3
+ from differt_core.geometry import TriangleMesh
4
+
5
+ class Scene:
6
+ mesh: list[TriangleMesh]
7
+
8
+ @classmethod
9
+ def load_xml(cls, file: str | PathLike[str]) -> Scene: ...
@@ -1,9 +1,11 @@
1
+ from os import PathLike
2
+
1
3
  class SionnaScene:
2
4
  shapes: dict[str, Shape]
3
5
  materials: dict[str, Material]
4
6
 
5
7
  @classmethod
6
- def load_xml(cls, file: str) -> SionnaScene: ...
8
+ def load_xml(cls, file: str | PathLike[str]) -> SionnaScene: ...
7
9
 
8
10
  class Material:
9
11
  name: str
@@ -1,4 +1,4 @@
1
- """Ray Tracing utilities used by :mod:`differt.rt`."""
1
+ """Geometry utilities used by :mod:`differt.geometry`."""
2
2
 
3
3
  __all__ = (
4
4
  "AllPathsFromCompleteGraphChunksIter",
@@ -7,6 +7,13 @@ __all__ = (
7
7
  "AllPathsFromDiGraphIter",
8
8
  "CompleteGraph",
9
9
  "DiGraph",
10
+ "Material",
11
+ "Mesh",
12
+ "Scene",
13
+ "Shape",
14
+ "SionnaScene",
15
+ "TriangleMesh",
16
+ "TriangleScene",
10
17
  )
11
18
 
12
19
  from ._graph import (
@@ -17,3 +24,6 @@ from ._graph import (
17
24
  CompleteGraph,
18
25
  DiGraph,
19
26
  )
27
+ from ._mesh import Mesh, TriangleMesh
28
+ from ._scene import Scene, TriangleScene
29
+ from ._sionna import Material, Shape, SionnaScene
@@ -0,0 +1,23 @@
1
+ __all__ = (
2
+ "AllPathsFromCompleteGraphChunksIter",
3
+ "AllPathsFromCompleteGraphIter",
4
+ "AllPathsFromDiGraphChunksIter",
5
+ "AllPathsFromDiGraphIter",
6
+ "CompleteGraph",
7
+ "DiGraph",
8
+ )
9
+
10
+ from differt_core import _differt_core
11
+
12
+ AllPathsFromCompleteGraphChunksIter = (
13
+ _differt_core.geometry.graph.AllPathsFromCompleteGraphChunksIter
14
+ )
15
+ AllPathsFromCompleteGraphIter = (
16
+ _differt_core.geometry.graph.AllPathsFromCompleteGraphIter
17
+ )
18
+ AllPathsFromDiGraphChunksIter = (
19
+ _differt_core.geometry.graph.AllPathsFromDiGraphChunksIter
20
+ )
21
+ AllPathsFromDiGraphIter = _differt_core.geometry.graph.AllPathsFromDiGraphIter
22
+ CompleteGraph = _differt_core.geometry.graph.CompleteGraph
23
+ DiGraph = _differt_core.geometry.graph.DiGraph