eulumdat 0.2.1__tar.gz → 0.2.2__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 (39) hide show
  1. {eulumdat-0.2.1 → eulumdat-0.2.2}/Cargo.lock +25 -9
  2. {eulumdat-0.2.1 → eulumdat-0.2.2}/Cargo.toml +2 -2
  3. {eulumdat-0.2.1 → eulumdat-0.2.2}/PKG-INFO +1 -1
  4. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/eulumdat.rs +37 -0
  5. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/symmetry.rs +70 -0
  6. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/Cargo.toml +5 -5
  7. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/types.rs +33 -0
  8. {eulumdat-0.2.1 → eulumdat-0.2.2}/pyproject.toml +1 -1
  9. {eulumdat-0.2.1 → eulumdat-0.2.2}/README.md +0 -0
  10. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/Cargo.toml +0 -0
  11. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/README.md +0 -0
  12. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/examples/generate_watchface.rs +0 -0
  13. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/examples/parse_road.rs +0 -0
  14. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/batch.rs +0 -0
  15. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/bug_rating.rs +0 -0
  16. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/calculations.rs +0 -0
  17. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/butterfly.rs +0 -0
  18. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/cartesian.rs +0 -0
  19. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/color.rs +0 -0
  20. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/heatmap.rs +0 -0
  21. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/mod.rs +0 -0
  22. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/polar.rs +0 -0
  23. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/projection.rs +0 -0
  24. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/svg.rs +0 -0
  25. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/diagram/watchface.rs +0 -0
  26. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/error.rs +0 -0
  27. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/ies.rs +0 -0
  28. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/lib.rs +0 -0
  29. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/parser.rs +0 -0
  30. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/validation.rs +0 -0
  31. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/src/writer.rs +0 -0
  32. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat/tests/integration_test.rs +0 -0
  33. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/README.md +0 -0
  34. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/batch.rs +0 -0
  35. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/bug_rating.rs +0 -0
  36. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/diagram.rs +0 -0
  37. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/error.rs +0 -0
  38. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/lib.rs +0 -0
  39. {eulumdat-0.2.1 → eulumdat-0.2.2}/crates/eulumdat-py/src/validation.rs +0 -0
