archgraph-layout 1.0.1__tar.gz → 2.0.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.
- {archgraph_layout-1.0.1/archgraph_layout.egg-info → archgraph_layout-2.0.0}/PKG-INFO +4 -4
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/README.md +3 -3
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/__init__.py +2 -2
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/model.py +1 -10
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/parser.py +20 -24
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/hierarchy.py +6 -29
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/ports.py +370 -359
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/sizing.py +60 -54
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/portlabels.py +12 -1
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/html.py +3 -6
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/jsonout.py +4 -4
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/shapes.py +129 -221
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/svg.py +35 -79
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/routing/__init__.py +6 -23
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/routing/grid.py +2 -6
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/routing/router.py +1127 -1122
- archgraph_layout-2.0.0/archgraph/shapegeometry.py +261 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/styles.py +6 -10
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/textmetrics.py +0 -13
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0/archgraph_layout.egg-info}/PKG-INFO +4 -4
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph_layout.egg-info/SOURCES.txt +1 -1
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/docs/xml-reference.md +19 -14
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/automotive.xml +1 -1
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/basic.xml +3 -3
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/datacenter.xml +1 -1
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/fpga_pipeline.xml +1 -1
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/halcyon7.xml +54 -54
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/halcyon7_lib.xml +14 -14
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/halcyon7_styles.xml +2 -4
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/soc.xml +5 -5
- archgraph_layout-1.0.1/archgraph/routing/labels.py +0 -156
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/LICENSE +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/MANIFEST.in +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/__main__.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/cli.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/geometry.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/__init__.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/congestion.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/detail.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/engine.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/floorplan.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/graphalgo.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/layered.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/placement/vpsc.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/progress.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/quality.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/__init__.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/render/png.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph/routing/nudge.py +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph_layout.egg-info/dependency_links.txt +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph_layout.egg-info/entry_points.txt +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph_layout.egg-info/requires.txt +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/archgraph_layout.egg-info/top_level.txt +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/accelerator_mesh.xml +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/examples/flat_network.xml +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/pyproject.toml +0 -0
- {archgraph_layout-1.0.1 → archgraph_layout-2.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: archgraph-layout
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Hierarchical architecture diagrams from XML with floorplanning and orthogonal routing
|
|
5
5
|
Author: Ellie Hartung
|
|
6
6
|
License-Expression: MIT
|
|
@@ -96,7 +96,7 @@ XML ──► parse/expand ──► size blocks ──► auto-cluster huge lev
|
|
|
96
96
|
──► hierarchical floorplanning (bottom-up, several passes)
|
|
97
97
|
──► detailed QP placement + edge alignment (bottom-up, then top-down)
|
|
98
98
|
──► port sides (router-chosen) ──► orthogonal routing ──► congestion-driven spacing
|
|
99
|
-
──► nudging ──►
|
|
99
|
+
──► nudging ──► SVG / HTML / JSON / PNG
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
**Floorplanning (placement).** Each subsystem level is a floorplanning problem over
|
|
@@ -151,8 +151,8 @@ the border of a subsystem that contains neither end is almost forbidden. Specifi
|
|
|
151
151
|
painted over a hop. Crossings closer together than one hop share a single flat bridge.
|
|
152
152
|
Each hop bumps to the side where the crossed wire has room (clear of its arrowheads,
|
|
153
153
|
corners and neighbouring parallel wires). A crossing right next to a corner of the
|
|
154
|
-
horizontal wire hops sideways on the vertical wire instead. Bit-width markers
|
|
155
|
-
|
|
154
|
+
horizontal wire hops sideways on the vertical wire instead. Bit-width markers
|
|
155
|
+
keep clear of crossings.
|
|
156
156
|
|
|
157
157
|
---
|
|
158
158
|
|
|
@@ -71,7 +71,7 @@ XML ──► parse/expand ──► size blocks ──► auto-cluster huge lev
|
|
|
71
71
|
──► hierarchical floorplanning (bottom-up, several passes)
|
|
72
72
|
──► detailed QP placement + edge alignment (bottom-up, then top-down)
|
|
73
73
|
──► port sides (router-chosen) ──► orthogonal routing ──► congestion-driven spacing
|
|
74
|
-
──► nudging ──►
|
|
74
|
+
──► nudging ──► SVG / HTML / JSON / PNG
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
**Floorplanning (placement).** Each subsystem level is a floorplanning problem over
|
|
@@ -126,8 +126,8 @@ the border of a subsystem that contains neither end is almost forbidden. Specifi
|
|
|
126
126
|
painted over a hop. Crossings closer together than one hop share a single flat bridge.
|
|
127
127
|
Each hop bumps to the side where the crossed wire has room (clear of its arrowheads,
|
|
128
128
|
corners and neighbouring parallel wires). A crossing right next to a corner of the
|
|
129
|
-
horizontal wire hops sideways on the vertical wire instead. Bit-width markers
|
|
130
|
-
|
|
129
|
+
horizontal wire hops sideways on the vertical wire instead. Bit-width markers
|
|
130
|
+
keep clear of crossings.
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
@@ -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__ = "
|
|
26
|
+
__version__ = "2.0.0"
|
|
27
27
|
__all__ = ["load", "loads", "layout", "route", "build", "to_svg", "to_html", "to_json", "to_png", "save",
|
|
28
28
|
"ArchGraphError", "Graph", "__version__"]
|
|
29
29
|
|
|
@@ -45,7 +45,7 @@ def layout(g: Graph) -> Graph:
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def route(g: Graph) -> Graph:
|
|
48
|
-
"""Route all connections and buses
|
|
48
|
+
"""Route all connections and buses and nudge them apart."""
|
|
49
49
|
_routing.route(g)
|
|
50
50
|
from .geometry import Rect
|
|
51
51
|
rects = [e.rect for e in g.root.iter_descendants()]
|
|
@@ -53,7 +53,6 @@ class Settings:
|
|
|
53
53
|
junction_dots: bool = True
|
|
54
54
|
shadows: bool = True
|
|
55
55
|
background_grid: bool = False
|
|
56
|
-
edge_labels: bool = True
|
|
57
56
|
default_shape: str = "box"
|
|
58
57
|
bundle_parallel: bool = False # merge parallel edges between the same endpoints
|
|
59
58
|
margin: float = 24.0 # outer margin of the drawing
|
|
@@ -184,6 +183,7 @@ class Node(Element):
|
|
|
184
183
|
min_h: Optional[float] = None
|
|
185
184
|
virtual: bool = False # internal helper node (not drawn)
|
|
186
185
|
is_note: bool = False
|
|
186
|
+
layout_direction: str = "LR" # inherited flow used by shape geometry
|
|
187
187
|
|
|
188
188
|
kind = "node"
|
|
189
189
|
|
|
@@ -246,9 +246,6 @@ class Edge:
|
|
|
246
246
|
id: str
|
|
247
247
|
src: Endpoint
|
|
248
248
|
dst: Endpoint
|
|
249
|
-
label: str = ""
|
|
250
|
-
src_label: str = ""
|
|
251
|
-
dst_label: str = ""
|
|
252
249
|
classes: List[str] = field(default_factory=list)
|
|
253
250
|
attrs: Dict[str, str] = field(default_factory=dict)
|
|
254
251
|
style: Dict[str, Any] = field(default_factory=dict)
|
|
@@ -267,9 +264,6 @@ class Edge:
|
|
|
267
264
|
line: Optional[int] = None
|
|
268
265
|
# results
|
|
269
266
|
points: List[Point] = field(default_factory=list)
|
|
270
|
-
label_pos: Optional[Tuple[float, float, float, float]] = None # x, y, w, h (centre)
|
|
271
|
-
src_label_pos: Optional[Tuple[float, float]] = None
|
|
272
|
-
dst_label_pos: Optional[Tuple[float, float]] = None
|
|
273
267
|
routed: bool = False
|
|
274
268
|
uid: int = field(default_factory=lambda: next(_counter))
|
|
275
269
|
|
|
@@ -286,14 +280,12 @@ class Edge:
|
|
|
286
280
|
@dataclass(eq=False, repr=False)
|
|
287
281
|
class BusTap:
|
|
288
282
|
endpoint: Endpoint
|
|
289
|
-
label: str = ""
|
|
290
283
|
direction: Optional[str] = None
|
|
291
284
|
|
|
292
285
|
|
|
293
286
|
@dataclass(eq=False, repr=False)
|
|
294
287
|
class Bus:
|
|
295
288
|
id: str
|
|
296
|
-
label: str = ""
|
|
297
289
|
taps: List[BusTap] = field(default_factory=list)
|
|
298
290
|
classes: List[str] = field(default_factory=list)
|
|
299
291
|
attrs: Dict[str, str] = field(default_factory=dict)
|
|
@@ -305,7 +297,6 @@ class Bus:
|
|
|
305
297
|
# results
|
|
306
298
|
segments: List[Tuple[Point, Point]] = field(default_factory=list)
|
|
307
299
|
junctions: List[Point] = field(default_factory=list)
|
|
308
|
-
label_pos: Optional[Tuple[float, float, float, float]] = None
|
|
309
300
|
routed: bool = False
|
|
310
301
|
uid: int = field(default_factory=lambda: next(_counter))
|
|
311
302
|
|
|
@@ -1086,25 +1086,28 @@ class Builder:
|
|
|
1086
1086
|
e.ports[pid] = p
|
|
1087
1087
|
return Endpoint(element=e, port=p, ref=ref)
|
|
1088
1088
|
|
|
1089
|
-
EDGE_ATTRS = {"from", "to", "src", "dst", "source", "target", "
|
|
1090
|
-
"dst-label", "head-label", "class", "dir", "directed", "arrowhead", "arrowtail", "weight",
|
|
1089
|
+
EDGE_ATTRS = {"from", "to", "src", "dst", "source", "target", "class", "dir", "directed", "arrowhead", "arrowtail", "weight",
|
|
1091
1090
|
"minlen", "constraint", "layout", "straight", "bits", "width-bits", "bus-width", "net",
|
|
1092
1091
|
"tooltip", "id", "nodes", "path", "priority", "lanes"}
|
|
1093
1092
|
|
|
1093
|
+
def _connection_attrs(self, el: ET.Element, attrs: Dict[str, Any]) -> Dict[str, Any]:
|
|
1094
|
+
"""Ignore retired connection labels while still accepting older XML files."""
|
|
1095
|
+
retired = {"label", "src-label", "tail-label", "dst-label", "head-label"}
|
|
1096
|
+
if retired.intersection(attrs) or el.find("label") is not None or (el.text or "").strip():
|
|
1097
|
+
self.graph.warn(f"{_loc(el)}: connection labels are no longer supported; ignored")
|
|
1098
|
+
return {k: v for k, v in attrs.items() if k not in retired}
|
|
1099
|
+
|
|
1094
1100
|
def _make_edges(self, el: ET.Element, extra: Dict[str, Any], sub: Subsystem):
|
|
1095
1101
|
g = self.graph
|
|
1096
1102
|
attrs = {k: v for k, v in el.attrib.items() if not k.startswith("__")}
|
|
1097
1103
|
attrs.update({k: v for k, v in extra.items() if not k.startswith("__")})
|
|
1104
|
+
attrs = self._connection_attrs(el, attrs)
|
|
1098
1105
|
where = _loc(el)
|
|
1099
1106
|
srcs = _split_refs(attrs.get("from") or attrs.get("src") or attrs.get("source"))
|
|
1100
1107
|
dsts = _split_refs(attrs.get("to") or attrs.get("dst") or attrs.get("target"))
|
|
1101
1108
|
if not srcs or not dsts:
|
|
1102
1109
|
self.errors.append(f"{where}: <{el.tag}> needs from= and to=")
|
|
1103
1110
|
return
|
|
1104
|
-
label = attrs.get("label")
|
|
1105
|
-
if label is None:
|
|
1106
|
-
lab_el = el.find("label")
|
|
1107
|
-
label = (lab_el.text or "").strip() if lab_el is not None else (el.text or "").strip()
|
|
1108
1111
|
classes = _split_refs(attrs.get("class"))
|
|
1109
1112
|
raw_style = {}
|
|
1110
1113
|
for k, v in attrs.items():
|
|
@@ -1127,11 +1130,9 @@ class Builder:
|
|
|
1127
1130
|
de = self._endpoint(d, where, eid, "d")
|
|
1128
1131
|
if se is None or de is None:
|
|
1129
1132
|
continue
|
|
1130
|
-
e = Edge(id=eid, src=se, dst=de,
|
|
1133
|
+
e = Edge(id=eid, src=se, dst=de, classes=list(classes), style=dict(style),
|
|
1131
1134
|
line=_line(el))
|
|
1132
1135
|
e.attrs = attrs
|
|
1133
|
-
e.src_label = attrs.get("src-label", attrs.get("tail-label", ""))
|
|
1134
|
-
e.dst_label = attrs.get("dst-label", attrs.get("head-label", ""))
|
|
1135
1136
|
dirv = (attrs.get("dir") or "").lower()
|
|
1136
1137
|
if attrs.get("directed") is not None:
|
|
1137
1138
|
e.directed = st.parse_bool(attrs.get("directed"), True)
|
|
@@ -1165,36 +1166,38 @@ class Builder:
|
|
|
1165
1166
|
g = self.graph
|
|
1166
1167
|
where = _loc(el)
|
|
1167
1168
|
bid = el.get("id") or f"bus{len(g.buses) + 1}"
|
|
1168
|
-
|
|
1169
|
+
attrs = self._connection_attrs(el, el.attrib)
|
|
1170
|
+
bus = Bus(id=bid, line=_line(el))
|
|
1169
1171
|
bus.classes = _split_refs(el.get("class"))
|
|
1170
1172
|
bus.bits = el.get("bits", el.get("width-bits", ""))
|
|
1171
1173
|
bus.orientation = el.get("orientation", "auto")
|
|
1172
1174
|
bus.tooltip = el.get("tooltip", "")
|
|
1173
|
-
raw = {k: v for k, v in
|
|
1174
|
-
if k not in ("id", "
|
|
1175
|
+
raw = {k: v for k, v in attrs.items()
|
|
1176
|
+
if k not in ("id", "class", "bits", "orientation", "taps", "tooltip", "width-bits")
|
|
1175
1177
|
and not k.startswith("__")}
|
|
1176
1178
|
if "style" in raw and raw["style"].lower() not in st.DASH_PATTERNS:
|
|
1177
1179
|
raw.update(st.parse_css_style(raw.pop("style")))
|
|
1178
1180
|
bus.style = st.normalize_style(raw)
|
|
1179
1181
|
k = 0
|
|
1180
|
-
tap_specs: List[Tuple[str,
|
|
1182
|
+
tap_specs: List[Tuple[str, Optional[str], Optional[int]]] = []
|
|
1181
1183
|
for t in _split_refs(el.get("taps")):
|
|
1182
|
-
tap_specs.append((t,
|
|
1184
|
+
tap_specs.append((t, None, _line(el)))
|
|
1183
1185
|
for c in el:
|
|
1184
1186
|
if c.tag in ("tap", "member", "endpoint", "connect", "pin"):
|
|
1187
|
+
self._connection_attrs(c, c.attrib)
|
|
1185
1188
|
ref = c.get("node") or c.get("ref") or c.get("to") or c.get("id")
|
|
1186
1189
|
if not ref:
|
|
1187
1190
|
self.errors.append(f"{_loc(c)}: <tap> needs node=")
|
|
1188
1191
|
continue
|
|
1189
1192
|
if c.get("port") and ":" not in ref:
|
|
1190
1193
|
ref = f"{ref}:{c.get('port')}"
|
|
1191
|
-
tap_specs.append((ref, c.get("
|
|
1192
|
-
for ref,
|
|
1194
|
+
tap_specs.append((ref, c.get("dir", c.get("direction")), _line(c)))
|
|
1195
|
+
for ref, d, ln in tap_specs:
|
|
1193
1196
|
ep = self._endpoint(ref, f"{_loc(el)}" if ln is None else f"{os.path.basename(el.get(FILE, ''))}:{ln}",
|
|
1194
1197
|
f"bus~{bid}~{k}", "t")
|
|
1195
1198
|
k += 1
|
|
1196
1199
|
if ep is not None:
|
|
1197
|
-
bus.taps.append(BusTap(endpoint=ep,
|
|
1200
|
+
bus.taps.append(BusTap(endpoint=ep, direction=d))
|
|
1198
1201
|
if len(bus.taps) < 2:
|
|
1199
1202
|
self.errors.append(f"{where}: bus '{bid}' needs at least two taps")
|
|
1200
1203
|
return
|
|
@@ -1355,11 +1358,6 @@ def _bundle_parallel(g: Graph):
|
|
|
1355
1358
|
continue
|
|
1356
1359
|
first = es[0]
|
|
1357
1360
|
first.multiplicity = sum(x.multiplicity for x in es)
|
|
1358
|
-
labels = [x.label for x in es if x.label]
|
|
1359
|
-
if labels:
|
|
1360
|
-
uniq = list(dict.fromkeys(labels))
|
|
1361
|
-
first.label = ", ".join(uniq[:3]) + (" …" if len(uniq) > 3 else "")
|
|
1362
|
-
first.label = (first.label + " " if first.label else "") + f"×{first.multiplicity}"
|
|
1363
1361
|
for x in es[1:]:
|
|
1364
1362
|
dropped.add(x)
|
|
1365
1363
|
for ep in (x.src, x.dst):
|
|
@@ -1435,8 +1433,6 @@ def _apply_styles(g: Graph):
|
|
|
1435
1433
|
base.update(e.style)
|
|
1436
1434
|
if "color" in base and "stroke" not in e.style:
|
|
1437
1435
|
base["stroke"] = base["color"]
|
|
1438
|
-
if "label-color" not in cls and "label-color" not in e.style and "color" in (cls.keys() | e.style.keys()):
|
|
1439
|
-
base["label-color"] = st.mix(base["color"], theme["text"], 0.35)
|
|
1440
1436
|
if "stroke-width-scale" in e.style:
|
|
1441
1437
|
base["stroke-width"] = base.get("stroke-width", 1.3) * e.style["stroke-width-scale"]
|
|
1442
1438
|
for raw in [st.class_raw(e.classes, g.classes, "edge")]:
|
|
@@ -23,7 +23,7 @@ from ..geometry import E, N, OPPOSITE, S, W, Rect, parse_side, side_of_point
|
|
|
23
23
|
from ..model import Bus, Edge, Element, Graph, Hint, Node, Port, Subsystem
|
|
24
24
|
from ..progress import activity, report
|
|
25
25
|
from ..portlabels import port_content_padding
|
|
26
|
-
from ..textmetrics import
|
|
26
|
+
from ..textmetrics import text_block_size
|
|
27
27
|
from .detail import LevelQP, QPEdge, QPHint, QPItem, QPTerminal, solve_level
|
|
28
28
|
from .floorplan import (FPEdge, FPHint, FPItem, FPProblem, FPState, FPTerminal, FPVariant, Floorplanner)
|
|
29
29
|
from .graphalgo import fiedler_order, partition_items
|
|
@@ -418,36 +418,13 @@ class HierarchicalPlacer:
|
|
|
418
418
|
for le in ledges:
|
|
419
419
|
self._classify(le)
|
|
420
420
|
self.auto_pad = self._auto_padding()
|
|
421
|
-
self.label_margins, self.label_padding = self._label_spacing()
|
|
422
421
|
self.deadline = (time.time() + self.s.time_budget) if self.s.time_budget > 0 else None
|
|
423
422
|
|
|
424
|
-
def _label_spacing(self):
|
|
425
|
-
"""Reserve channels for text before floorplanning, including draft layouts."""
|
|
426
|
-
margins, padding = {}, {}
|
|
427
|
-
for le in self.ledges:
|
|
428
|
-
source = le.source
|
|
429
|
-
if not isinstance(source, (Edge, Bus)) or not source.label:
|
|
430
|
-
continue
|
|
431
|
-
if isinstance(source, Edge) and not self.s.edge_labels:
|
|
432
|
-
continue
|
|
433
|
-
w, h = label_box_size(source.label, source.style, self.s.font_family, bus=isinstance(source, Bus))
|
|
434
|
-
need = max(w, h) + 2 * LABEL_CLEARANCE
|
|
435
|
-
scope = le.scope
|
|
436
|
-
children = [scope.child_containing(el) for el in (le.a, le.b) if el is not scope]
|
|
437
|
-
for child in children:
|
|
438
|
-
if child is not None:
|
|
439
|
-
key = (scope.uid, child.uid)
|
|
440
|
-
margins[key] = max(margins.get(key, 0.0), need / 2)
|
|
441
|
-
if le.a is scope or le.b is scope:
|
|
442
|
-
padding[scope.uid] = max(padding.get(scope.uid, 0.0), need)
|
|
443
|
-
return margins, padding
|
|
444
|
-
|
|
445
423
|
def _text_margin(self, sub, child):
|
|
446
|
-
margin = self.label_margins.get((sub.uid, child.uid), 0.0)
|
|
447
424
|
if isinstance(child, Subsystem) and child.visible:
|
|
448
425
|
bands = port_content_padding(estimate_port_sides(child, flow_of(child, self.s)), self.s)
|
|
449
|
-
|
|
450
|
-
return
|
|
426
|
+
return max(bands.values())
|
|
427
|
+
return 0.0
|
|
451
428
|
|
|
452
429
|
def _auto_padding(self) -> Dict[int, float]:
|
|
453
430
|
"""Extra inner padding for subsystems that many wires leave: every wire needs a straight
|
|
@@ -567,8 +544,7 @@ class HierarchicalPlacer:
|
|
|
567
544
|
self.margin_bonus[(x.uid, c.uid)] = role_max[role]
|
|
568
545
|
|
|
569
546
|
def _pad(self, sub: Subsystem) -> float:
|
|
570
|
-
return
|
|
571
|
-
self.label_padding.get(sub.uid, 0.0)) + self.pad_bonus.get(sub.uid, 0.0)
|
|
547
|
+
return subsystem_padding(sub, self.s) + self.auto_pad.get(sub.uid, 0.0) + self.pad_bonus.get(sub.uid, 0.0)
|
|
572
548
|
|
|
573
549
|
# ------------------------------------------------------------------------------------
|
|
574
550
|
def node_variant(self, n: Node) -> FPVariant:
|
|
@@ -907,7 +883,8 @@ class HierarchicalPlacer:
|
|
|
907
883
|
box_w = cw + 2 * pad
|
|
908
884
|
box_h = ch + 2 * pad + header
|
|
909
885
|
if not is_root and sub.visible and sub.label:
|
|
910
|
-
|
|
886
|
+
bold = str(sub.style.get("font-weight", "700")) in ("bold", "600", "700", "800", "900")
|
|
887
|
+
lw, _ = text_block_size(sub.label, float(sub.style.get("font-size", s.font_size+.5)), bold,
|
|
911
888
|
sub.style.get("font-family", s.font_family))
|
|
912
889
|
lw += 2 * pad + 8
|
|
913
890
|
if box_w < lw:
|