differt-core 0.0.32__tar.gz → 0.0.34__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 (61) hide show
  1. {differt_core-0.0.32 → differt_core-0.0.34}/Cargo.lock +6 -5
  2. {differt_core-0.0.32 → differt_core-0.0.34}/PKG-INFO +1 -1
  3. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/Cargo.toml +3 -3
  4. {differt_core-0.0.32 → differt_core-0.0.34/differt-core}/python/differt_core/_differt_core/scene/sionna.pyi +3 -1
  5. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/geometry/triangle_mesh.rs +12 -7
  6. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/scene/sionna.rs +153 -42
  7. {differt_core-0.0.32 → differt_core-0.0.34/differt-core}/src/scene/triangle_scene.rs +6 -2
  8. {differt_core-0.0.32/differt-core → differt_core-0.0.34}/python/differt_core/_differt_core/scene/sionna.pyi +3 -1
  9. {differt_core-0.0.32 → differt_core-0.0.34}/src/geometry/triangle_mesh.rs +12 -7
  10. {differt_core-0.0.32 → differt_core-0.0.34}/src/scene/sionna.rs +153 -42
  11. {differt_core-0.0.32/differt-core → differt_core-0.0.34}/src/scene/triangle_scene.rs +6 -2
  12. {differt_core-0.0.32 → differt_core-0.0.34}/Cargo.toml +0 -0
  13. {differt_core-0.0.32 → differt_core-0.0.34}/LICENSE.md +0 -0
  14. {differt_core-0.0.32 → differt_core-0.0.34}/README.md +0 -0
  15. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/LICENSE.md +0 -0
  16. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/README.md +0 -0
  17. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/benches/bench_main.rs +0 -0
  18. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/benches/benchmarks/graph_iterators.rs +0 -0
  19. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/benches/benchmarks/mod.rs +0 -0
  20. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/__init__.py +0 -0
  21. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/_differt_core/__init__.pyi +0 -0
  22. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/_differt_core/geometry/triangle_mesh.pyi +0 -0
  23. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/_differt_core/rt/__init__.pyi +0 -0
  24. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/_differt_core/rt/graph.pyi +0 -0
  25. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/_differt_core/scene/triangle_scene.pyi +0 -0
  26. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/geometry/__init__.py +0 -0
  27. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/geometry/_triangle_mesh.py +0 -0
  28. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/py.typed +0 -0
  29. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/rt/__init__.py +0 -0
  30. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/rt/_graph.py +0 -0
  31. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/scene/__init__.py +0 -0
  32. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/scene/_sionna.py +0 -0
  33. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/python/differt_core/scene/_triangle_scene.py +0 -0
  34. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/geometry/mod.rs +0 -0
  35. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/lib.rs +0 -0
  36. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/rt/graph.rs +0 -0
  37. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/rt/mod.rs +0 -0
  38. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/src/scene/mod.rs +0 -0
  39. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/tests/__init__.py +0 -0
  40. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/tests/rt/__init__.py +0 -0
  41. {differt_core-0.0.32 → differt_core-0.0.34}/differt-core/tests/rt/test_graph.py +0 -0
  42. {differt_core-0.0.32 → differt_core-0.0.34}/pyproject.toml +0 -0
  43. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/__init__.py +0 -0
  44. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/_differt_core/__init__.pyi +0 -0
  45. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/_differt_core/geometry/triangle_mesh.pyi +0 -0
  46. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/_differt_core/rt/__init__.pyi +0 -0
  47. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/_differt_core/rt/graph.pyi +0 -0
  48. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/_differt_core/scene/triangle_scene.pyi +0 -0
  49. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/geometry/__init__.py +0 -0
  50. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/geometry/_triangle_mesh.py +0 -0
  51. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/py.typed +0 -0
  52. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/rt/__init__.py +0 -0
  53. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/rt/_graph.py +0 -0
  54. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/scene/__init__.py +0 -0
  55. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/scene/_sionna.py +0 -0
  56. {differt_core-0.0.32 → differt_core-0.0.34}/python/differt_core/scene/_triangle_scene.py +0 -0
  57. {differt_core-0.0.32 → differt_core-0.0.34}/src/geometry/mod.rs +0 -0
  58. {differt_core-0.0.32 → differt_core-0.0.34}/src/lib.rs +0 -0
  59. {differt_core-0.0.32 → differt_core-0.0.34}/src/rt/graph.rs +0 -0
  60. {differt_core-0.0.32 → differt_core-0.0.34}/src/rt/mod.rs +0 -0
  61. {differt_core-0.0.32 → differt_core-0.0.34}/src/scene/mod.rs +0 -0
