archgraph-layout 2.0.1__tar.gz → 2.0.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 (57) hide show
  1. {archgraph_layout-2.0.1/archgraph_layout.egg-info → archgraph_layout-2.0.2}/PKG-INFO +1 -1
  2. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/__init__.py +1 -1
  3. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/engine.py +4 -0
  4. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/hierarchy.py +4 -0
  5. archgraph_layout-2.0.2/archgraph/placement/notes.py +81 -0
  6. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2/archgraph_layout.egg-info}/PKG-INFO +1 -1
  7. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph_layout.egg-info/SOURCES.txt +1 -0
  8. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/docs/xml-reference.md +4 -0
  9. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/halcyon7.xml +1 -22
  10. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/LICENSE +0 -0
  11. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/MANIFEST.in +0 -0
  12. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/README.md +0 -0
  13. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/__main__.py +0 -0
  14. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/cli.py +0 -0
  15. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/geometry.py +0 -0
  16. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/model.py +0 -0
  17. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/parser.py +0 -0
  18. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/__init__.py +0 -0
  19. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/congestion.py +0 -0
  20. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/detail.py +0 -0
  21. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/floorplan.py +0 -0
  22. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/graphalgo.py +0 -0
  23. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/layered.py +0 -0
  24. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/ports.py +0 -0
  25. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/sizing.py +0 -0
  26. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/placement/vpsc.py +0 -0
  27. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/portlabels.py +0 -0
  28. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/progress.py +0 -0
  29. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/quality.py +0 -0
  30. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/__init__.py +0 -0
  31. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/html.py +0 -0
  32. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/jsonout.py +0 -0
  33. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/png.py +0 -0
  34. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/shapes.py +0 -0
  35. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/render/svg.py +0 -0
  36. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/routing/__init__.py +0 -0
  37. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/routing/grid.py +0 -0
  38. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/routing/nudge.py +0 -0
  39. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/routing/router.py +0 -0
  40. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/shapegeometry.py +0 -0
  41. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/styles.py +0 -0
  42. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph/textmetrics.py +0 -0
  43. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph_layout.egg-info/dependency_links.txt +0 -0
  44. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph_layout.egg-info/entry_points.txt +0 -0
  45. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph_layout.egg-info/requires.txt +0 -0
  46. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/archgraph_layout.egg-info/top_level.txt +0 -0
  47. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/accelerator_mesh.xml +0 -0
  48. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/automotive.xml +0 -0
  49. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/basic.xml +0 -0
  50. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/datacenter.xml +0 -0
  51. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/flat_network.xml +0 -0
  52. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/fpga_pipeline.xml +0 -0
  53. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/halcyon7_lib.xml +0 -0
  54. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/halcyon7_styles.xml +0 -0
  55. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/examples/soc.xml +0 -0
  56. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/pyproject.toml +0 -0
  57. {archgraph_layout-2.0.1 → archgraph_layout-2.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: archgraph-layout
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Hierarchical architecture diagrams from XML with floorplanning and orthogonal routing
5
5
  Author: Ellie Hartung
6
6
  License-Expression: MIT
@@ -23,7 +23,7 @@ from .progress import report
23
23
  from .placement import engine as _engine # noqa: E402
24
24
  from . import routing as _routing # noqa: E402
25
25
 
26
- __version__ = "2.0.1"
26
+ __version__ = "2.0.2"
27
27
  __all__ = ["load", "loads", "layout", "route", "build", "to_svg", "to_html", "to_json", "to_png", "save",
28
28
  "ArchGraphError", "Graph", "__version__"]
29
29
 
@@ -11,6 +11,7 @@ from . import sizing
11
11
  from .hierarchy import (HierarchicalPlacer, apply_keep_together, auto_cluster, build_layout_edges, lift_hints,
12
12
  translate_element)
13
13
  from .ports import assign_ports, distribute_ports, slide_ports
14
+ from .notes import compact_notes
14
15
 
15
16
 
16
17
  def unify_instances(g: Graph) -> int:
@@ -85,6 +86,9 @@ def finish_ports(g: Graph, placer):
85
86
  assign_ports(g, slide=False)
86
87
  if g.settings.align_edges:
87
88
  placer.topdown(g.root)
89
+ if compact_notes(g):
90
+ # A relocated annotation may now face a different side of its target.
91
+ assign_ports(g, slide=False)
88
92
  distribute_ports(g)
89
93
  slide_ports(g)
90
94
  normalize(g)
@@ -92,6 +92,10 @@ def build_layout_edges(g: Graph) -> List[LEdge]:
92
92
  a, b = e.src.element, e.dst.element
93
93
  flow = e.constraint and e.directed and "__note__" not in e.classes
94
94
  w = e.weight * (1.0 + 0.15 * (e.multiplicity - 1))
95
+ if "__note__" in e.classes:
96
+ # Annotations should stay with their subject. They remain auxiliary
97
+ # edges so they impose no signal flow, crossings or hard alignment.
98
+ w *= 6.0
95
99
  le = LEdge(a, e.src.port, b, e.dst.port, w, flow, e.straight, e)
96
100
  le.aux = "__note__" in e.classes
97
101
  le.scope = g.edge_scope(a, b)
@@ -0,0 +1,81 @@
1
+ """Move targeted annotations into nearby free space without moving circuit blocks."""
2
+ from __future__ import annotations
3
+
4
+ from ..geometry import Rect
5
+ from ..model import Graph, Node, Subsystem
6
+ from ..portlabels import port_label_geometry, port_text_size
7
+ from .sizing import subsystem_padding
8
+
9
+
10
+ def _distance(note: Rect, target: Rect):
11
+ dx = max(target.x0-note.x1, note.x0-target.x1, 0)
12
+ dy = max(target.y0-note.y1, note.y0-target.y1, 0)
13
+ # Prefer a short leader and alignment with the target, including when the
14
+ # target is a large subsystem with many equally close boundary positions.
15
+ return dx+dy+.08*(abs(note.cx-target.cx)+abs(note.cy-target.cy))
16
+
17
+
18
+ def compact_notes(g: Graph) -> int:
19
+ """Improve note proximity within the existing parent frame and free gaps.
20
+
21
+ Detailed placement preserves sibling ordering. An annotation can instead
22
+ move around a sibling into a closer gap, provided it covers no other block
23
+ or external port name. Explicit hints and table layouts keep their positions.
24
+ """
25
+ pinned = {el for hint in g.hints for el in hint.elements}
26
+ links = [e for e in g.edges if "__note__" in e.classes and
27
+ isinstance(e.src.element, Node) and e.src.element.is_note]
28
+ moved = set()
29
+ for _ in range(2):
30
+ changed = False
31
+ for edge in links:
32
+ note, target = edge.src.element, edge.dst.element
33
+ parent = note.parent
34
+ if parent is None or note in pinned or parent.algorithm in ("row", "column", "grid", "layered"):
35
+ continue
36
+ spacing = parent.spacing if parent.spacing is not None else g.settings.node_spacing
37
+ gap = max(spacing, 2*g.settings.node_margin+g.settings.edge_spacing)
38
+ pad = 0 if parent is g.root else subsystem_padding(parent, g.settings)
39
+ frame = parent.rect
40
+ bounds = Rect(frame.x0+pad, frame.y0+pad+parent.header_h,
41
+ frame.x1-pad, frame.y1-pad)
42
+ if bounds.w < note.w or bounds.h < note.h:
43
+ continue
44
+ obstacles = []
45
+ for sibling in parent.children:
46
+ if sibling is note:
47
+ continue
48
+ obstacles.append(sibling.rect.inflate(gap))
49
+ if isinstance(sibling, Subsystem) and sibling.visible:
50
+ for p in sibling.ports.values():
51
+ if port_text_size(p, g.settings) != (0.0, 0.0):
52
+ obstacles.append(port_label_geometry(p, g.settings, False)[3].inflate(8))
53
+ hw, hh = note.w/2, note.h/2
54
+ xs = {note.x, target.x, bounds.x0+hw, bounds.x1-hw,
55
+ target.rect.x0-hw-gap, target.rect.x1+hw+gap}
56
+ ys = {note.y, target.y, bounds.y0+hh, bounds.y1-hh,
57
+ target.rect.y0-hh-gap, target.rect.y1+hh+gap}
58
+ for ob in obstacles:
59
+ xs.update((ob.x0-hw, ob.x1+hw))
60
+ ys.update((ob.y0-hh, ob.y1+hh))
61
+ xs = [x for x in xs if bounds.x0+hw-1e-6 <= x <= bounds.x1-hw+1e-6]
62
+ ys = [y for y in ys if bounds.y0+hh-1e-6 <= y <= bounds.y1-hh+1e-6]
63
+ current = _distance(note.rect, target.rect)
64
+ candidates = []
65
+ for x in xs:
66
+ for y in ys:
67
+ r = Rect.from_center(x, y, note.w, note.h)
68
+ distance = _distance(r, target.rect)
69
+ if distance < current-.5:
70
+ candidates.append((distance, abs(x-note.x)+abs(y-note.y), x, y, r))
71
+ candidates.sort(key=lambda c: c[:4])
72
+ for _, _, x, y, r in candidates:
73
+ if any(r.overlaps(ob) for ob in obstacles):
74
+ continue
75
+ note.x, note.y = x, y
76
+ moved.add(note)
77
+ changed = True
78
+ break
79
+ if not changed:
80
+ break
81
+ return len(moved)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: archgraph-layout
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Hierarchical architecture diagrams from XML with floorplanning and orthogonal routing
5
5
  Author: Ellie Hartung
6
6
  License-Expression: MIT
@@ -22,6 +22,7 @@ archgraph/placement/floorplan.py
22
22
  archgraph/placement/graphalgo.py
23
23
  archgraph/placement/hierarchy.py
24
24
  archgraph/placement/layered.py
25
+ archgraph/placement/notes.py
25
26
  archgraph/placement/ports.py
26
27
  archgraph/placement/sizing.py
27
28
  archgraph/placement/vpsc.py
@@ -509,6 +509,10 @@ A note without `target` is a free-standing annotation in its enclosing
509
509
  subsystem. A targeted note normally shares its target's parent; use
510
510
  `inside="true"` to place a note inside a target subsystem. `id`, `width`, `class`,
511
511
  `fill`, and `text` optionally customize the note.
512
+ Targeted notes prefer nearby open space and can move around sibling blocks to
513
+ shorten their dotted leader. They keep the configured spacing, stay inside their
514
+ parent, and avoid external port labels. Explicit layout hints involving a note
515
+ and table/row/column layouts retain their placement.
512
516
 
513
517
  `legend="auto"` uses classes with a `legend` attribute. Explicit `<legend><entry
514
518
  .../></legend>` entries switch the default auto mode to `always`. Set
@@ -1,26 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <!--
3
- ╔══════════════════════════════════════════════════════════════════════╗
4
- ║ HALCYON-7 · agile X-band SAR constellation node ("dark-vessel hunter") ║
5
- ╚══════════════════════════════════════════════════════════════════════╝
6
- Mission: image the ocean through cloud and night with a 64-element digital-
7
- beamforming SAR, focus the raw echoes on board, find ships with a CNN, match
8
- them against AIS, and flag the ones running dark. Alerts reach peers over
9
- 10 Gbps laser crosslinks and reach the ground in minutes, not hours.
10
-
11
- Architecture is real-world shaped (ESA-style regulated 28 V bus, GR740 OBC,
12
- SpaceWire/SpaceFibre, CMG agility, Hall EP); the spacecraft is fictional.
13
-
14
- Files (keep together, includes are relative):
15
- halcyon7.xml ← this file
16
- halcyon7_lib.xml ← templates; includes ↓
17
- halcyon7_styles.xml ← classes
18
-
19
- Stress knobs: PANELS / TILES / TRMS scale the phased array (PANELS*TILES*TRMS
20
- T/R modules, ~5 nodes and ~5 edges each). CMGS, HEADS, BMODS, RCS, WING_PANELS
21
- scale the rest.
22
- -->
23
- <archgraph title="HALCYON-7 · Dark-Vessel Hunter"
2
+ <archgraph title="HALCYON-7"
24
3
  direction="LR" aspect="16:9" theme="blueprint" quality="high" seed="1337"
25
4
  author="archgraph stress test" revision="C2" date="2026-10-10">
26
5
  <subtitle>Agile X-band DBF-SAR · on-board AI · 10 Gbps laser crosslinks · SSO 520 km dawn-dusk</subtitle>