@@ -1400,7 +1400,7 @@ dependencies = [
1400
1400
 
1401
1401
  [[package]]
1402
1402
  name = "eulumdat"
1403
- version = "0.2.1"
1403
+ version = "0.2.2"
1404
1404
  dependencies = [
1405
1405
  "anyhow",
1406
1406
  "approx",
@@ -1409,7 +1409,7 @@ dependencies = [
1409
1409
 
1410
1410
  [[package]]
1411
1411
  name = "eulumdat-cli"
1412
- version = "0.2.1"
1412
+ version = "0.2.2"
1413
1413
  dependencies = [
1414
1414
  "anyhow",
1415
1415
  "clap",
@@ -1419,7 +1419,7 @@ dependencies = [
1419
1419
 
1420
1420
  [[package]]
1421
1421
  name = "eulumdat-egui"
1422
- version = "0.2.1"
1422
+ version = "0.2.2"
1423
1423
  dependencies = [
1424
1424
  "anyhow",
1425
1425
  "eframe",
@@ -1434,16 +1434,32 @@ dependencies = [
1434
1434
 
1435
1435
  [[package]]
1436
1436
  name = "eulumdat-ffi"
1437
- version = "0.2.1"
1437
+ version = "0.2.2"
1438
1438
  dependencies = [
1439
1439
  "eulumdat",
1440
1440
  "thiserror 1.0.69",
1441
1441
  "uniffi",
1442
1442
  ]
1443
1443
 
1444
+ [[package]]
1445
+ name = "eulumdat-harmonyos-ffi"
1446
+ version = "0.2.2"
1447
+ dependencies = [
1448
+ "eulumdat",
1449
+ "eulumdat-photweb",
1450
+ ]
1451
+
1452
+ [[package]]
1453
+ name = "eulumdat-photweb"
1454
+ version = "0.2.2"
1455
+ dependencies = [
1456
+ "approx",
1457
+ "eulumdat",
1458
+ ]
1459
+
1444
1460
  [[package]]
1445
1461
  name = "eulumdat-py"
1446
- version = "0.2.1"
1462
+ version = "0.2.2"
1447
1463
  dependencies = [
1448
1464
  "eulumdat",
1449
1465
  "pyo3",
@@ -1451,7 +1467,7 @@ dependencies = [
1451
1467
 
1452
1468
  [[package]]
1453
1469
  name = "eulumdat-wasm"
1454
- version = "0.2.1"
1470
+ version = "0.2.2"
1455
1471
  dependencies = [
1456
1472
  "base64",
1457
1473
  "eulumdat",
@@ -1467,7 +1483,7 @@ dependencies = [
1467
1483
 
1468
1484
  [[package]]
1469
1485
  name = "eulumdat-windows-preview"
1470
- version = "0.2.1"
1486
+ version = "0.2.2"
1471
1487
  dependencies = [
1472
1488
  "anyhow",
1473
1489
  "embed-resource",
@@ -1523,9 +1539,9 @@ checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
1523
1539
 
1524
1540
  [[package]]
1525
1541
  name = "flate2"
1526
- version = "1.1.7"
1542
+ version = "1.1.5"
1527
1543
  source = "registry+https://github.com/rust-lang/crates.io-index"
1528
- checksum = "a2152dbcb980c05735e2a651d96011320a949eb31a0c8b38b72645ce97dec676"
1544
+ checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
1529
1545
  dependencies = [
1530
1546
  "crc32fast",
1531
1547
  "miniz_oxide",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/*"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.1"
6
+ version = "0.2.2"
7
7
  edition = "2021"
8
8
  authors = ["Holger Trahe <trahe@mac.com>"]
9
9
  license = "MIT OR Apache-2.0"
@@ -12,7 +12,7 @@ homepage = "https://github.com/holg/eulumdat-rs"
12
12
 
13
13
  [workspace.dependencies]
14
14
  # Internal crates
15
- eulumdat = { path = "crates/eulumdat" }
15
+ eulumdat = { path = "crates/eulumdat", version = "0.2.2" }
16
16
 
17
17
  # Shared dependencies
18
18
  anyhow = "1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eulumdat
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -361,4 +361,41 @@ impl Eulumdat {
361
361
  0.0
362
362
  }
363
363
  }
364
+
365
+ /// Sample intensity at any C and G angle using bilinear interpolation.
366
+ ///
367
+ /// This is the key method for generating beam meshes and smooth geometry.
368
+ /// It handles symmetry automatically and interpolates between stored data points.
369
+ ///
370
+ /// # Arguments
371
+ /// * `c_angle` - C-plane angle in degrees (0-360, will be normalized)
372
+ /// * `g_angle` - Gamma angle in degrees (0-180, will be clamped)
373
+ ///
374
+ /// # Returns
375
+ /// Interpolated intensity value in cd/klm
376
+ ///
377
+ /// # Example
378
+ /// ```rust,no_run
379
+ /// use eulumdat::Eulumdat;
380
+ ///
381
+ /// let ldt = Eulumdat::from_file("luminaire.ldt")?;
382
+ ///
383
+ /// // Sample at exact stored angles
384
+ /// let intensity = ldt.sample(0.0, 45.0);
385
+ ///
386
+ /// // Sample at arbitrary angles (will interpolate)
387
+ /// let intensity = ldt.sample(22.5, 67.5);
388
+ ///
389
+ /// // Generate smooth beam mesh at 5° intervals
390
+ /// for c in (0..360).step_by(5) {
391
+ /// for g in (0..=180).step_by(5) {
392
+ /// let intensity = ldt.sample(c as f64, g as f64);
393
+ /// // Use intensity for mesh generation...
394
+ /// }
395
+ /// }
396
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
397
+ /// ```
398
+ pub fn sample(&self, c_angle: f64, g_angle: f64) -> f64 {
399
+ crate::symmetry::SymmetryHandler::get_intensity_at(self, c_angle, g_angle)
400
+ }
364
401
  }
@@ -342,4 +342,74 @@ mod tests {
342
342
  assert!((x - 1.0).abs() < 0.001);
343
343
  assert!((y - 0.0).abs() < 0.001);
344
344
  }
345
+
346
+ #[test]
347
+ fn test_get_intensity_at_exact_angles() {
348
+ let ldt = Eulumdat {
349
+ symmetry: Symmetry::None,
350
+ c_angles: vec![0.0, 90.0, 180.0, 270.0],
351
+ g_angles: vec![0.0, 45.0, 90.0],
352
+ intensities: vec![
353
+ vec![100.0, 80.0, 50.0], // C0
354
+ vec![90.0, 70.0, 40.0], // C90
355
+ vec![80.0, 60.0, 30.0], // C180
356
+ vec![70.0, 50.0, 20.0], // C270
357
+ ],
358
+ ..Default::default()
359
+ };
360
+
361
+ // Test exact angles
362
+ let i = SymmetryHandler::get_intensity_at(&ldt, 0.0, 0.0);
363
+ assert!((i - 100.0).abs() < 0.001);
364
+
365
+ let i = SymmetryHandler::get_intensity_at(&ldt, 90.0, 45.0);
366
+ assert!((i - 70.0).abs() < 0.001);
367
+ }
368
+
369
+ #[test]
370
+ fn test_get_intensity_at_interpolated() {
371
+ let ldt = Eulumdat {
372
+ symmetry: Symmetry::None,
373
+ c_angles: vec![0.0, 90.0],
374
+ g_angles: vec![0.0, 90.0],
375
+ intensities: vec![
376
+ vec![100.0, 0.0], // C0: 100 at nadir, 0 at horizontal
377
+ vec![100.0, 0.0], // C90: same
378
+ ],
379
+ ..Default::default()
380
+ };
381
+
382
+ // Interpolate at G=45 should give ~50 (midpoint)
383
+ let i = SymmetryHandler::get_intensity_at(&ldt, 0.0, 45.0);
384
+ assert!((i - 50.0).abs() < 0.001);
385
+ }
386
+
387
+ #[test]
388
+ fn test_sample_method() {
389
+ let ldt = Eulumdat {
390
+ symmetry: Symmetry::BothPlanes,
391
+ c_angles: vec![0.0, 45.0, 90.0],
392
+ g_angles: vec![0.0, 30.0, 60.0, 90.0],
393
+ intensities: vec![
394
+ vec![100.0, 90.0, 70.0, 40.0], // C0
395
+ vec![95.0, 85.0, 65.0, 35.0], // C45
396
+ vec![90.0, 80.0, 60.0, 30.0], // C90
397
+ ],
398
+ ..Default::default()
399
+ };
400
+
401
+ // Test the sample() convenience method
402
+ let i = ldt.sample(0.0, 0.0);
403
+ assert!((i - 100.0).abs() < 0.001);
404
+
405
+ // Test symmetry - C180 should mirror C0
406
+ let i_c0 = ldt.sample(0.0, 30.0);
407
+ let i_c180 = ldt.sample(180.0, 30.0);
408
+ assert!((i_c0 - i_c180).abs() < 0.001);
409
+
410
+ // Test symmetry - C270 should mirror C90
411
+ let i_c90 = ldt.sample(90.0, 60.0);
412
+ let i_c270 = ldt.sample(270.0, 60.0);
413
+ assert!((i_c90 - i_c270).abs() < 0.001);
414
+ }
345
415
  }
@@ -1,9 +1,9 @@
1
1
  [package]
2
2
  name = "eulumdat-py"
3
- version.workspace = true
4
- edition.workspace = true
5
- license.workspace = true
6
- repository.workspace = true
3
+ version = "0.2.2" # Must be explicit for maturin (workspace inheritance not supported)
4
+ edition = "2021"
5
+ license = "MIT OR Apache-2.0"
6
+ repository = "https://github.com/holg/eulumdat-rs"
7
7
  description = "Python bindings for eulumdat photometric file parsing library"
8
8
  readme = "README.md"
9
9
 
@@ -12,5 +12,5 @@ name = "eulumdat"
12
12
  crate-type = ["cdylib"]
13
13
 
14
14
  [dependencies]
15
- eulumdat = { path = "../eulumdat" }
15
+ eulumdat = { path = "../eulumdat", version = "0.2.2" }
16
16
  pyo3 = { version = "0.27", features = ["extension-module"] }
@@ -720,6 +720,39 @@ impl Eulumdat {
720
720
  self.inner.get_intensity(c_index, g_index)
721
721
  }
722
722
 
723
+ /// Sample intensity at any C and G angle using bilinear interpolation.
724
+ ///
725
+ /// This method handles symmetry automatically - you can query any angle
726
+ /// in the full 0-360° C range and 0-180° G range regardless of stored symmetry.
727
+ ///
728
+ /// Args:
729
+ /// c_angle: C-plane angle in degrees (will be normalized to 0-360)
730
+ /// g_angle: Gamma angle in degrees (will be clamped to 0-180)
731
+ ///
732
+ /// Returns:
733
+ /// Intensity in cd/klm at the specified angle
734
+ fn sample(&self, c_angle: f64, g_angle: f64) -> f64 {
735
+ self.inner.sample(c_angle, g_angle)
736
+ }
737
+
738
+ /// Sample normalized intensity (0.0 to 1.0) at any C and G angle.
739
+ ///
740
+ /// Returns intensity relative to maximum intensity, useful for visualization.
741
+ ///
742
+ /// Args:
743
+ /// c_angle: C-plane angle in degrees
744
+ /// g_angle: Gamma angle in degrees
745
+ ///
746
+ /// Returns:
747
+ /// Normalized intensity (0.0 to 1.0)
748
+ fn sample_normalized(&self, c_angle: f64, g_angle: f64) -> f64 {
749
+ let max = self.inner.max_intensity();
750
+ if max <= 0.0 {
751
+ return 0.0;
752
+ }
753
+ self.inner.sample(c_angle, g_angle) / max
754
+ }
755
+
723
756
  // === Diagram Generation ===
724
757
 
725
758
  /// Generate a polar diagram SVG.
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "eulumdat"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Python bindings for eulumdat photometric file parsing library"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT OR Apache-2.0" }
File without changes