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.
- {differt_core-0.9.1 → differt_core-0.11.0}/Cargo.lock +1 -1
- {differt_core-0.9.1 → differt_core-0.11.0}/PKG-INFO +1 -1
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/Cargo.toml +1 -1
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/benchmarks/graph_iterators.rs +1 -1
- 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
- differt_core-0.11.0/differt-core/src/geometry/mod.rs +16 -0
- differt_core-0.11.0/differt-core/src/geometry/scene.rs +78 -0
- {differt_core-0.9.1/src/scene → differt_core-0.11.0/differt-core/src/geometry}/sionna.rs +92 -7
- {differt_core-0.9.1 → differt_core-0.11.0/differt-core}/src/lib.rs +0 -4
- differt_core-0.11.0/differt-core/tests/geometry/test_core_mesh.py +11 -0
- differt_core-0.11.0/differt-core/tests/geometry/test_core_scene.py +11 -0
- {differt_core-0.9.1/differt-core/tests/rt → differt_core-0.11.0/differt-core/tests/geometry}/test_graph.py +3 -30
- {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
- 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
- differt_core-0.11.0/python/differt_core/_differt_core/geometry/scene.pyi +9 -0
- {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
- {differt_core-0.9.1/python/differt_core/rt → differt_core-0.11.0/python/differt_core/geometry}/__init__.py +11 -1
- differt_core-0.11.0/python/differt_core/geometry/_graph.py +23 -0
- differt_core-0.11.0/python/differt_core/geometry/_mesh.py +28 -0
- differt_core-0.11.0/python/differt_core/geometry/_scene.py +27 -0
- differt_core-0.11.0/python/differt_core/geometry/_sionna.py +7 -0
- differt_core-0.9.1/src/geometry/triangle_mesh.rs → differt_core-0.11.0/src/geometry/mesh.rs +52 -36
- differt_core-0.11.0/src/geometry/mod.rs +16 -0
- differt_core-0.11.0/src/geometry/scene.rs +78 -0
- {differt_core-0.9.1/differt-core/src/scene → differt_core-0.11.0/src/geometry}/sionna.rs +92 -7
- {differt_core-0.9.1/differt-core → differt_core-0.11.0}/src/lib.rs +0 -4
- differt_core-0.9.1/differt-core/src/geometry/mod.rs +0 -10
- differt_core-0.9.1/differt-core/src/rt/mod.rs +0 -10
- differt_core-0.9.1/differt-core/src/scene/mod.rs +0 -12
- differt_core-0.9.1/differt-core/src/scene/triangle_scene.rs +0 -83
- differt_core-0.9.1/python/differt_core/_differt_core/rt/__init__.pyi +0 -0
- differt_core-0.9.1/python/differt_core/_differt_core/scene/triangle_scene.pyi +0 -7
- differt_core-0.9.1/python/differt_core/geometry/__init__.py +0 -5
- differt_core-0.9.1/python/differt_core/geometry/_triangle_mesh.py +0 -5
- differt_core-0.9.1/python/differt_core/rt/_graph.py +0 -19
- differt_core-0.9.1/python/differt_core/scene/__init__.py +0 -6
- differt_core-0.9.1/python/differt_core/scene/_sionna.py +0 -7
- differt_core-0.9.1/python/differt_core/scene/_triangle_scene.py +0 -5
- differt_core-0.9.1/src/geometry/mod.rs +0 -10
- differt_core-0.9.1/src/rt/mod.rs +0 -10
- differt_core-0.9.1/src/scene/mod.rs +0 -12
- differt_core-0.9.1/src/scene/triangle_scene.rs +0 -83
- {differt_core-0.9.1 → differt_core-0.11.0}/Cargo.toml +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/LICENSE.md +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/README.md +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/LICENSE.md +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/README.md +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/bench_main.rs +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/benchmarks/mod.rs +0 -0
- {differt_core-0.9.1/differt-core/src/rt → differt_core-0.11.0/differt-core/src/geometry}/graph.rs +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/tests/__init__.py +0 -0
- {differt_core-0.9.1/differt-core/tests/rt → differt_core-0.11.0/differt-core/tests/geometry}/__init__.py +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/differt-core/tests/test_version.py +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/pyproject.toml +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/__init__.py +2 -2
- {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/_differt_core/__init__.pyi +0 -0
- {differt_core-0.9.1 → differt_core-0.11.0}/python/differt_core/py.typed +0 -0
- {differt_core-0.9.1/src/rt → differt_core-0.11.0/src/geometry}/graph.rs +0 -0
{differt_core-0.9.1 → differt_core-0.11.0}/differt-core/benches/benchmarks/graph_iterators.rs
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use criterion::{Criterion, Throughput, black_box, criterion_group};
|
|
2
|
-
use differt_core::
|
|
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::{
|
|
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.
|
|
16
|
+
/// :class:`differt.geometry.Mesh` instead.
|
|
13
17
|
#[derive(Clone, Debug, Default)]
|
|
14
|
-
#[pyclass]
|
|
15
|
-
pub(crate) struct
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
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
|
-
///
|
|
283
|
+
/// Mesh: The corresponding mesh containing only triangles.
|
|
280
284
|
#[classmethod]
|
|
281
|
-
|
|
282
|
-
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
507
|
-
m.add_class::<
|
|
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::{
|
|
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
|
|
12
|
-
/// to
|
|
13
|
-
/// :class:`
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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) #
|
|
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) #
|
|
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,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
from os import PathLike
|
|
2
|
+
|
|
2
3
|
import numpy as np
|
|
3
4
|
from jaxtyping import Float, Int, UInt
|
|
4
5
|
|
|
5
|
-
class
|
|
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:
|
|
14
|
+
def append(self, other: Mesh) -> None: ...
|
|
14
15
|
@classmethod
|
|
15
|
-
def load_obj(cls, file: str) ->
|
|
16
|
+
def load_obj(cls, file: str | PathLike[str]) -> Mesh: ...
|
|
16
17
|
@classmethod
|
|
17
|
-
def load_ply(cls, file: str) ->
|
|
18
|
+
def load_ply(cls, file: str | PathLike[str]) -> Mesh: ...
|
|
@@ -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
|
-
"""
|
|
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
|