spyrrow 0.4.0__tar.gz → 0.6.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.

Potentially problematic release.


This version of spyrrow might be problematic. Click here for more details.

@@ -58,6 +58,24 @@ version = "1.0.98"
58
58
  source = "registry+https://github.com/rust-lang/crates.io-index"
59
59
  checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
60
60
 
61
+ [[package]]
62
+ name = "approx"
63
+ version = "0.5.1"
64
+ source = "registry+https://github.com/rust-lang/crates.io-index"
65
+ checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
66
+ dependencies = [
67
+ "num-traits",
68
+ ]
69
+
70
+ [[package]]
71
+ name = "atomic-polyfill"
72
+ version = "1.0.3"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
75
+ dependencies = [
76
+ "critical-section",
77
+ ]
78
+
61
79
  [[package]]
62
80
  name = "autocfg"
63
81
  version = "1.4.0"
@@ -70,6 +88,12 @@ version = "2.9.0"
70
88
  source = "registry+https://github.com/rust-lang/crates.io-index"
71
89
  checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
72
90
 
91
+ [[package]]
92
+ name = "byteorder"
93
+ version = "1.5.0"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
96
+
73
97
  [[package]]
74
98
  name = "cfg-if"
75
99
  version = "1.0.0"
@@ -128,6 +152,12 @@ version = "1.0.3"
128
152
  source = "registry+https://github.com/rust-lang/crates.io-index"
129
153
  checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
130
154
 
155
+ [[package]]
156
+ name = "critical-section"
157
+ version = "1.2.0"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
160
+
131
161
  [[package]]
132
162
  name = "crossbeam-deque"
133
163
  version = "0.8.6"
@@ -202,6 +232,58 @@ dependencies = [
202
232
  "num-traits",
203
233
  ]
204
234
 
235
+ [[package]]
236
+ name = "float_next_after"
237
+ version = "1.0.0"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
240
+
241
+ [[package]]
242
+ name = "geo"
243
+ version = "0.24.1"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "c7d640a4dd1d1c98b45f4653c841a8ec15f461a71b86bc30533ae64c6f20f268"
246
+ dependencies = [
247
+ "float_next_after",
248
+ "geo-types",
249
+ "geographiclib-rs",
250
+ "log",
251
+ "num-traits",
252
+ "robust",
253
+ "rstar",
254
+ ]
255
+
256
+ [[package]]
257
+ name = "geo-buffer"
258
+ version = "0.2.0"
259
+ source = "registry+https://github.com/rust-lang/crates.io-index"
260
+ checksum = "267bf0373df2f0b0b05065ebc0c84b97ccd221e19e0cb9442bcffe8fce04c130"
261
+ dependencies = [
262
+ "geo",
263
+ "geo-types",
264
+ ]
265
+
266
+ [[package]]
267
+ name = "geo-types"
268
+ version = "0.7.16"
269
+ source = "registry+https://github.com/rust-lang/crates.io-index"
270
+ checksum = "62ddb1950450d67efee2bbc5e429c68d052a822de3aad010d28b351fbb705224"
271
+ dependencies = [
272
+ "approx",
273
+ "num-traits",
274
+ "rstar",
275
+ "serde",
276
+ ]
277
+
278
+ [[package]]
279
+ name = "geographiclib-rs"
280
+ version = "0.2.5"
281
+ source = "registry+https://github.com/rust-lang/crates.io-index"
282
+ checksum = "f611040a2bb37eaa29a78a128d1e92a378a03e0b6e66ae27398d42b1ba9a7841"
283
+ dependencies = [
284
+ "libm",
285
+ ]
286
+
205
287
  [[package]]
206
288
  name = "getrandom"
207
289
  version = "0.3.2"
@@ -214,6 +296,28 @@ dependencies = [
214
296
  "wasi",
215
297
  ]
216
298
 
299
+ [[package]]
300
+ name = "hash32"
301
+ version = "0.2.1"
302
+ source = "registry+https://github.com/rust-lang/crates.io-index"
303
+ checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
304
+ dependencies = [
305
+ "byteorder",
306
+ ]
307
+
308
+ [[package]]
309
+ name = "heapless"
310
+ version = "0.7.17"
311
+ source = "registry+https://github.com/rust-lang/crates.io-index"
312
+ checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
313
+ dependencies = [
314
+ "atomic-polyfill",
315
+ "hash32",
316
+ "rustc_version",
317
+ "spin",
318
+ "stable_deref_trait",
319
+ ]
320
+
217
321
  [[package]]