@@ -247,7 +247,7 @@ checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
247
247
 
248
248
  [[package]]
249
249
  name = "differt-core"
250
- version = "0.0.32"
250
+ version = "0.0.34"
251
251
  dependencies = [
252
252
  "criterion",
253
253
  "indexmap",
@@ -825,8 +825,9 @@ dependencies = [
825
825
 
826
826
  [[package]]
827
827
  name = "pyo3-log"
828
- version = "0.13.0"
829
- source = "git+https://github.com/vorner/pyo3-log.git?rev=refs/pull/62/head#5fdd313653dcb3fb3297180d1b7ca3564a0e18aa"
828
+ version = "0.12.2"
829
+ source = "registry+https://github.com/rust-lang/crates.io-index"
830
+ checksum = "4b78e4983ba15bc62833a0e0941d965bc03690163f1127864f1408db25063466"
830
831
  dependencies = [
831
832
  "arc-swap",
832
833
  "log",
@@ -869,9 +870,9 @@ dependencies = [
869
870
 
870
871
  [[package]]
871
872
  name = "quick-xml"
872
- version = "0.31.0"
873
+ version = "0.37.2"
873
874
  source = "registry+https://github.com/rust-lang/crates.io-index"
874
- checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
875
+ checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003"
875
876
  dependencies = [
876
877
  "memchr",
877
878
  "serde",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: differt-core
3
- Version: 0.0.32
3
+ Version: 0.0.34
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -11,8 +11,8 @@ numpy = "0.24"
11
11
  obj-rs = "0.7.1"
12
12
  ply-rs = "0.1.3"
13
13
  pyo3 = {version = "0.24", features = ["abi3-py310", "indexmap", "generate-import-lib"]}
14
- pyo3-log = {version = "0.13", git = "https://github.com/vorner/pyo3-log.git", rev = "refs/pull/62/head"}
15
- quick-xml = {version = "0.31.0", features = ["serialize"]}
14
+ pyo3-log = "0.12.2"
15
+ quick-xml = {version = "0.37.2", features = ["serialize", "serde-types"]}
16
16
  serde = {version = "1.0.197", features = ["derive"]}
17
17
 
18
18
  [dev-dependencies]
@@ -37,4 +37,4 @@ 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.0.32"
40
+ version = "0.0.34"
@@ -6,8 +6,10 @@ class SionnaScene:
6
6
  def load_xml(cls, file: str) -> SionnaScene: ...
7
7
 
8
8
  class Material:
9
+ name: str
9
10
  id: str
10
- rgb: tuple[float, float, float]
11
+ color: tuple[float, float, float]
12
+ thickness: float | None
11
13
 
12
14
  class Shape:
13
15
  type: str
@@ -281,7 +281,10 @@ impl TriangleMesh {
281
281
  pub(crate) fn load_obj(_: &Bound<'_, PyType>, filename: &str) -> PyResult<Self> {
282
282
  let input = BufReader::new(File::open(filename)?);
283
283
  let mut obj: RawObj = parse_obj(input).map_err(|err| {
284
- PyValueError::new_err(format!("An error occurred while reading obj file: {}", err))
284
+ PyValueError::new_err(format!(
285
+ "An error occurred while reading obj file {filename:#?}: {}",
286
+ err
287
+ ))
285
288
  })?;
286
289
 
287
290
  for material_file in obj.material_libraries.iter_mut() {
@@ -317,7 +320,7 @@ impl TriangleMesh {
317
320
 
318
321
  let header = vertex_parser.read_header(&mut input).map_err(|err| {
319
322
  PyValueError::new_err(format!(
320
- "An error occurred while reading the header of ply file: {}",
323
+ "An error occurred while reading the header of ply file {filename:#?}: {}",
321
324
  err
322
325
  ))
323
326
  })?;
@@ -334,7 +337,7 @@ impl TriangleMesh {
334
337
  .map_err(|err| {
335
338
  PyValueError::new_err(format!(
336
339
  "An error occurred while reading the vertex elements of ply \
337
- file: {}",
340
+ file {filename:#?}: {}",
338
341
  err
339
342
  ))
340
343
  })?
@@ -349,7 +352,7 @@ impl TriangleMesh {
349
352
  .map_err(|err| {
350
353
  PyValueError::new_err(format!(
351
354
  "An error occurred while reading the face elements of PLY \
352
- file: {}",
355
+ file {filename:#?}: {}",
353
356
  err
354
357
  ))
355
358
  })?
@@ -423,14 +426,16 @@ impl From<RawObj> for TriangleMesh {
423
426
  },
424
427
  Err(e) => {
425
428
  log::warn!(
426
- "An error occurred when parsing MTL file {material_file}: {e}."
429
+ "An error occurred when parsing MTL file {material_file:#?}: \
430
+ {e}."
427
431
  );
428
432
  },
429
433
  }
430
434
  },
431
435
  Err(e) => {
432
436
  log::warn!(
433
- "An error occurred when trying to read MTL file {material_file}: {e}."
437
+ "An error occurred when trying to read MTL file {material_file:#?}: \
438
+ {e}."
434
439
  );
435
440
  },
436
441
  }
@@ -460,7 +465,7 @@ impl From<RawObj> for TriangleMesh {
460
465
  }
461
466
  } else {
462
467
  log::warn!(
463
- "Material {material_name} was listed in OBJ material libraries, but is \
468
+ "Material {material_name:#?} was listed in OBJ material libraries, but is \
464
469
  not present is mesh groups."
465
470
  );
466
471
  }
@@ -74,12 +74,47 @@ where
74
74
  #[pyclass(get_all)]
75
75
  #[derive(Clone, Debug, Default)]
76
76
  pub(crate) struct Material {
77
- /// str: The material ID.
77
+ /// str: The material name.
78
78
  ///
79
79
  /// This can be, e.g., an ITU identifier.
80
+ pub(crate) name: String,
81
+ /// str: The material ID.
82
+ ///
83
+ /// This is used to match the material id used in shapes.
80
84
  pub(crate) id: String,
81
- /// tuple[float, float, float]: The material color, used when plotted.
82
- pub(crate) rgb: [f32; 3],
85
+ /// tuple[float, float, float]: The material color, used for plotting.
86
+ ///
87
+ /// The color is obtained from a---possibly-nested---``<rgb>`` element,
88
+ /// or from a ``<string>`` element with a ``type`` attribute. In the latter
89
+ /// case, the color is chosen from a predefined list of colors.
90
+ pub(crate) color: [f32; 3],
91
+ /// typing.Optional[float]: The thickness of the material.
92
+ pub(crate) thickness: Option<f32>,
93
+ }
94
+
95
+ fn deserialize_rgb<'de, D>(deserializer: D) -> Result<[f32; 3], D::Error>
96
+ where
97
+ D: de::Deserializer<'de>,
98
+ {
99
+ let value = <String>::deserialize(deserializer)?;
100
+
101
+ match value
102
+ .split_ascii_whitespace()
103
+ .collect::<Vec<_>>()
104
+ .as_slice()
105
+ {
106
+ [r_str, g_str, b_str] => {
107
+ let r = r_str.parse().map_err(de::Error::custom)?;
108
+ let g = g_str.parse().map_err(de::Error::custom)?;
109
+ let b = b_str.parse().map_err(de::Error::custom)?;
110
+ Ok([r, g, b])
111
+ },
112
+ _ => {
113
+ Err(de::Error::custom(
114
+ "value of <rgb> element must contain three floats",
115
+ ))
116
+ },
117
+ }
83
118
  }
84
119
 
85
120
  impl<'de> Deserialize<'de> for Material {
@@ -87,56 +122,129 @@ impl<'de> Deserialize<'de> for Material {
87
122
  where
88
123
  D: de::Deserializer<'de>,
89
124
  {
90
- #[derive(Deserialize)]
91
- struct RawMaterial {
92
- #[serde(rename(deserialize = "@id"))]
93
- id: String,
94
- #[serde(rename = "$value")]
95
- rgb: PossiblyNestedRgb,
96
- }
97
-
98
- #[derive(Deserialize)]
125
+ #[derive(Debug, Deserialize)]
99
126
  struct Rgb {
100
- #[serde(rename(deserialize = "@value"))]
101
- value: String,
127
+ #[serde(rename(deserialize = "@value"), deserialize_with = "deserialize_rgb")]
128
+ color: [f32; 3],
102
129
  }
103
130
 
104
- #[derive(Deserialize)]
131
+ #[derive(Debug, Deserialize)]
105
132
  #[serde(rename_all = "snake_case")]
106
- enum PossiblyNestedRgb {
107
- Rgb {
108
- #[serde(rename(deserialize = "@value"))]
109
- value: String,
133
+ struct Bsdf {
134
+ #[serde(rename = "rgb")]
135
+ rgb: Rgb,
136
+ }
137
+
138
+ #[derive(Debug)]
139
+ enum Type {
140
+ Struct { value: String },
141
+ }
142
+
143
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
144
+ Type, "@name",
145
+ ("type" => Struct {
146
+ #[serde(rename = "@value")]
147
+ value: String }),
148
+ }
149
+
150
+ #[derive(Debug)]
151
+ enum Thickness {
152
+ Struct { value: f32 },
153
+ }
154
+
155
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
156
+ Thickness, "@name",
157
+ ("thickness" => Struct {
158
+ #[serde(rename = "@value")]
159
+ value: f32,
160
+ }),
161
+ }
162
+
163
+ #[derive(Debug)]
164
+ enum RawMaterial {
165
+ TwoSided {
166
+ id: String,
167
+ bsdf: Bsdf,
168
+ //thickness: Option<f32>,
110
169
  },
111
- Bsdf {
112
- rgb: Rgb,
170
+ ItuRadioMaterial {
171
+ id: String,
172
+ r#type: Type,
173
+ thickness: Option<Thickness>,
113
174
  },
114
175
  }
115
176
 
177
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
178
+ RawMaterial, "@type",
179
+ ("twosided" => TwoSided {
180
+ #[serde(rename = "@id")]
181
+ id: String,
182
+ bsdf: Bsdf,
183
+ }),
184
+ ("itu-radio-material" => ItuRadioMaterial {
185
+ #[serde(rename = "@id")]
186
+ id: String,
187
+ #[serde(rename = "string")]
188
+ r#type: Type,
189
+ #[serde(skip)]
190
+ thickness: Option<Thickness>,
191
+ }),
192
+ }
193
+
116
194
  let raw_mat = RawMaterial::deserialize(deserializer)?;
117
195
 
118
- let (id, rgb) = match raw_mat {
119
- RawMaterial {
120
- id,
121
- rgb: PossiblyNestedRgb::Rgb { value },
122
- } => (id, value),
123
- RawMaterial {
196
+ match raw_mat {
197
+ RawMaterial::TwoSided {
124
198
  id,
125
- rgb: PossiblyNestedRgb::Bsdf { rgb: Rgb { value } },
126
- } => (id, value),
127
- };
128
-
129
- match rgb.split_ascii_whitespace().collect::<Vec<_>>().as_slice() {
130
- [r_str, g_str, b_str] => {
131
- let r = r_str.parse().map_err(de::Error::custom)?;
132
- let g = g_str.parse().map_err(de::Error::custom)?;
133
- let b = b_str.parse().map_err(de::Error::custom)?;
134
- Ok(Material { id, rgb: [r, g, b] })
199
+ bsdf: Bsdf { rgb: Rgb { color } },
200
+ } => {
201
+ Ok(Material {
202
+ name: id.strip_prefix("mat-").unwrap_or(&id).to_string(),
203
+ id,
204
+ color,
205
+ thickness: None,
206
+ })
135
207
  },
136
- _ => {
137
- Err(de::Error::custom(
138
- "value of <rgb> element must contain three floats",
139
- ))
208
+ RawMaterial::ItuRadioMaterial {
209
+ id,
210
+ r#type: Type::Struct { value: r#type },
211
+ thickness,
212
+ } => {
213
+ let color = match r#type.as_str() {
214
+ // Copied from Sionna-RT's code, to match their colors:
215
+ // https://github.com/NVlabs/sionna-rt/blob/main/src/sionna/rt/radio_materials/itu_material.py
216
+ // v1.0.0
217
+ "marble" => [0.701, 0.644, 0.485],
218
+ "concrete" => [0.539, 0.539, 0.539],
219
+ "wood" => [0.266, 0.109, 0.060],
220
+ "metal" => [0.220, 0.220, 0.254],
221
+ "brick" => [0.402, 0.112, 0.087],
222
+ "glass" => [0.168, 0.139, 0.509],
223
+ "floorboard" => [0.539, 0.386, 0.025],
224
+ "ceiling_board" => [0.376, 0.539, 0.117],
225
+ "chipboard" => [0.509, 0.159, 0.323],
226
+ "plasterboard" => [0.051, 0.539, 0.133],
227
+ "plywood" => [0.136, 0.076, 0.539],
228
+ "very_dry_ground" => [0.539, 0.319, 0.223],
229
+ "medium_dry_ground" => [0.539, 0.181, 0.076],
230
+ "wet_ground" => [0.539, 0.027, 0.147],
231
+ _ => {
232
+ log::warn!(
233
+ "unknown material type: {type:#?}, using default color, i.e., black",
234
+ );
235
+ [0.0, 0.0, 0.0]
236
+ },
237
+ };
238
+ Ok(Material {
239
+ name: format!("itu_{type}"),
240
+ id,
241
+ color,
242
+ thickness: thickness.map(|t| {
243
+ match t {
244
+ Thickness::Struct { value: thickness } => thickness,
245
+ }
246
+ }),
247
+ })
140
248
  },
141
249
  }
142
250
  }
@@ -218,7 +326,10 @@ impl SionnaScene {
218
326
  pub(crate) fn load_xml(_: &Bound<'_, PyType>, file: &str) -> PyResult<Self> {
219
327
  let input = BufReader::new(File::open(file)?);
220
328
  quick_xml::de::from_reader(input).map_err(|err| {
221
- PyValueError::new_err(format!("An error occurred while reading XML file: {}", err))
329
+ PyValueError::new_err(format!(
330
+ "An error occurred while reading XML file {file:#?}: {}",
331
+ err
332
+ ))
222
333
  })
223
334
  }
224
335
  }
@@ -60,10 +60,14 @@ impl TriangleScene {
60
60
  },
61
61
  };
62
62
 
63
- let color = sionna.materials.get(&shape.material_id).map(|mat| mat.rgb);
63
+ let material = sionna.materials.get(&shape.material_id);
64
+
65
+ let color = material.map(|mat| mat.color);
66
+
67
+ let material_name = material.map(|mat| mat.name.clone());
64
68
 
65
69
  other_mesh.set_face_color(color.as_ref());
66
- other_mesh.set_face_material(Some(shape.material_id));
70
+ other_mesh.set_face_material(material_name);
67
71
 
68
72
  mesh.append(&mut other_mesh);
69
73
  }
@@ -6,8 +6,10 @@ class SionnaScene:
6
6
  def load_xml(cls, file: str) -> SionnaScene: ...
7
7
 
8
8
  class Material:
9
+ name: str
9
10
  id: str
10
- rgb: tuple[float, float, float]
11
+ color: tuple[float, float, float]
12
+ thickness: float | None
11
13
 
12
14
  class Shape:
13
15
  type: str
@@ -281,7 +281,10 @@ impl TriangleMesh {
281
281
  pub(crate) fn load_obj(_: &Bound<'_, PyType>, filename: &str) -> PyResult<Self> {
282
282
  let input = BufReader::new(File::open(filename)?);
283
283
  let mut obj: RawObj = parse_obj(input).map_err(|err| {
284
- PyValueError::new_err(format!("An error occurred while reading obj file: {}", err))
284
+ PyValueError::new_err(format!(
285
+ "An error occurred while reading obj file {filename:#?}: {}",
286
+ err
287
+ ))
285
288
  })?;
286
289
 
287
290
  for material_file in obj.material_libraries.iter_mut() {
@@ -317,7 +320,7 @@ impl TriangleMesh {
317
320
 
318
321
  let header = vertex_parser.read_header(&mut input).map_err(|err| {
319
322
  PyValueError::new_err(format!(
320
- "An error occurred while reading the header of ply file: {}",
323
+ "An error occurred while reading the header of ply file {filename:#?}: {}",
321
324
  err
322
325
  ))
323
326
  })?;
@@ -334,7 +337,7 @@ impl TriangleMesh {
334
337
  .map_err(|err| {
335
338
  PyValueError::new_err(format!(
336
339
  "An error occurred while reading the vertex elements of ply \
337
- file: {}",
340
+ file {filename:#?}: {}",
338
341
  err
339
342
  ))
340
343
  })?
@@ -349,7 +352,7 @@ impl TriangleMesh {
349
352
  .map_err(|err| {
350
353
  PyValueError::new_err(format!(
351
354
  "An error occurred while reading the face elements of PLY \
352
- file: {}",
355
+ file {filename:#?}: {}",
353
356
  err
354
357
  ))
355
358
  })?
@@ -423,14 +426,16 @@ impl From<RawObj> for TriangleMesh {
423
426
  },
424
427
  Err(e) => {
425
428
  log::warn!(
426
- "An error occurred when parsing MTL file {material_file}: {e}."
429
+ "An error occurred when parsing MTL file {material_file:#?}: \
430
+ {e}."
427
431
  );
428
432
  },
429
433
  }
430
434
  },
431
435
  Err(e) => {
432
436
  log::warn!(
433
- "An error occurred when trying to read MTL file {material_file}: {e}."
437
+ "An error occurred when trying to read MTL file {material_file:#?}: \
438
+ {e}."
434
439
  );
435
440
  },
436
441
  }
@@ -460,7 +465,7 @@ impl From<RawObj> for TriangleMesh {
460
465
  }
461
466
  } else {
462
467
  log::warn!(
463
- "Material {material_name} was listed in OBJ material libraries, but is \
468
+ "Material {material_name:#?} was listed in OBJ material libraries, but is \
464
469
  not present is mesh groups."
465
470
  );
466
471
  }
@@ -74,12 +74,47 @@ where
74
74
  #[pyclass(get_all)]
75
75
  #[derive(Clone, Debug, Default)]
76
76
  pub(crate) struct Material {
77
- /// str: The material ID.
77
+ /// str: The material name.
78
78
  ///
79
79
  /// This can be, e.g., an ITU identifier.
80
+ pub(crate) name: String,
81
+ /// str: The material ID.
82
+ ///
83
+ /// This is used to match the material id used in shapes.
80
84
  pub(crate) id: String,
81
- /// tuple[float, float, float]: The material color, used when plotted.
82
- pub(crate) rgb: [f32; 3],
85
+ /// tuple[float, float, float]: The material color, used for plotting.
86
+ ///
87
+ /// The color is obtained from a---possibly-nested---``<rgb>`` element,
88
+ /// or from a ``<string>`` element with a ``type`` attribute. In the latter
89
+ /// case, the color is chosen from a predefined list of colors.
90
+ pub(crate) color: [f32; 3],
91
+ /// typing.Optional[float]: The thickness of the material.
92
+ pub(crate) thickness: Option<f32>,
93
+ }
94
+
95
+ fn deserialize_rgb<'de, D>(deserializer: D) -> Result<[f32; 3], D::Error>
96
+ where
97
+ D: de::Deserializer<'de>,
98
+ {
99
+ let value = <String>::deserialize(deserializer)?;
100
+
101
+ match value
102
+ .split_ascii_whitespace()
103
+ .collect::<Vec<_>>()
104
+ .as_slice()
105
+ {
106
+ [r_str, g_str, b_str] => {
107
+ let r = r_str.parse().map_err(de::Error::custom)?;
108
+ let g = g_str.parse().map_err(de::Error::custom)?;
109
+ let b = b_str.parse().map_err(de::Error::custom)?;
110
+ Ok([r, g, b])
111
+ },
112
+ _ => {
113
+ Err(de::Error::custom(
114
+ "value of <rgb> element must contain three floats",
115
+ ))
116
+ },
117
+ }
83
118
  }
84
119
 
85
120
  impl<'de> Deserialize<'de> for Material {
@@ -87,56 +122,129 @@ impl<'de> Deserialize<'de> for Material {
87
122
  where
88
123
  D: de::Deserializer<'de>,
89
124
  {
90
- #[derive(Deserialize)]
91
- struct RawMaterial {
92
- #[serde(rename(deserialize = "@id"))]
93
- id: String,
94
- #[serde(rename = "$value")]
95
- rgb: PossiblyNestedRgb,
96
- }
97
-
98
- #[derive(Deserialize)]
125
+ #[derive(Debug, Deserialize)]
99
126
  struct Rgb {
100
- #[serde(rename(deserialize = "@value"))]
101
- value: String,
127
+ #[serde(rename(deserialize = "@value"), deserialize_with = "deserialize_rgb")]
128
+ color: [f32; 3],
102
129
  }
103
130
 
104
- #[derive(Deserialize)]
131
+ #[derive(Debug, Deserialize)]
105
132
  #[serde(rename_all = "snake_case")]
106
- enum PossiblyNestedRgb {
107
- Rgb {
108
- #[serde(rename(deserialize = "@value"))]
109
- value: String,
133
+ struct Bsdf {
134
+ #[serde(rename = "rgb")]
135
+ rgb: Rgb,
136
+ }
137
+
138
+ #[derive(Debug)]
139
+ enum Type {
140
+ Struct { value: String },
141
+ }
142
+
143
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
144
+ Type, "@name",
145
+ ("type" => Struct {
146
+ #[serde(rename = "@value")]
147
+ value: String }),
148
+ }
149
+
150
+ #[derive(Debug)]
151
+ enum Thickness {
152
+ Struct { value: f32 },
153
+ }
154
+
155
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
156
+ Thickness, "@name",
157
+ ("thickness" => Struct {
158
+ #[serde(rename = "@value")]
159
+ value: f32,
160
+ }),
161
+ }
162
+
163
+ #[derive(Debug)]
164
+ enum RawMaterial {
165
+ TwoSided {
166
+ id: String,
167
+ bsdf: Bsdf,
168
+ //thickness: Option<f32>,
110
169
  },
111
- Bsdf {
112
- rgb: Rgb,
170
+ ItuRadioMaterial {
171
+ id: String,
172
+ r#type: Type,
173
+ thickness: Option<Thickness>,
113
174
  },
114
175
  }
115
176
 
177
+ quick_xml::impl_deserialize_for_internally_tagged_enum! {
178
+ RawMaterial, "@type",
179
+ ("twosided" => TwoSided {
180
+ #[serde(rename = "@id")]
181
+ id: String,
182
+ bsdf: Bsdf,
183
+ }),
184
+ ("itu-radio-material" => ItuRadioMaterial {
185
+ #[serde(rename = "@id")]
186
+ id: String,
187
+ #[serde(rename = "string")]
188
+ r#type: Type,
189
+ #[serde(skip)]
190
+ thickness: Option<Thickness>,
191
+ }),
192
+ }
193
+
116
194
  let raw_mat = RawMaterial::deserialize(deserializer)?;
117
195
 
118
- let (id, rgb) = match raw_mat {
119
- RawMaterial {
120
- id,
121
- rgb: PossiblyNestedRgb::Rgb { value },
122
- } => (id, value),
123
- RawMaterial {
196
+ match raw_mat {
197
+ RawMaterial::TwoSided {
124
198
  id,
125
- rgb: PossiblyNestedRgb::Bsdf { rgb: Rgb { value } },
126
- } => (id, value),
127
- };
128
-
129
- match rgb.split_ascii_whitespace().collect::<Vec<_>>().as_slice() {
130
- [r_str, g_str, b_str] => {
131
- let r = r_str.parse().map_err(de::Error::custom)?;
132
- let g = g_str.parse().map_err(de::Error::custom)?;
133
- let b = b_str.parse().map_err(de::Error::custom)?;
134
- Ok(Material { id, rgb: [r, g, b] })
199
+ bsdf: Bsdf { rgb: Rgb { color } },
200
+ } => {
201
+ Ok(Material {
202
+ name: id.strip_prefix("mat-").unwrap_or(&id).to_string(),
203
+ id,
204
+ color,
205
+ thickness: None,
206
+ })
135
207
  },
136
- _ => {
137
- Err(de::Error::custom(
138
- "value of <rgb> element must contain three floats",
139
- ))
208
+ RawMaterial::ItuRadioMaterial {
209
+ id,
210
+ r#type: Type::Struct { value: r#type },
211
+ thickness,
212
+ } => {
213
+ let color = match r#type.as_str() {
214
+ // Copied from Sionna-RT's code, to match their colors:
215
+ // https://github.com/NVlabs/sionna-rt/blob/main/src/sionna/rt/radio_materials/itu_material.py
216
+ // v1.0.0
217
+ "marble" => [0.701, 0.644, 0.485],
218
+ "concrete" => [0.539, 0.539, 0.539],
219
+ "wood" => [0.266, 0.109, 0.060],
220
+ "metal" => [0.220, 0.220, 0.254],
221
+ "brick" => [0.402, 0.112, 0.087],
222
+ "glass" => [0.168, 0.139, 0.509],
223
+ "floorboard" => [0.539, 0.386, 0.025],
224
+ "ceiling_board" => [0.376, 0.539, 0.117],
225
+ "chipboard" => [0.509, 0.159, 0.323],
226
+ "plasterboard" => [0.051, 0.539, 0.133],
227
+ "plywood" => [0.136, 0.076, 0.539],
228
+ "very_dry_ground" => [0.539, 0.319, 0.223],
229
+ "medium_dry_ground" => [0.539, 0.181, 0.076],
230
+ "wet_ground" => [0.539, 0.027, 0.147],
231
+ _ => {
232
+ log::warn!(
233
+ "unknown material type: {type:#?}, using default color, i.e., black",
234
+ );
235
+ [0.0, 0.0, 0.0]
236
+ },
237
+ };
238
+ Ok(Material {
239
+ name: format!("itu_{type}"),
240
+ id,
241
+ color,
242
+ thickness: thickness.map(|t| {
243
+ match t {
244
+ Thickness::Struct { value: thickness } => thickness,
245
+ }
246
+ }),
247
+ })
140
248
  },
141
249
  }
142
250
  }
@@ -218,7 +326,10 @@ impl SionnaScene {
218
326
  pub(crate) fn load_xml(_: &Bound<'_, PyType>, file: &str) -> PyResult<Self> {
219
327
  let input = BufReader::new(File::open(file)?);
220
328
  quick_xml::de::from_reader(input).map_err(|err| {
221
- PyValueError::new_err(format!("An error occurred while reading XML file: {}", err))
329
+ PyValueError::new_err(format!(
330
+ "An error occurred while reading XML file {file:#?}: {}",
331
+ err
332
+ ))
222
333
  })
223
334
  }
224
335
  }
@@ -60,10 +60,14 @@ impl TriangleScene {
60
60
  },
61
61
  };
62
62
 
63
- let color = sionna.materials.get(&shape.material_id).map(|mat| mat.rgb);
63
+ let material = sionna.materials.get(&shape.material_id);
64
+
65
+ let color = material.map(|mat| mat.color);
66
+
67
+ let material_name = material.map(|mat| mat.name.clone());
64
68
 
65
69
  other_mesh.set_face_color(color.as_ref());
66
- other_mesh.set_face_material(Some(shape.material_id));
70
+ other_mesh.set_face_material(material_name);
67
71
 
68
72
  mesh.append(&mut other_mesh);
69
73
  }
File without changes
File without changes
File without changes
File without changes