polycubetools 1.1.0__py3-none-any.whl → 1.1.2__py3-none-any.whl

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.
polycubetools/__init__.py CHANGED
@@ -10,9 +10,10 @@ Basic framework for working with polycubes in a 3-dimensional grid.
10
10
  __author__ = "Team Polycube"
11
11
  __title__ = "polycubetools"
12
12
  __license__ = "MIT"
13
- __version__ = "1.1.0"
13
+ __version__ = "1.1.2"
14
14
 
15
15
  from . import utils as utils
16
+ from .errors import *
16
17
  from .grid import *
17
18
  from .hull import *
18
19
  from .models import *
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = (
4
+ "InvalidVolumeException",
5
+ "PolycubeException",
6
+ )
7
+
8
+
9
+ class PolycubeException(Exception):
10
+ """Base exception for all errors raised by this library."""
11
+
12
+ pass
13
+
14
+
15
+ class InvalidVolumeException(PolycubeException):
16
+ """Exception raised when failing to compute a volume."""
17
+
18
+ pass
polycubetools/hull.py CHANGED
@@ -35,6 +35,11 @@ class AbstractHull(ABC):
35
35
  """Checks if the given coordinate is inside the hull."""
36
36
  pass
37
37
 
38
+ @abstractmethod
39
+ def inside_hull(self, pos: Coordinate) -> bool:
40
+ """Checks if the given coordinate is inside the hull or in its inner part."""
41
+ pass
42
+
38
43
  @abstractmethod
39
44
  def in_inner_part(self, pos: Coordinate) -> bool:
40
45
  """Checks if the given coordinate is inside the inner part of the hull."""
@@ -67,6 +72,9 @@ class CoordinateHull(AbstractHull):
67
72
  def in_hull(self, pos: Coordinate) -> bool:
68
73
  return pos in self.coords
69
74
 
75
+ def inside_hull(self, pos: Coordinate) -> bool:
76
+ return pos in self.coords or self.in_inner_part(pos)
77
+
70
78
  def in_inner_part(self, pos: Coordinate) -> bool:
71
79
  raise NotImplementedError(
72
80
  "Inner part is not defined for coordinate hulls (you can implement it in a subclass if needed)"
@@ -113,9 +121,56 @@ class CuboidHull(AbstractHull):
113
121
  def in_hull(self, pos: Coordinate) -> bool:
114
122
  minc, maxc = self.min_coord, self.max_coord
115
123
  px, py, pz = pos.x, pos.y, pos.z
116
- return px == minc.x or px == maxc.x or py == minc.y or py == maxc.y or pz == minc.z or pz == maxc.z
124
+ return (
125
+ px == minc.x or px == maxc.x or py == minc.y or py == maxc.y or pz == minc.z or pz == maxc.z
126
+ ) and self.inside_hull(pos)
127
+
128
+ def inside_hull(self, pos: Coordinate) -> bool:
129
+ minc, maxc = self.min_coord, self.max_coord
130
+ px, py, pz = pos.x, pos.y, pos.z
131
+ return minc.x <= px <= maxc.x and minc.y <= py <= maxc.y and minc.z <= pz <= maxc.z
117
132
 
118
133
  def in_inner_part(self, pos: Coordinate) -> bool:
119
134
  minc, maxc = self.min_coord, self.max_coord
120
135
  px, py, pz = pos.x, pos.y, pos.z
121
136
  return minc.x < px < maxc.x and minc.y < py < maxc.y and minc.z < pz < maxc.z
137
+
138
+
139
+ class CoordinateSetHull(AbstractHull):
140
+ def __init__(
141
+ self,
142
+ coords: frozenset[Coordinate],
143
+ inner_coords: frozenset[Coordinate],
144
+ frontier: set[Coordinate] | None = None,
145
+ ) -> None:
146
+ super().__init__()
147
+ self.coords = coords
148
+ self.inner_coords = inner_coords
149
+ if frontier is None:
150
+ self.frontier = set(coords)
151
+ else:
152
+ self.frontier = frontier
153
+
154
+ @classmethod
155
+ def from_snapshot(cls, snapshot: SNAPSHOT) -> Self:
156
+ coords = frozenset(Coordinate.from_tuple(t) for t in snapshot["coords"])
157
+ frontier = {Coordinate.from_tuple(t) for t in snapshot["frontier"]}
158
+ inner_coords = frozenset(Coordinate.from_tuple(t) for t in snapshot["inner_coords"])
159
+
160
+ return cls(coords, inner_coords, frontier)
161
+
162
+ def to_snapshot(self) -> SNAPSHOT:
163
+ return {
164
+ "coords": tuple(c.to_tuple() for c in self.coords),
165
+ "frontier": tuple(c.to_tuple() for c in self.frontier),
166
+ "inner_coords": tuple(c.to_tuple() for c in self.inner_coords),
167
+ }
168
+
169
+ def in_hull(self, pos: Coordinate) -> bool:
170
+ return pos in self.coords
171
+
172
+ def inside_hull(self, pos: Coordinate) -> bool:
173
+ return pos in self.coords or self.in_inner_part(pos)
174
+
175
+ def in_inner_part(self, pos: Coordinate) -> bool:
176
+ return pos in self.inner_coords
polycubetools/utils.py CHANGED
@@ -5,6 +5,7 @@ import json
5
5
  import logging
6
6
  from typing import Any, TYPE_CHECKING
7
7
 
8
+ from .errors import InvalidVolumeException
8
9
  from .models import TWENTY_SIX_NEIGHBORHOOD_DIRECTIONS, Polycube, RotatedPolycube, Coordinate
9
10
 
10
11
  if TYPE_CHECKING:
@@ -12,12 +13,10 @@ if TYPE_CHECKING:
12
13
 
13
14
  _logger = logging.getLogger(__name__)
14
15
 
15
- NO_VOLUME = -1
16
- MULTIPLE_VOLUME_COMPONENTS = -2
17
- ERROR_IN_ALGORITHM = -3
18
-
19
16
  __all__ = (
17
+ "collect_volume",
20
18
  "compute_volume",
19
+ "find_connected_component",
21
20
  "get_extreme_points",
22
21
  "is_valid_fence",
23
22
  "load_polycubes",
@@ -49,7 +48,7 @@ def _is_border_coordinate(coord: Coordinate, max_extrem_point: Coordinate, min_e
49
48
  )
50
49
 
51
50
 
52
- def _find_connected_component(visible_coords: set[Coordinate]) -> set[Coordinate]:
51
+ def find_connected_component(visible_coords: set[Coordinate]) -> set[Coordinate]:
53
52
  """Finds all coordinates connected to the first coordinate in the set using 26-neighborhood."""
54
53
  start = visible_coords.pop()
55
54
  stack = [start]
@@ -86,7 +85,7 @@ def get_extreme_points(coords: set[Coordinate]) -> tuple[Coordinate, Coordinate]
86
85
  return Coordinate(x_max, y_max, z_max), Coordinate(x_min, y_min, z_min)
87
86
 
88
87
 
89
- def compute_volume(coords: set[Coordinate]) -> int:
88
+ def collect_volume(coords: set[Coordinate]) -> set[Coordinate]:
90
89
  """Compute the volume from the hull formed by the coordinates"""
91
90
  max_extreme_point, min_extreme_point = get_extreme_points(coords)
92
91
  max_extreme_point = Coordinate(max_extreme_point.x + 1, max_extreme_point.y + 1, max_extreme_point.z + 1)
@@ -101,24 +100,41 @@ def compute_volume(coords: set[Coordinate]) -> int:
101
100
  }
102
101
 
103
102
  # the here popped coordinate is always outside the hull; this is because we added this outside new layer!
104
- _find_connected_component(visible_coords)
103
+ first_component = find_connected_component(visible_coords)
105
104
 
106
- # we return false because there is no second component, meaning the shape has no volume
107
105
  if not visible_coords:
108
106
  _logger.warning("No volume found! Only one component detected.")
109
- return NO_VOLUME
107
+ return set()
108
+
109
+ second_component = find_connected_component(visible_coords)
110
+
111
+ if min_extreme_point in second_component and max_extreme_point in second_component:
112
+ volume_cubes = first_component
113
+ elif min_extreme_point in first_component and max_extreme_point in first_component:
114
+ volume_cubes = second_component
115
+ else:
116
+ _logger.warning("Error. Couldn't find one outside component")
117
+ raise InvalidVolumeException("Couldn't find one outside component")
110
118
 
111
- volume_cubes = _find_connected_component(visible_coords)
112
119
  if visible_coords:
113
120
  _logger.warning(
114
- f"Found {len(volume_cubes)} as volume, but still {len(visible_coords)} unvisited cubes left. Exists another enclosed area!"
121
+ f"Found {len(volume_cubes)} as volume, but still {len(visible_coords)} unvisited cubes left. Another enclosed area exists!"
115
122
  )
116
- return MULTIPLE_VOLUME_COMPONENTS
123
+ raise InvalidVolumeException("Volume has multiple components")
117
124
 
118
125
  if any(_is_border_coordinate(c, max_extreme_point, min_extreme_point) for c in volume_cubes):
119
126
  _logger.warning("Volume has been found on the border! Bug in the validation.")
120
- return ERROR_IN_ALGORITHM
127
+ raise InvalidVolumeException("Volume has been found on the border")
121
128
 
129
+ return volume_cubes
130
+
131
+
132
+ def compute_volume(coords: set[Coordinate]) -> int:
133
+ """
134
+ Ruft die Funktion collect_volume auf, um alle Punkte der Volumen-Komponente zu erhalten.
135
+ Die Anzahl dieser Punkte ist das Volumen.
136
+ """
137
+ volume_cubes = collect_volume(coords)
122
138
  return len(volume_cubes)
123
139
 
124
140
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polycubetools
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Basic framework for working with polycubes in a 3-dimensional grid.
5
5
  Author: Team Polycube
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -0,0 +1,16 @@
1
+ polycubetools/__init__.py,sha256=r957_jZL81olgIrGgMPRZhC2_a8NMOmlAJOyKF7p39Y,394
2
+ polycubetools/errors.py,sha256=dIb032TJFwQJkZL3HOjPbDL8Qz2l9oAsunQzCz8TOe4,339
3
+ polycubetools/grid.py,sha256=UbUXxQ5opEAPKlPmbv7KIorUnRw41cuK7mkVoLDhqLE,7540
4
+ polycubetools/hull.py,sha256=FK9E9LV3SXcNI2EYAT4-MWeDNsyUue3VtSpehibl4EM,6212
5
+ polycubetools/models.py,sha256=5qi9WWOxYg8666x1LOkfyQoDSLf4Qzs3Z94-8N_zRLU,5128
6
+ polycubetools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ polycubetools/solver.py,sha256=X_6dXQ9WBm4CRHXILvs2whXbxYjW-ZeChBczosKJ7vM,5918
8
+ polycubetools/utils.py,sha256=P2pVBDfMQhbYcn4zWRzJmSNPp4opkOTBC9fm3RRtAlk,7647
9
+ polycubetools/data/heptacubes.json,sha256=vQ9QA3J5vOtjqw9wFYYDLLhmw7oMmOYzNdmTRiMga20,15393824
10
+ polycubetools/data/hexacubes.json,sha256=klfA_tcan_J0CFsOaQdmZgerbVIWDEN1yjW2tq31kd8,1986336
11
+ polycubetools/data/pentacubes.json,sha256=U3lpdByY9jPlDQRcuOUxS3xQBGpPwh9MNpEeSYTq7KE,259662
12
+ polycubetools-1.1.2.dist-info/licenses/LICENSE,sha256=dhlyH2n5C_8e4NuZqQk-CKpYSg8BaCHKep2mPLBZHiQ,1064
13
+ polycubetools-1.1.2.dist-info/METADATA,sha256=krGSfDBo88VmCFn-nArtxDm6k_c_e2i08pCsiIZFb1Q,1476
14
+ polycubetools-1.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
+ polycubetools-1.1.2.dist-info/top_level.txt,sha256=3pdiwnGYfuq3VSO8Y5L2wJYyU2U87PrfH5BBcBZjeKI,14
16
+ polycubetools-1.1.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +0,0 @@
1
- polycubetools/__init__.py,sha256=nvdLE5imasBZJQvtr79OrJ7eCPAVSirOWbGIkin96qM,372
2
- polycubetools/grid.py,sha256=UbUXxQ5opEAPKlPmbv7KIorUnRw41cuK7mkVoLDhqLE,7540
3
- polycubetools/hull.py,sha256=tM6pipw05fAWJ3N0KXgzCpXnZbQsRljPUXAinNXOd2w,4257
4
- polycubetools/models.py,sha256=5qi9WWOxYg8666x1LOkfyQoDSLf4Qzs3Z94-8N_zRLU,5128
5
- polycubetools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- polycubetools/solver.py,sha256=X_6dXQ9WBm4CRHXILvs2whXbxYjW-ZeChBczosKJ7vM,5918
7
- polycubetools/utils.py,sha256=80gA7bVXO5hxLuHNbsfC_s9Wz7shBubMWyKVm52Taw8,6928
8
- polycubetools/data/heptacubes.json,sha256=vQ9QA3J5vOtjqw9wFYYDLLhmw7oMmOYzNdmTRiMga20,15393824
9
- polycubetools/data/hexacubes.json,sha256=klfA_tcan_J0CFsOaQdmZgerbVIWDEN1yjW2tq31kd8,1986336
10
- polycubetools/data/pentacubes.json,sha256=U3lpdByY9jPlDQRcuOUxS3xQBGpPwh9MNpEeSYTq7KE,259662
11
- polycubetools-1.1.0.dist-info/licenses/LICENSE,sha256=dhlyH2n5C_8e4NuZqQk-CKpYSg8BaCHKep2mPLBZHiQ,1064
12
- polycubetools-1.1.0.dist-info/METADATA,sha256=Pwsw00NgRNvTq-YNjAA0KC-HhkmWMG3vQ7NBhb14qtQ,1476
13
- polycubetools-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- polycubetools-1.1.0.dist-info/top_level.txt,sha256=3pdiwnGYfuq3VSO8Y5L2wJYyU2U87PrfH5BBcBZjeKI,14
15
- polycubetools-1.1.0.dist-info/RECORD,,