pyrbd 0.3.0__py3-none-any.whl → 0.3.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.
pyrbd/block.py CHANGED
@@ -1,8 +1,11 @@
1
1
  """Module containing Block, Series and Group class definitions."""
2
2
 
3
- from typing import Optional
4
- from itertools import combinations
3
+ from typing import Optional, Generator
5
4
  from copy import deepcopy
5
+ from collections import namedtuple
6
+
7
+
8
+ Padding = namedtuple("Padding", ["n", "e", "s", "w"])
6
9
 
7
10
 
8
11
  class Block:
@@ -49,6 +52,7 @@ class Block:
49
52
  )
50
53
 
51
54
  arrow_options: str = "arrowcolor, thick"
55
+ arrow_length: float = 0.5
52
56
 
53
57
  def __init__(
54
58
  self,
@@ -70,7 +74,13 @@ class Block:
70
74
  if self.parent is None:
71
75
  return ""
72
76
 
73
- return f"[right={0.5 + self.shift[0]}cm of {self.parent.id}, yshift={self.shift[1]}cm]"
77
+ return " ".join(
78
+ [
79
+ f"[right={self.arrow_length + self.shift[0]}cm",
80
+ f"of {self.parent.id},",
81
+ f"yshift={self.shift[1]}cm]",
82
+ ]
83
+ )
74
84
 
75
85
  def arrow(self, connector_position: float) -> str:
76
86
  """Get TikZ arrow string.
@@ -97,13 +107,13 @@ class Block:
97
107
  ]
98
108
  )
99
109
 
100
- def get_node(self, connector_position: float = 0.25) -> str:
110
+ def get_node(self, connector_position: Optional[float] = None) -> str:
101
111
  """Get TikZ node string.
102
112
 
103
113
  Parameters
104
114
  ----------
105
- connector_position : float, default: 0.25
106
- distance in cm to right angle bend in connector
115
+ connector_position : Optional[float]
116
+ distance in cm to right angle bend in connector. Defaults to `0.5*arrow_length`.
107
117
 
108
118
  Returns
109
119
  -------
@@ -111,6 +121,9 @@ class Block:
111
121
  TikZ string for rendering block
112
122
  """
113
123
 
124
+ if connector_position is None:
125
+ connector_position = self.arrow_length / 2
126
+
114
127
  node = "".join(
115
128
  [
116
129
  "% Block\n",
@@ -123,6 +136,11 @@ class Block:
123
136
  )
124
137
  return node
125
138
 
139
+ def get_blocks(self) -> Generator["Block", None, None]:
140
+ """Yield child `Block` istances."""
141
+
142
+ yield self
143
+
126
144
  def __add__(self, block: "Block") -> "Series":
127
145
  """Add two `Block` instances to make a `Series` instance.
128
146
 
@@ -208,6 +226,10 @@ class Series(Block):
208
226
  ]
209
227
  )
210
228
 