218
322
  name = "heck"
219
323
  version = "0.5.0"
@@ -255,12 +359,14 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
255
359
 
256
360
  [[package]]
257
361
  name = "jagua-rs"
258
- version = "0.5.1"
259
- source = "git+https://github.com/JeroenGar/jagua-rs.git?rev=3d36f849653e3e2273be9f6a7fd24da51ded585d#3d36f849653e3e2273be9f6a7fd24da51ded585d"
362
+ version = "0.6.2"
363
+ source = "git+https://github.com/JeroenGar/jagua-rs.git?rev=6e47329e4fd8dfe014428bd9045db132d26d35c9#6e47329e4fd8dfe014428bd9045db132d26d35c9"
260
364
  dependencies = [
261
365
  "anyhow",
262
366
  "document-features",
263
367
  "float-cmp",
368
+ "geo-buffer",
369
+ "geo-types",
264
370
  "itertools",
265
371
  "log",
266
372
  "ndarray",
@@ -270,7 +376,6 @@ dependencies = [
270
376
  "serde",
271
377
  "slotmap",
272
378
  "svg",
273
- "tribool",
274
379
  ]
275
380
 
276
381
  [[package]]
@@ -332,6 +437,16 @@ version = "0.4.1"
332
437
  source = "registry+https://github.com/rust-lang/crates.io-index"
333
438
  checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
334
439
 
440
+ [[package]]
441
+ name = "lock_api"
442
+ version = "0.4.13"
443
+ source = "registry+https://github.com/rust-lang/crates.io-index"
444
+ checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
445
+ dependencies = [
446
+ "autocfg",
447
+ "scopeguard",
448
+ ]
449
+
335
450
  [[package]]
336
451
  name = "log"
337
452
  version = "0.4.27"
@@ -630,12 +745,50 @@ dependencies = [
630
745
  "crossbeam-utils",
631
746
  ]
632
747
 
748
+ [[package]]
749
+ name = "robust"
750
+ version = "0.2.3"
751
+ source = "registry+https://github.com/rust-lang/crates.io-index"
752
+ checksum = "e5864e7ef1a6b7bcf1d6ca3f655e65e724ed3b52546a0d0a663c991522f552ea"
753
+
754
+ [[package]]
755
+ name = "rstar"
756
+ version = "0.10.0"
757
+ source = "registry+https://github.com/rust-lang/crates.io-index"
758
+ checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a"
759
+ dependencies = [
760
+ "heapless",
761
+ "num-traits",
762
+ "smallvec",
763
+ ]
764
+
765
+ [[package]]
766
+ name = "rustc_version"
767
+ version = "0.4.1"
768
+ source = "registry+https://github.com/rust-lang/crates.io-index"
769
+ checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
770
+ dependencies = [
771
+ "semver",
772
+ ]
773
+
633
774
  [[package]]
634
775
  name = "ryu"
635
776
  version = "1.0.20"
636
777
  source = "registry+https://github.com/rust-lang/crates.io-index"
637
778
  checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
638
779
 
780
+ [[package]]
781
+ name = "scopeguard"
782
+ version = "1.2.0"
783
+ source = "registry+https://github.com/rust-lang/crates.io-index"
784
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
785
+
786
+ [[package]]
787
+ name = "semver"
788
+ version = "1.0.26"
789
+ source = "registry+https://github.com/rust-lang/crates.io-index"
790
+ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
791
+
639
792
  [[package]]
640
793
  name = "serde"
641
794
  version = "1.0.219"
@@ -677,10 +830,16 @@ dependencies = [
677
830
  "version_check",
678
831
  ]
679
832
 
833
+ [[package]]
834
+ name = "smallvec"
835
+ version = "1.15.1"
836
+ source = "registry+https://github.com/rust-lang/crates.io-index"
837
+ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
838
+
680
839
  [[package]]
681
840
  name = "sparrow"
682
841
  version = "0.1.0"
683
- source = "git+https://github.com/JeroenGar/sparrow.git?rev=3adafa222a260dc3f83a15a899541d2c51fae5d6#3adafa222a260dc3f83a15a899541d2c51fae5d6"
842
+ source = "git+https://github.com/JeroenGar/sparrow.git?rev=7f069e9d146f5599d7ba69a47ab4e8097c56e54f#7f069e9d146f5599d7ba69a47ab4e8097c56e54f"
684
843
  dependencies = [
685
844
  "anyhow",
686
845
  "clap",
@@ -691,6 +850,7 @@ dependencies = [
691
850
  "jagua-rs",
692
851
  "jiff",
693
852
  "log",
853
+ "ndarray",
694
854
  "num_cpus",
695
855
  "numfmt",
696
856
  "ordered-float",
@@ -705,9 +865,18 @@ dependencies = [
705
865
  "test-case",
706
866
  ]
707
867
 
868
+ [[package]]
869
+ name = "spin"
870
+ version = "0.9.8"
871
+ source = "registry+https://github.com/rust-lang/crates.io-index"
872
+ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
873
+ dependencies = [
874
+ "lock_api",
875
+ ]
876
+
708
877
  [[package]]
709
878
  name = "spyrrow"
710
- version = "0.4.0"
879
+ version = "0.6.0"
711
880
  dependencies = [
712
881
  "jagua-rs",
713
882
  "pyo3",
@@ -717,6 +886,12 @@ dependencies = [
717
886
  "sparrow",
718
887
  ]
719
888
 
889
+ [[package]]
890
+ name = "stable_deref_trait"
891
+ version = "1.2.0"
892
+ source = "registry+https://github.com/rust-lang/crates.io-index"
893
+ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
894
+
720
895
  [[package]]
721
896
  name = "strsim"
722
897
  version = "0.11.1"
@@ -785,12 +960,6 @@ dependencies = [
785
960
  "test-case-core",
786
961
  ]
787
962
 
788
- [[package]]
789
- name = "tribool"
790
- version = "0.3.0"
791
- source = "registry+https://github.com/rust-lang/crates.io-index"
792
- checksum = "1e8660361502033a51e119386b47fbb811e5706722f2e91ccf867aa6b2b09f90"
793
-
794
963
  [[package]]
795
964
  name = "unicode-ident"
796
965
  version = "1.0.18"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "spyrrow"
3
- version = "0.4.0"
3
+ version = "0.6.0"
4
4
  edition = "2024"
5
5
  license = "MIT"
6
6
 
@@ -10,9 +10,9 @@ name = "spyrrow"
10
10
  crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
- jagua-rs = { git = "https://github.com/JeroenGar/jagua-rs.git", rev="3d36f849653e3e2273be9f6a7fd24da51ded585d", features = ["spp"], default-features = false}
13
+ jagua-rs = { git = "https://github.com/JeroenGar/jagua-rs.git", rev="6e47329e4fd8dfe014428bd9045db132d26d35c9", features = ["spp"], default-features = false}
14
14
  pyo3 = "0.24.0"
15
15
  rand = { version = "0.9.0", features = ["small_rng"] }
16
16
  serde = {version = "1.0.219", features = ["derive"]}
17
17
  serde_json = "1.0.140"
18
- sparrow = { git = "https://github.com/JeroenGar/sparrow.git",rev="3adafa222a260dc3f83a15a899541d2c51fae5d6",features = ["only_final_svg"], default-features = false}
18
+ sparrow = { git = "https://github.com/JeroenGar/sparrow.git",rev="7f069e9d146f5599d7ba69a47ab4e8097c56e54f",features = ["only_final_svg"], default-features = false}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spyrrow
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -35,10 +35,10 @@ pip install spyrrow
35
35
  import spyrrow
36
36
 
37
37
  rectangle1 = spyrrow.Item(
38
- 0, [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
38
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
39
39
  )
40
40
  triangle1 = spyrrow.Item(
41
- 1,
41
+ "triangle",
42
42
  [(0, 0), (1, 0), (1, 1), (0, 0)],
43
43
  demand=6,
44
44
  allowed_orientations=[0, 90, 180, -90],
@@ -21,10 +21,10 @@ pip install spyrrow
21
21
  import spyrrow
22
22
 
23
23
  rectangle1 = spyrrow.Item(
24
- 0, [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
24
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
25
25
  )
26
26
  triangle1 = spyrrow.Item(
27
- 1,
27
+ "triangle",
28
28
  [(0, 0), (1, 0), (1, 1), (0, 0)],
29
29
  demand=6,
30
30
  allowed_orientations=[0, 90, 180, -90],
@@ -9,7 +9,7 @@
9
9
  project = 'spyrrow'
10
10
  copyright = '2025, Paul Durand-Lupinski'
11
11
  author = 'Paul Durand-Lupinski'
12
- release = '0.4.0'
12
+ release = '0.5.0'
13
13
 
14
14
  # -- General configuration ---------------------------------------------------
15
15
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -36,10 +36,10 @@ Examples
36
36
  import spyrrow
37
37
 
38
38
  rectangle1 = spyrrow.Item(
39
- 0, [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
39
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
40
40
  )
41
41
  triangle1 = spyrrow.Item(
42
- 1,
42
+ "triangle",
43
43
  [(0, 0), (1, 0), (1, 1), (0, 0)],
44
44
  demand=6,
45
45
  allowed_orientations=[0, 90, 180, -90],
@@ -54,4 +54,17 @@ Examples
54
54
  print(pi.id)
55
55
  print(pi.rotation)
56
56
  print(pi.translation)
57
- print("\n")
57
+ print("\n")
58
+
59
+ In order to express that an Item can rotate freely, its `allowed_orientations` attributes should be set to `None`.
60
+
61
+ .. code-block:: python
62
+
63
+ import spyrrow
64
+
65
+ triangle1 = spyrrow.Item(
66
+ "triangle",
67
+ [(0, 0), (1, 0), (1, 1), (0, 0)],
68
+ demand=6,
69
+ allowed_orientations=None,
70
+ )
@@ -3,52 +3,51 @@ from typing import TypeAlias
3
3
  Point: TypeAlias = tuple[float, float]
4
4
 
5
5
  class Item:
6
- id: int
6
+ id: str
7
7
  demand: int
8
8
  shape: list[Point]
9
9
  allowed_orientations: list[float]
10
10
 
11
11
  def __init__(
12
12
  self,
13
- id: int,
13
+ id: str,
14
14
  shape: list[Point],
15
15
  demand: int,
16
- allowed_orientations: list[float],
16
+ allowed_orientations: list[float] | None,
17
17
  ):
18
18
  """
19
19
  An Item represents any closed 2D shape by its outer boundary.
20
20
 
21
21
  Spyrrow doesn't support hole(s) inside the shape as of yet. Therefore no Item can be nested inside another.
22
22
 
23
- Continous rotation is not supported as of yet. A workaround is to specify any integer degrees between 0 and 360
24
- to the allowed_orientations list.
25
-
26
23
  Args:
27
- id: The Item identifier for a given StripPackingInstance.
28
- Best autoincremented as the instance verifies that all ids are presents starting from 0.
24
+ id (str): The Item identifier
25
+ Needs to be unique accross all Items of a StripPackingInstance
29
26
  shape: An ordered list of (x,y) defining the shape boundary. The shape is represented as a polygon formed by this list of points.
30
27
  The origin point can be included twice as the finishing point. If not, [last point, first point] is infered to be the last straight line of the shape.
31
28
  demand: The quantity of identical Items to be placed inside the strip. Should be positive.
32
- allowed_orientations: List of angles in degrees allowed. An empty list is equivalent to [0.].
29
+ allowed_orientations (list[float]|None): List of angles in degrees allowed.
30
+ An empty list is equivalent to [0.].
31
+ A None value means that the item is free to rotate
33
32
  The algorithmn is only very weakly sensible to the length of the list given.
34
33
 
35
34
  """
36
-
37
- def to_json_str(self)->str:
38
- """ Return a string of the JSON representation of the object"""
35
+
36
+ def to_json_str(self) -> str:
37
+ """Return a string of the JSON representation of the object"""
39
38
 
40
39
  class PlacedItem:
41
40
  """
42
41
  An object representing where a copy of an Item was placed inside the strip.
43
42
 
44
43
  Attributes:
45
- id (int): The Item identifier for a given StripPackingInstance.
46
- translation (tuple[float,float]): the translation vector in the X-Y axis
47
- rotation (float): The roation angle in degrees, assuming that the original Item was defined with 0° as its rotation angle.
44
+ id (str): The Item identifier referencing the items of the StripPackingInstance
45
+ rotation (float): The rotation angle in degrees, assuming that the original Item was defined with 0° as its rotation angle.
46
+ Use the origin (0.0,0.0) as the rotation point.
47
+ translation (tuple[float,float]): the translation vector in the X-Y axis. To apply after the rotation
48
48
  """
49
49
 
50
- id: int
51
- shape: list[Point]
50
+ id: str
52
51
  translation: Point
53
52
  rotation: float
54
53
 
@@ -82,14 +81,11 @@ class StripPackingInstance:
82
81
  An empty string '' can be used, if the user doesn't have a use for this name.
83
82
  strip_height (float): the fixed height of the strip. The unit should be compatible with the Item
84
83
  items (list[Item]): The Items which defines the instances. All Items should be defined with the same scale ( same length unit).
85
- Items ids should be an increasing series starting at 0 until len(items)-1.
86
-
87
84
  Raises:
88
85
  ValueError
89
86
  """
90
- def to_json_str(self)->str:
91
- """ Return a string of the JSON representation of the object"""
92
-
87
+ def to_json_str(self) -> str:
88
+ """Return a string of the JSON representation of the object"""
93
89
 
94
90
  def solve(self, computation_time: int = 600) -> StripPackingSolution:
95
91
  """
@@ -11,6 +11,7 @@ use sparrow::config::{
11
11
  CDE_CONFIG, COMPRESS_TIME_RATIO, EXPLORE_TIME_RATIO, MIN_ITEM_SEPARATION, SIMPL_TOLERANCE,
12
12
  };
13
13
  use sparrow::optimizer::{Terminator, optimize};
14
+ use std::collections::HashSet;
14
15
  use std::fs;
15
16
  use std::time::Duration;
16
17
 
@@ -20,20 +21,20 @@ use std::time::Duration;
20
21
  ///
21
22
  /// Spyrrow doesn't support hole(s) inside the shape as of yet. Therefore no Item can be nested inside another.
22
23
  ///
23
- /// Continous rotation is not supported as of yet. A workaround is to specify any integer degrees between 0 and 360
24
- /// to the allowed_orientations list.
25
24
  ///
26
25
  /// Args:
27
- /// id (int): The Item identifier for a given StripPackingInstance.
28
- /// Best autoincremented as the instance verifies that all ids are presents starting from 0.
26
+ /// id (str): The Item identifier
27
+ /// Needs to be unique accross all Items of a StripPackingInstance
29
28
  /// shape (list[tuple[float,float]]): An ordered list of (x,y) defining the shape boundary. The shape is represented as a polygon formed by this list of points.
30
29
  /// The origin point can be included twice as the finishing point. If not, [last point, first point] is infered to be the last straight line of the shape.
31
30
  /// demand (int): The quantity of identical Items to be placed inside the strip. Should be positive.
32
- /// allowed_orientations (list[float]): List of angles in degrees allowed. An empty list is equivalent to [0.].
31
+ /// allowed_orientations (list[float]|None): List of angles in degrees allowed.
32
+ /// An empty list is equivalent to [0.].
33
+ /// A None value means that the item is free to rotate
33
34
  /// The algorithmn is only very weakly sensible to the length of the list given.
34
35
  ///
35
36
  struct ItemPy {
36
- id: u64,
37
+ id: String,
37
38
  demand: u64,
38
39
  allowed_orientations: Option<Vec<f32>>,
39
40
  shape: Vec<(f32, f32)>,
@@ -42,11 +43,16 @@ struct ItemPy {
42
43
  #[pymethods]
43
44
  impl ItemPy {
44
45
  #[new]
45
- fn new(id: u64, shape: Vec<(f32, f32)>, demand: u64, allowed_orientations: Vec<f32>) -> Self {
46
+ fn new(
47
+ id: String,
48
+ shape: Vec<(f32, f32)>,
49
+ demand: u64,
50
+ allowed_orientations: Option<Vec<f32>>,
51
+ ) -> Self {
46
52
  ItemPy {
47
53
  id,
48
54
  demand,
49
- allowed_orientations: Some(allowed_orientations),
55
+ allowed_orientations,
50
56
  shape,
51
57
  }
52
58
  }
@@ -78,34 +84,19 @@ impl ItemPy {
78
84
  }
79
85
  }
80
86
 
81
- impl From<ItemPy> for ExtItem {
82
- fn from(value: ItemPy) -> Self {
83
- let polygon = ExtSPolygon(value.shape);
84
- let shape = ExtShape::SimplePolygon(polygon);
85
- let base = BaseItem {
86
- id: value.id,
87
- allowed_orientations: value.allowed_orientations,
88
- shape,
89
- min_quality: None,
90
- };
91
- ExtItem {
92
- base,
93
- demand: value.demand,
94
- }
95
- }
96
- }
97
-
98
87
  #[pyclass(name = "PlacedItem", get_all)]
99
88
  #[derive(Clone, Debug)]
100
89
  /// An object representing where a copy of an Item was placed inside the strip.
101
90
  ///
102
91
  /// Attributes:
103
- /// id (int): The Item identifier for a given StripPackingInstance.
104
- /// translation (tuple[float,float]): the translation vector in the X-Y axis
105
- /// rotation (float): The roation angle in degrees, assuming that the original Item was defined with 0° as its rotation angle.
92
+ /// id (str): The Item identifier referencing the items of the StripPackingInstance
93
+ /// rotation (float): The rotation angle in degrees, assuming that the original Item was defined with 0° as its rotation angle.
94
+ /// Use the origin (0.0,0.0) as the rotation point.
95
+ /// translation (tuple[float,float]): the translation vector in the X-Y axis. To apply after the rotation
96
+ ///
106
97
  ///
107
98
  struct PlacedItemPy {
108
- pub id: u64,
99
+ pub id: String,
109
100
  pub translation: (f32, f32),
110
101
  pub rotation: f32,
111
102
  }
@@ -127,6 +118,11 @@ struct StripPackingSolutionPy {
127
118
  pub density: f32,
128
119
  }
129
120
 
121
+ fn all_unique(strings: &[&str]) -> bool {
122
+ let mut seen = HashSet::new();
123
+ strings.iter().all(|s| seen.insert(*s))
124
+ }
125
+
130
126
  #[pyclass(name = "StripPackingInstance", get_all, set_all)]
131
127
  #[derive(Clone, Serialize)]
132
128
  /// An Instance of a Strip Packing Problem.
@@ -136,7 +132,6 @@ struct StripPackingSolutionPy {
136
132
  /// An empty string '' can be used, if the user doesn't have a use for this name.
137
133
  /// strip_height (float): the fixed height of the strip. The unit should be compatible with the Item
138
134
  /// items (list[Item]): The Items which defines the instances. All Items should be defined with the same scale ( same length unit).
139
- /// Items ids should be an increasing series starting at 0 until len(items)-1.
140
135
  ///
141
136
  /// Raises:
142
137
  /// ValueError
@@ -149,7 +144,25 @@ struct StripPackingInstancePy {
149
144
 
150
145
  impl From<StripPackingInstancePy> for ExtSPInstance {
151
146
  fn from(value: StripPackingInstancePy) -> Self {
152
- let items = value.items.into_iter().map(|v| v.into()).collect();
147
+ let items = value
148
+ .items
149
+ .into_iter()
150
+ .enumerate()
151
+ .map(|(idx, v)| {
152
+ let polygon = ExtSPolygon(v.shape);
153
+ let shape = ExtShape::SimplePolygon(polygon);
154
+ let base = BaseItem {
155
+ id: idx as u64,
156
+ allowed_orientations: v.allowed_orientations,
157
+ shape,
158
+ min_quality: None,
159
+ };
160
+ ExtItem {
161
+ base,
162
+ demand: v.demand,
163
+ }
164
+ })
165
+ .collect();
153
166
  ExtSPInstance {
154
167
  name: value.name,
155
168
  strip_height: value.strip_height,
@@ -162,14 +175,9 @@ impl From<StripPackingInstancePy> for ExtSPInstance {
162
175
  impl StripPackingInstancePy {
163
176
  #[new]
164
177
  fn new(name: String, strip_height: f32, items: Vec<ItemPy>) -> PyResult<Self> {
165
- let mut item_ids: Vec<u64> = items.iter().map(|i| i.id).collect();
166
- item_ids.sort();
167
- let expected_ids: Vec<u64> = (0..items.len()).map(|idx| idx as u64).collect();
168
- if item_ids != expected_ids {
169
- let error_string = format!(
170
- "The item ids are not ordered from 0 to the Items length -1: {:#?}",
171
- item_ids
172
- );
178
+ let item_ids: Vec<&str> = items.iter().map(|i| i.id.as_str()).collect();
179
+ if !all_unique(&item_ids) {
180
+ let error_string = format!("The item ids are not uniques: {:#?}", item_ids);
173
181
  return Err(PyValueError::new_err(error_string));
174
182
  }
175
183
  Ok(StripPackingInstancePy {
@@ -238,8 +246,8 @@ impl StripPackingInstancePy {
238
246
  .placed_items
239
247
  .into_iter()
240
248
  .map(|jpi| PlacedItemPy {
241
- id: jpi.item_id,
242
- rotation: jpi.transformation.rotation,
249
+ id: self.items[jpi.item_id as usize].id.clone(),
250
+ rotation: jpi.transformation.rotation.to_degrees(), // Until sparrow exports to degrees instead of radians
243
251
  translation: jpi.transformation.translation,
244
252
  })
245
253
  .collect();
@@ -2,12 +2,11 @@ import spyrrow
2
2
  import pytest
3
3
 
4
4
  def test_basic():
5
- ## Continuous rotation seems to not be implemented for strip packing
6
5
  rectangle1 = spyrrow.Item(
7
- 0, [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
6
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
8
7
  )
9
8
  triangle1 = spyrrow.Item(
10
- 1,
9
+ "triangle",
11
10
  [(0, 0), (1, 0), (1, 1), (0, 0)],
12
11
  demand=6,
13
12
  allowed_orientations=[0, 90, 180, -90],
@@ -22,10 +21,10 @@ def test_basic():
22
21
  def test_2_consecutive_calls():
23
22
  # Test correpsonding to crash on the second consecutive call of solve method
24
23
  rectangle1 = spyrrow.Item(
25
- 0, [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
24
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
26
25
  )
27
26
  triangle1 = spyrrow.Item(
28
- 1,
27
+ "triangle",
29
28
  [(0, 0), (1, 0), (1, 1), (0, 0)],
30
29
  demand=6,
31
30
  allowed_orientations=[0, 90, 180, -90],
@@ -39,9 +38,26 @@ def test_2_consecutive_calls():
39
38
  assert sol.width == pytest.approx(4,rel=0.2)
40
39
 
41
40
  def test_concave_polygons():
42
- poly1 = spyrrow.Item(0,[(0, 0), (3, 0), (4, 1), (3, 2), (0, 2), (1, 1), (0, 0)],demand=2,allowed_orientations=[0,90,180,270])
43
- poly2 = spyrrow.Item(1,[(0, 0), (1, 0), (1, 2), (3, 2), (3, 0), (4, 0), (4, 3), (0, 3), (0, 0)], demand=3, allowed_orientations=[0,90,180,270])
41
+ poly1 = spyrrow.Item("0",[(0, 0), (3, 0), (4, 1), (3, 2), (0, 2), (1, 1), (0, 0)],demand=2,allowed_orientations=[0,90,180,270])
42
+ poly2 = spyrrow.Item("1",[(0, 0), (1, 0), (1, 2), (3, 2), (3, 0), (4, 0), (4, 3), (0, 3), (0, 0)], demand=3, allowed_orientations=[0,90,180,270])
44
43
  instance = spyrrow.StripPackingInstance(
45
44
  "test", strip_height=4.001, items=[poly1, poly2]
46
45
  )
47
- sol = instance.solve(30)
46
+ sol = instance.solve(30)
47
+
48
+ def test_continuous_rotation():
49
+ rectangle1 = spyrrow.Item(
50
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=None
51
+ )
52
+ triangle1 = spyrrow.Item(
53
+ "triangle",
54
+ [(0, 0), (1, 0), (1, 1), (0, 0)],
55
+ demand=6,
56
+ allowed_orientations=None,
57
+ )
58
+
59
+ instance = spyrrow.StripPackingInstance(
60
+ "test", strip_height=2.001, items=[rectangle1, triangle1]
61
+ )
62
+ sol = instance.solve(30)
63
+ assert sol.width == pytest.approx(4,rel=0.2)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes