1
1
import functools
2
- from typing import List , Set , get_args
2
+ from typing import Dict , List , Set , get_args
3
3
4
4
from classy_blocks .items .block import Block
5
5
from classy_blocks .items .wires .axis import Axis
6
6
from classy_blocks .mesh import Mesh
7
- from classy_blocks .types import AxisType , ChopTakeType
7
+ from classy_blocks .types import ChopTakeType , DirectionType
8
8
9
9
10
10
@functools .lru_cache (maxsize = 3000 ) # that's for 1000 blocks
11
- def _get_block_from_axis (mesh : Mesh , axis : Axis ) -> Block :
11
+ def get_block_from_axis (mesh : Mesh , axis : Axis ) -> Block :
12
12
for block in mesh .blocks :
13
- for index in get_args (AxisType ):
14
- if block .axes [index ] == axis :
15
- return block
13
+ if axis in block .axes :
14
+ return block
16
15
17
16
raise RuntimeError ("Block for Axis not found!" )
18
17
19
18
20
- class Layer :
21
- """A collection of all blocks on a specific AxisIndex """
19
+ class Instruction :
20
+ """A descriptor that tells in which direction the specific block can be chopped. """
22
21
23
- def __init__ (self , axis : AxisType , blocks : Set [Block ]):
24
- self .axis = axis
25
- self .blocks = blocks
22
+ def __init__ (self , block : Block ):
23
+ self .block = block
24
+ self .directions : List [bool ] = [False ] * 3
25
+
26
+ @property
27
+ def is_defined (self ):
28
+ return all (self .directions )
29
+
30
+ def __hash__ (self ) -> int :
31
+ return id (self )
32
+
33
+
34
+ class Row :
35
+ """A string of blocks that must share the same count
36
+ because they sit next to each other.
37
+
38
+ This needs not be an actual 'string' of blocks because,
39
+ depending on blocking, a whole 'layer' of blocks can be
40
+ chopped by specifying a single block only (for example, direction 2 in a cylinder)"""
41
+
42
+ def __init__ (self , direction : DirectionType ):
43
+ self .direction = direction
44
+ self .blocks : Set [Block ] = set ()
45
+
46
+ def add_block (self , block : Block ) -> None :
47
+ self .blocks .add (block )
26
48
27
49
def get_length (self , take : ChopTakeType = "avg" ):
28
50
lengths : List [float ] = []
29
51
30
52
for block in self .blocks :
31
- lengths += block .axes [self .axis ].lengths
53
+ lengths += block .axes [self .direction ].lengths
32
54
33
55
if take == "min" :
34
56
return min (lengths )
@@ -40,74 +62,72 @@ def get_length(self, take: ChopTakeType = "avg"):
40
62
41
63
42
64
class Catalogue :
43
- """A collection of layers on a specified axis"""
65
+ """A collection of rows on a specified axis"""
44
66
45
- def __init__ (self , axis : AxisType ):
46
- self .axis = axis
47
- self .layers : List [Layer ] = []
67
+ def __init__ (self , mesh : Mesh ):
68
+ self .mesh = mesh
48
69
49
- def has_block (self , block : Block ) -> bool :
50
- for layer in self .layers :
51
- if block in layer .blocks :
52
- return True
70
+ self .rows : Dict [DirectionType , List [Row ]] = {0 : [], 1 : [], 2 : []}
71
+ self .instructions = [Instruction (block ) for block in mesh .blocks ]
53
72
54
- return False
73
+ for i in get_args (DirectionType ):
74
+ self ._populate (i )
55
75
56
- def add_layer (self , blocks : Set [Block ]):
57
- """Adds a block to the appropriate Layer"""
58
- layer = Layer (self .axis , blocks )
59
- self .layers .append (layer )
76
+ def _get_undefined_instructions (self , direction : DirectionType ) -> List [Instruction ]:
77
+ return [i for i in self .instructions if not i .directions [direction ]]
60
78
61
- def get_layer (self , block : Block ):
62
- for layer in self .layers :
63
- if block in layer .blocks :
64
- return layer
79
+ def _find_instruction (self , block : Block ):
80
+ # TODO: perform dedumbing on this exquisite piece of code
81
+ for instruction in self .instructions :
82
+ if instruction .block == block :
83
+ return instruction
65
84
66
- # TODO: create a custom exception
67
- raise RuntimeError ("No such layer!" )
85
+ raise RuntimeError (f"No instruction found for block { block } " )
68
86
87
+ def _add_block_to_row (self , row : Row , instruction : Instruction , direction : DirectionType ) -> None :
88
+ row .add_block (instruction .block )
89
+ instruction .directions [direction ] = True
69
90
70
- class Probe :
71
- """Examines the mesh and gathers required data for auto chopping"""
91
+ block = instruction .block
72
92
73
- def __init__ ( self , mesh : Mesh ) :
74
- self . mesh = mesh
93
+ for neighbour_axis in block . axes [ direction ]. neighbours :
94
+ neighbour_block = get_block_from_axis ( self . mesh , neighbour_axis )
75
95
76
- self .catalogues = [Catalogue (axis ) for axis in get_args (AxisType )]
96
+ if neighbour_block in row .blocks :
97
+ continue
77
98
78
- for block in self .mesh .blocks :
79
- for axis in get_args (AxisType ):
80
- if self .catalogues [axis ].has_block (block ):
81
- continue
99
+ instruction = self ._find_instruction (neighbour_block )
82
100
83
- self .catalogues [ axis ]. add_layer ( self . _get_blocks_on_layer ( block , axis ))
101
+ self ._add_block_to_row ( row , instruction , neighbour_block . get_axis_direction ( neighbour_axis ))
84
102
85
- def _get_block_from_axis (self , axis : Axis ) -> Block :
86
- return _get_block_from_axis (self .mesh , axis )
103
+ def _populate (self , direction : DirectionType ) -> None :
104
+ while True :
105
+ undefined_instructions = self ._get_undefined_instructions (direction )
106
+ if len (undefined_instructions ) == 0 :
107
+ break
87
108
88
- def _get_blocks_on_layer (self , block : Block , axis : AxisType ) -> Set [Block ]:
89
- """Returns all blocks on the same 'layer' as the one in arguments"""
90
- # blocks to be returned
91
- blocks : Set [Block ] = set ()
92
- # blocks not to check again
93
- traversed : Set [Block ] = set ()
109
+ row = Row (direction )
110
+ self ._add_block_to_row (row , undefined_instructions [0 ], direction )
111
+ self .rows [direction ].append (row )
94
112
95
- def check (blk : Block ):
96
- if blk not in traversed :
97
- traversed .add (blk )
113
+ def get_row_blocks (self , block : Block , direction : DirectionType ) -> Set [Block ]:
114
+ for row in self .rows [direction ]:
115
+ if block in row .blocks :
116
+ return row .blocks
98
117
99
- for neighbour_axis in blk .axes [axis ].neighbours :
100
- neighbour_block = self ._get_block_from_axis (neighbour_axis )
101
- blocks .add (neighbour_block )
118
+ # TODO: make a custom exception
119
+ raise RuntimeError (f"Direction { direction } of { block } not in catalogue" )
102
120
103
- check (self ._get_block_from_axis (neighbour_axis ))
104
121
105
- check (block )
122
+ class Probe :
123
+ """Examines the mesh and gathers required data for auto chopping"""
106
124
107
- return blocks
125
+ def __init__ (self , mesh : Mesh ):
126
+ self .mesh = mesh
127
+ self .catalogue = Catalogue (self .mesh )
108
128
109
- def get_blocks_on_layer (self , block : Block , axis : AxisType ) :
110
- return self .catalogues [ axis ]. get_layer (block ). blocks
129
+ def get_row_blocks (self , block : Block , direction : DirectionType ) -> Set [ Block ] :
130
+ return self .catalogue . get_row_blocks (block , direction )
111
131
112
- def get_layers (self , axis : AxisType ) -> List [Layer ]:
113
- return self .catalogues [ axis ]. layers
132
+ def get_rows (self , direction : DirectionType ) -> List [Row ]:
133
+ return self .catalogue . rows [ direction ]
0 commit comments