229
+ internal_arrow_length = 0.3
230
+ pad = Padding(1, 1, 1, 2.5)
231
+ label_height = 5
232
+
211
233
  def __init__(
212
234
  self,
213
235
  blocks: list[Block],
@@ -219,11 +241,13 @@ class Series(Block):
219
241
 
220
242
  self.blocks = blocks
221
243
  self.blocks[0].id = f"{self.id}+0"
244
+ self.blocks[0].shift = (self.internal_arrow_length, 0)
222
245
  for i, (block, new_parent) in enumerate(
223
246
  zip(self.blocks[1::], self.blocks[0:-1]), start=1
224
247
  ):
225
248
  block.parent = new_parent
226
249
  block.id = f"{self.id}+{i}"
250
+ block.arrow_length = self.internal_arrow_length
227
251
 
228
252
  @property
229
253
  def background(self) -> str:
@@ -232,11 +256,13 @@ class Series(Block):
232
256
  if self.color in ("white", ""):
233
257
  return ""
234
258
 
259
+ pad = self.pad
260
+
235
261
  return "".join(
236
262
  [
237
263
  "\\begin{pgfonlayer}{background}\n",
238
- f"\\coordinate (sw) at ($({self.id}.south west)+(-1mm, -1mm)$);\n",
239
- f"\\coordinate (ne) at ($({self.id}.north east)+(1mm, 1mm)$);\n",
264
+ f"\\coordinate (sw) at ($({self.id}.south west)+(-{pad.w}mm, -{pad.s}mm)$);\n",
265
+ f"\\coordinate (ne) at ($({self.id}.north east)+({pad.e}mm, {pad.n}mm)$);\n",
240
266
  f"\\draw[{self.color}, thick] (sw) rectangle (ne);\n",
241
267
  "\\end{pgfonlayer}\n",
242
268
  ]
@@ -249,24 +275,27 @@ class Series(Block):
249
275
  if len(self.text) == 0:
250
276
  return ""
251
277
 
278
+ pad = self.pad
279
+
252
280
  return "".join(
253
281
  [
254
- f"\\coordinate (nw) at ($({self.id}.north west)+(-1mm, 1mm)$);\n",
255
- f"\\coordinate (ne) at ($({self.id}.north east)+(1mm, 1mm)$);\n",
256
- f"\\coordinate (n) at ($({self.id}.north)+(0mm, 1mm)$);\n",
282
+ f"\\coordinate (nw) at ($({self.id}.north west)+(-{pad.w}mm, {pad.n}mm)$);\n",
283
+ f"\\coordinate (ne) at ($({self.id}.north east)+({pad.e}mm, {pad.n}mm)$);\n",
284
+ "\\coordinate (n) at "
285
+ f"($({self.id}.north)+(0mm, {self.label_height / 2 + pad.n}mm)$);\n",
257
286
  f"\\draw[{self.color}, fill={self.color}!50, thick] (nw) ",
258
- "rectangle ($(ne)+(0, 0.5cm)$);\n",
259
- f"\\node[anchor=south] at (n) {{{self.text}}};\n",
287
+ f"rectangle ($(ne)+(0, {self.label_height}mm)$);\n",
288
+ f"\\node[anchor=center, inner sep=0pt, outer sep=0pt] at (n) {{{self.text}}};\n",
260
289
  ]
261
290
  )
262
291
 
263
- def get_node(self, connector_position: float = 0.25) -> str:
292
+ def get_node(self, connector_position: Optional[float] = None) -> str:
264
293
  """Get TikZ node string.
265
294
 
266
295
  Parameters
267
296
  ----------
268
- connector_position : float, default: 0.25
269
- distance in cm to right angle bend in connector
297
+ connector_position : Optional[float]
298
+ distance in cm to right angle bend in connector. Defaults to `0.5 * arrow_length`
270
299
 
271
300
  Returns
272
301
  -------
@@ -275,12 +304,15 @@ class Series(Block):
275
304
 
276
305
  """
277
306
 
307
+ if connector_position is None:
308
+ connector_position = self.arrow_length / 2
309
+
278
310
  block_nodes = "\n".join(
279
311
  block.get_node(connector_position) for block in self.blocks
280
312
  )
281
313
  series_node = "".join(
282
314
  [
283
- f"\\node[{self.tikz_options}]",
315
+ f"%%% Series\n\\node[{self.tikz_options}]",
284
316
  f"({self.id})",
285
317
  self.position,
286
318
  "{\\begin{tikzpicture}\n",
@@ -293,6 +325,11 @@ class Series(Block):
293
325
  )
294
326
  return series_node
295
327
 
328
+ def get_blocks(self) -> Generator[Block, None, None]:
329
+ yield from [
330
+ children for block in self.blocks for children in block.get_blocks()
331
+ ]
332
+
296
333
 
297
334
  class Group(Block):
298
335
  """Group of `Block` instances for vertical stacking.
@@ -322,6 +359,9 @@ class Group(Block):
322
359
  "anchor=west",
323
360
  ]
324
361
  )
362
+ internal_arrow_length = 0.3
363
+ pad = Padding(1, 1, 1, 1)
364
+ label_height = 5
325
365
 
326
366
  def __init__(
327
367
  self,
@@ -337,6 +377,7 @@ class Group(Block):
337
377
  block.shift = (0, shift)
338
378
  block.parent = self
339
379
  block.id = f"{self.id}-{i}"
380
+ block.arrow_length = self.internal_arrow_length
340
381
 
341
382
  @property
342
383
  def shifts(self) -> list[float]:
@@ -359,11 +400,13 @@ class Group(Block):
359
400
  if self.color in ("white", ""):
360
401
  return ""
361
402
 
403
+ pad = self.pad
404
+
362
405
  return "".join(
363
406
  [
364
407
  "\\begin{pgfonlayer}{background}\n",
365
- f"\\coordinate (sw) at ($({self.id}.south west)+(-1mm, -1mm)$);\n",
366
- f"\\coordinate (ne) at ($({self.id}.north east)+(1mm, 1mm)$);\n",
408
+ f"\\coordinate (sw) at ($({self.id}.south west)+(-{pad.w}mm, -{pad.s}mm)$);\n",
409
+ f"\\coordinate (ne) at ($({self.id}.north east)+({pad.e}mm, {pad.n}mm)$);\n",
367
410
  f"\\draw[{self.color}, thick] (sw) rectangle (ne);\n",
368
411
  "\\end{pgfonlayer}\n",
369
412
  ]
@@ -376,14 +419,17 @@ class Group(Block):
376
419
  if len(self.text) == 0:
377
420
  return ""
378
421
 
422
+ pad = self.pad
423
+
379
424
  return "".join(
380
425
  [
381
- f"\\coordinate (nw) at ($({self.id}.north west)+(-1mm, 1mm)$);\n",
382
- f"\\coordinate (ne) at ($({self.id}.north east)+(1mm, 1mm)$);\n",
383
- f"\\coordinate (n) at ($({self.id}.north)+(0mm, 1mm)$);\n",
426
+ f"\\coordinate (nw) at ($({self.id}.north west)+(-{pad.w}mm, {pad.n}mm)$);\n",
427
+ f"\\coordinate (ne) at ($({self.id}.north east)+({pad.e}mm, {pad.n}mm)$);\n",
428
+ "\\coordinate (n) at ",
429
+ f"($({self.id}.north)+(0mm, {self.label_height / 2 + pad.n}mm)$);\n",
384
430
  f"\\draw[{self.color}, fill={self.color}!50, thick] (nw) ",
385
- "rectangle ($(ne)+(0, 0.5cm)$);\n",
386
- f"\\node[anchor=south] at (n) {{{self.text}}};\n",
431
+ f"rectangle ($(ne)+(0, {self.label_height}mm)$);\n",
432
+ f"\\node[anchor=center, inner sep=0pt, outer sep=0pt] at (n) {{{self.text}}};\n",
387
433
  ]
388
434
  )
389
435
 
@@ -410,25 +456,41 @@ class Group(Block):
410
456
  def arrows(self) -> str:
411
457
  """Get TikZ string for arrow connecting stacked blocks."""
412
458
 
459
+ scaling = 0.75
460
+
461
+ series_blocks = [block for block in self.blocks if isinstance(block, Series)]
462
+ series_blocks.sort(
463
+ key=lambda block: len(list(block.get_blocks())), reverse=True
464
+ )
465
+
466
+ if len(series_blocks) > 0:
467
+ longest_series_index = self.blocks.index(series_blocks[0])
468
+ else:
469
+ longest_series_index = 0
470
+ blocks = deepcopy(self.blocks)
471
+ longest_series = blocks.pop(longest_series_index)
472
+
413
473
  return "\n".join(
414
474
  [
415
475
  " ".join(
416
476
  [
417
- f"\\draw[{self.arrow_options}, rectangle line]",
418
- f"({block1.id}.east) to ({block2.id}.east);\n",
477
+ f"\\draw[{self.arrow_options},",
478
+ f"rectangle line={scaling * self.internal_arrow_length}cm]",
479
+ f"({longest_series.id}.east) to ({block.id}.east);\n",
419
480
  ]
420
481
  )
421
- for (block1, block2) in combinations(self.blocks, 2)
482
+ for block in blocks
422
483
  ]
423
484
  )
424
485
 
425
- def get_node(self, connector_position: float = 0.0) -> str:
486
+ def get_node(self, connector_position: Optional[float] = None) -> str:
426
487
  """Get TikZ node string.
427
488
 
428
489
  Parameters
429
490
  ----------
430
- connector_position : float, default: 0.0
431
- distance in cm to right angle bend in connector
491
+ connector_position : Optional[float]
492
+ distance in cm to right angle bend in connector.
493
+ Locked to 0.0 for `Group` class
432
494
 
433
495
  Returns
434
496
  -------
@@ -436,12 +498,15 @@ class Group(Block):
436
498
  TikZ string for rendering group
437
499
  """
438
500
 
501
+ connector_position = 0.0
502
+
439
503
  block_nodes = "\n".join(
440
504
  block.get_node(connector_position) for block in self.blocks
441
505
  )
442
506
 
443
507
  group_node = "".join(
444
508
  [
509
+ "%%% Group\n"
445
510
  f"\\node[anchor=west, outer sep=0pt, inner sep=0pt, align=center] ({self.id}) ",
446
511
  self.position,
447
512
  "{\\begin{tikzpicture}\n",
@@ -456,3 +521,6 @@ class Group(Block):
456
521
  )
457
522
 
458
523
  return group_node
524
+
525
+ def get_blocks(self) -> Generator[Block, None, None]:
526
+ yield from self.blocks
pyrbd/diagram.py CHANGED
@@ -143,7 +143,9 @@ class Diagram:
143
143
  """
144
144
 
145
145
  try:
146
- subprocess.check_call(["latexmk", f"{self.filename}.tex", "--silent"])
146
+ subprocess.check_call(
147
+ ["latexmk", "--lualatex", f"{self.filename}.tex", "--silent"]
148
+ )
147
149
  subprocess.check_call(["latexmk", "-c", f"{self.filename}.tex"])
148
150
  if clear_source:
149
151
  subprocess.check_call(["rm", f"{self.filename}.tex"])
pyrbd/py.typed ADDED
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrbd
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Package for creating simple reliability block diagrams (RBDs) using LaTeX and TikZ.
5
5
  Project-URL: Repository, https://github.com/hghugdal/pyrbd
6
6
  Project-URL: Issues, https://github.com/hghugdal/pyrbd/issues
@@ -11,6 +11,7 @@ License-File: LICENSE
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Requires-Python: >=3.10
14
+ Requires-Dist: pre-commit>=4.3.0
14
15
  Requires-Dist: pymupdf>=1.26.3
15
16
  Description-Content-Type: text/markdown
16
17
 
@@ -46,6 +47,6 @@ diagram.compile()
46
47
  ```
47
48
 
48
49
  producing the following diagram
49
- <div><img src="https://raw.githubusercontent.com/hghugdal/pyrbd/23d7f00ca74e8465df3760821488b4bb78df803c/docs/examples/simple_RBD.svg" width=500/></div>
50
+ <div><img src="https://raw.githubusercontent.com/hghugdal/pyrbd/e11808e9a40c902f0e1c2c2b0b42ca0d90170cd1/docs/examples/simple_RBD.svg" width=500/></div>
50
51
 
51
52
  For more examples, visit the [documentation](https://hghugdal.github.io/pyrbd/).
@@ -0,0 +1,8 @@
1
+ pyrbd/__init__.py,sha256=mpsb022BX9eDGSBhuYIr2gOr4sg_M9sYbPGHLPIdvl0,219
2
+ pyrbd/block.py,sha256=xRb4FnPTCA6CEAax2q3GRsngtzJvXlg9wCqr5q3nkEQ,14576
3
+ pyrbd/diagram.py,sha256=YiU2tslUh4HbtuVP2dXSsVQFKpOjLl_ArSYjCSiQoks,6806
4
+ pyrbd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ pyrbd-0.3.2.dist-info/METADATA,sha256=3JIMvqVdxlQq-30y4omd_yipT8YfgxZOmZxq5SopBBU,2402
6
+ pyrbd-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ pyrbd-0.3.2.dist-info/licenses/LICENSE,sha256=mxXMgWpFgk71JpEEElFrb9FJxeoaDgvrU8gjUUU9LzA,1069
8
+ pyrbd-0.3.2.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- pyrbd/__init__.py,sha256=mpsb022BX9eDGSBhuYIr2gOr4sg_M9sYbPGHLPIdvl0,219
2
- pyrbd/block.py,sha256=qQJx2Sn_SvAnm3Chz1f078nTzbS61LSFdxaEveu2RcY,12319
3
- pyrbd/diagram.py,sha256=llZ0ncDmyJRg7zyQfTXAzbEM1W66wyHs9gfiovy-ujI,6762
4
- pyrbd-0.3.0.dist-info/METADATA,sha256=KEMFXHPv7ykDCYbh2fOOjcKPyqhOchXl0Ck2pc3ODwQ,2369
5
- pyrbd-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
- pyrbd-0.3.0.dist-info/licenses/LICENSE,sha256=mxXMgWpFgk71JpEEElFrb9FJxeoaDgvrU8gjUUU9LzA,1069
7
- pyrbd-0.3.0.dist-info/RECORD,,
File without changes