stdraw 0.7.13__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.
- stdraw-0.7.13/LICENSE +21 -0
- stdraw-0.7.13/PKG-INFO +383 -0
- stdraw-0.7.13/README.md +356 -0
- stdraw-0.7.13/pyproject.toml +43 -0
- stdraw-0.7.13/setup.cfg +4 -0
- stdraw-0.7.13/stdraw/__init__.py +32 -0
- stdraw-0.7.13/stdraw/__main__.py +350 -0
- stdraw-0.7.13/stdraw/_interactive_js.py +2030 -0
- stdraw-0.7.13/stdraw/anchors.py +103 -0
- stdraw-0.7.13/stdraw/bowtielayout.py +350 -0
- stdraw-0.7.13/stdraw/check.py +651 -0
- stdraw-0.7.13/stdraw/columnlayout.py +531 -0
- stdraw-0.7.13/stdraw/comblayout.py +533 -0
- stdraw-0.7.13/stdraw/crossing_exact.py +1004 -0
- stdraw-0.7.13/stdraw/delivery.py +81 -0
- stdraw-0.7.13/stdraw/dsl.py +99 -0
- stdraw-0.7.13/stdraw/layout3d.py +167 -0
- stdraw-0.7.13/stdraw/layout_auto.py +1827 -0
- stdraw-0.7.13/stdraw/layoutdiff.py +424 -0
- stdraw-0.7.13/stdraw/loops.py +145 -0
- stdraw-0.7.13/stdraw/metrics.py +1225 -0
- stdraw-0.7.13/stdraw/model.py +134 -0
- stdraw-0.7.13/stdraw/placement.py +220 -0
- stdraw-0.7.13/stdraw/planar.py +625 -0
- stdraw-0.7.13/stdraw/recipes.py +68 -0
- stdraw-0.7.13/stdraw/regularity.py +176 -0
- stdraw-0.7.13/stdraw/render.py +361 -0
- stdraw-0.7.13/stdraw/render3d.py +165 -0
- stdraw-0.7.13/stdraw/rendercheck.py +90 -0
- stdraw-0.7.13/stdraw/ringlayout.py +475 -0
- stdraw-0.7.13/stdraw/routing.py +2630 -0
- stdraw-0.7.13/stdraw/spinewinglayout.py +329 -0
- stdraw-0.7.13/stdraw/symlayout.py +245 -0
- stdraw-0.7.13/stdraw/textmetrics.py +97 -0
- stdraw-0.7.13/stdraw/visibility.py +382 -0
- stdraw-0.7.13/stdraw.egg-info/PKG-INFO +383 -0
- stdraw-0.7.13/stdraw.egg-info/SOURCES.txt +61 -0
- stdraw-0.7.13/stdraw.egg-info/dependency_links.txt +1 -0
- stdraw-0.7.13/stdraw.egg-info/entry_points.txt +2 -0
- stdraw-0.7.13/stdraw.egg-info/requires.txt +10 -0
- stdraw-0.7.13/stdraw.egg-info/top_level.txt +1 -0
- stdraw-0.7.13/tests/test_auto_viewbox.py +96 -0
- stdraw-0.7.13/tests/test_crossing_exact.py +240 -0
- stdraw-0.7.13/tests/test_delivery_contract.py +144 -0
- stdraw-0.7.13/tests/test_dom_truth_gate.py +49 -0
- stdraw-0.7.13/tests/test_fan_frozen_copoint.py +129 -0
- stdraw-0.7.13/tests/test_hit_gate.py +44 -0
- stdraw-0.7.13/tests/test_interactive_segments.py +53 -0
- stdraw-0.7.13/tests/test_layout_modes.py +486 -0
- stdraw-0.7.13/tests/test_manual_layout.py +360 -0
- stdraw-0.7.13/tests/test_mode_decision_trace.py +105 -0
- stdraw-0.7.13/tests/test_mutual_lens_arrow_normal.py +196 -0
- stdraw-0.7.13/tests/test_placement.py +137 -0
- stdraw-0.7.13/tests/test_render_bugs.py +161 -0
- stdraw-0.7.13/tests/test_ring_layout.py +123 -0
- stdraw-0.7.13/tests/test_scalability.py +84 -0
- stdraw-0.7.13/tests/test_standard_anchors.py +114 -0
- stdraw-0.7.13/tests/test_v0711_constraints.py +106 -0
- stdraw-0.7.13/tests/test_v077_features.py +238 -0
- stdraw-0.7.13/tests/test_v078_wide_comb.py +151 -0
- stdraw-0.7.13/tests/test_v079_font_env.py +188 -0
- stdraw-0.7.13/tests/test_visibility.py +151 -0
- stdraw-0.7.13/tests/test_visual_crossings.py +171 -0
stdraw-0.7.13/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 stdraw contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
stdraw-0.7.13/PKG-INFO
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stdraw
|
|
3
|
+
Version: 0.7.13
|
|
4
|
+
Summary: Planarity-aware system-thinking (CLD) diagram engine with honest, metric-backed quality certificates
|
|
5
|
+
Author: stdraw contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: systems-thinking,causal-loop-diagram,cld,diagram,graph-layout,planarity,agent-tools
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
15
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: networkx>=3.0
|
|
20
|
+
Provides-Extra: solver
|
|
21
|
+
Requires-Dist: ortools>=9.10; extra == "solver"
|
|
22
|
+
Provides-Extra: measure
|
|
23
|
+
Requires-Dist: pillow>=10.0; extra == "measure"
|
|
24
|
+
Provides-Extra: render-check
|
|
25
|
+
Requires-Dist: playwright>=1.40; extra == "render-check"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
<div align="center">
|
|
29
|
+
|
|
30
|
+
# stdraw
|
|
31
|
+
|
|
32
|
+
**Planarity-aware system-thinking (CLD) diagram engine. Honest grades, hard quality gates, metric-backed certificates.**
|
|
33
|
+
|
|
34
|
+
*Text DSL in → diagram with a quality certificate out. Honest grades, never fake "zero crossings" claims.*
|
|
35
|
+
|
|
36
|
+
**Scale expectations, up front: recommended ≤ 10 nodes · classic recipe ~10 s per figure (graphviz-dot: 0.03 s — stdraw is 2–3 orders of magnitude slower by design, trading speed for hard quality gates + a machine-readable certificate) · n ≥ 50 not recommended.** Numbers: [benchmarks/REPORT.md](benchmarks/REPORT.md)
|
|
37
|
+
|
|
38
|
+
English | [中文](README_zh-CN.md)
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
[](https://github.com/jacky510/stdraw/actions/workflows/ci.yml)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[](pyproject.toml)
|
|
45
|
+
[](https://github.com/jacky510/stdraw/stargazers)
|
|
46
|
+
|
|
47
|
+
[Install](#install) · [Gallery](#gallery) · [Quickstart](#quickstart) · [DSL](#dsl-in-one-minute) · [Certificates](#certificates--quality-gates) · [Agent Integration](#agent-integration)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install stdraw
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## What & Why
|
|
56
|
+
|
|
57
|
+
Mainstream diagram tools stop at "crossing minimization" heuristics, and **none of them make a verifiable promise about their own output**. stdraw is built the other way around:
|
|
58
|
+
|
|
59
|
+
- **auto-v2 (default recipe)**: **structure-aware mode layout** — a deterministic priority chain (bowtie-vertex → spine-wing → column-chain → ring-cycle → layered) picks one arrangement template per graph structure, each with a trial gate (non-triggering graphs stay byte-identical); plus straight/arc-first routing (optional CP-SAT refinement), enforced by **hard quality gates**: box-over-line = 0 (any angle), edge-to-box clearance ≥ 16px, smooth border anchors, and mode-specific regularity gates — see [docs/layout-modes.md](docs/layout-modes.md) (zh original; self-contained English edition: [docs/layout-modes.en.md](docs/layout-modes.en.md));
|
|
60
|
+
- **Ring layout for loop-dominated small graphs (v0.5.0)**: when a small graph (≤ 12 nodes) has a dominant simple cycle covering ≥ n−2 nodes (or ≥ 90% of its edges lie on cycles), the main loop is placed on **regular-polygon ring slots** in cycle order (declared loop order preferred, so R/B polarity colors read along the ring), chords route inside the ring, and residual nodes get deterministic inner/outer slots; the certificate switches to **ring-appropriate metrics** (ring-spacing CV + polygon template-fit error) instead of row alignment — layout mode is stated honestly, never judged by row-gate yardsticks; non-triggering graphs keep the layered path byte-identical;
|
|
61
|
+
- **Anchor-band hard gate (v0.7.3)**: delivered edge anchors clamped to the mid-band (edge midpoint ± 1/4 edge length, corner hits = 0) are now enforced per-figure in CI — spine-wing figures require 100% in-band; mutual-pair edges whose band-clamped anchors cannot pass the hard gates get a deterministic band-anchored local re-route (anchor-band candidates + nested-arc escape hatch + own-box interior check); other engines are locked to their v0.7.2 baseline ratios (ring mutual corridors / bowtie ±12px spoke corridors are registered legacy) — see [docs/layout-modes.md](docs/layout-modes.md) (zh original; self-contained English edition: [docs/layout-modes.en.md](docs/layout-modes.en.md));
|
|
62
|
+
- **Layout aesthetics v3.1 (v0.7.1)**: **spine-wing mode** — graphs with a dominant hub pair (an edge whose endpoints share ≥3 common neighbors = a midline "wing band") plus a constructible secondary cluster get the vertical-spine + midline-wing layout (jacky's hand-drawn classic-cld reference): spine strictly vertical on the axis, wings pressed to the horizontal midline, outer mirror pair symmetric about it; deterministic trigger (hub-pair common-neighbor count + rest-cluster pairing + spine length ratio ≤0.5), equivalent certificate gates (spine-axis ≤2px, midline ≤2px, mirror ≤2px); corpus sweep: only classic-cld-growth-and-burden switches, all other 18 byte-identical — see [docs/layout-modes.md](docs/layout-modes.md) (zh original; self-contained English edition: [docs/layout-modes.en.md](docs/layout-modes.en.md));
|
|
63
|
+
- **Layout aesthetics v3 (v0.7.0)**: **bowtie-vertex mode** — graphs whose topology is exactly two simple cycles sharing one node (e.g. wage-price-spiral, per jacky's hand-drawn reference) get an explicit double-mirror layout (shared node dead-center, one loop per half-plane, symmetric rows, all-straight lines with mutual-pair parallel corridors); **connection-point caliber** — line anchors clamp to each edge's mid-point ±1/4 band (corners excluded entirely, hit count always 0), multiple lines on the same side fan out symmetrically around the mid-point; **adaptive arc curvature** — bulge = min(6 + 0.06·chord, 40) px (short edges bend less, long edges capped), arcs within one symmetric structure share a single unified k for visual evenness — see [docs/layout-modes.md](docs/layout-modes.md) (zh original; self-contained English edition: [docs/layout-modes.en.md](docs/layout-modes.en.md));
|
|
64
|
+
- **Layout aesthetics v2 (v0.6.0)**: regularity first — long-chain / few-loop graphs (main cycle ≤ 4) get a **vertical column layout** (spine aligned on one axis, branches on neighbor side columns, the spine's closing edge drawn as a big side arc for the loop-closure feel); bowtie graphs (two triangles sharing an edge, e.g. learning-flywheel) get an **explicit mirror-symmetric layout** with a strict line-type rule (axis edge straight + mirrored side arcs); loop edges are **colored by polarity** (R = amber `#d97706`, B = sky `#0284c7`, mixed/acyclic = neutral, negative edges dashed) replacing the old R/B letter badges; isomorphic skeletons map to the same layout template (WL structural colors drive slot assignment, not declared-loop ids); crossings and row alignment are demoted from hard gates to **reported soft targets** (order & beauty first) while arrangement regularity stays hard-gated — see [docs/layout-modes.md](docs/layout-modes.md) (zh original; self-contained English edition: [docs/layout-modes.en.md](docs/layout-modes.en.md));
|
|
65
|
+
- **WS3 routing math**: detours are **provably shortest** — obstacles inflated by the clearance, then an exact Dijkstra shortest path over the corner visibility graph (graph-optimal); polyline corners are smoothed with tangent circular arcs (G1-continuous, bounded curvature, per-arc clearance re-verified); crossings are re-checked in **dual calibers** — the router's theoretical count on final geometry must equal the geometric recount on delivered coordinates, or no certificate is issued; loop polarity is expressed by **edge color** (amber = reinforcing R, sky = balancing B, dashed = negative edge) with no letter badges cluttering complex graphs; polyline detours carry a fixed type cost (arc > straight > polyline — folded corners are a last resort);
|
|
66
|
+
- **ortho-v1 (classic recipe, optional)**: planar embedding + st-numbering + visibility representation — all wires strictly vertical with a **theorem-level 0-crossing guarantee** on planar graphs (behavior frozen, byte-regression protected, safe for production callers to pin);
|
|
67
|
+
- **Honest grading**: if it can't earn a certificate it says so — **never claims 0 crossings for arbitrary graphs** (in auto-v2, crossings are minimized, not guaranteed);
|
|
68
|
+
- **Two-layer ground-truth box measurement**: the layout side plans box bounds from worst-case real-font metrics; the verification side measures actual DOM bounds in a real browser after rendering — certificates verify rendered reality, not estimates;
|
|
69
|
+
- **Quality certificates**: every deliverable ships with quantified metrics (crossings / box-over-line / clearance / alignment / CV / anchors), verified on the delivered coordinates;
|
|
70
|
+
- **Determinism**: same input → same output, byte-identical (CP-SAT pinned to a single worker + fixed seed); diagrams are safe to commit with clean diffs.
|
|
71
|
+
|
|
72
|
+
### Recommended scale (≤ 10 nodes, honest boundary)
|
|
73
|
+
|
|
74
|
+
**Recommended: no more than 10 nodes** — the measured boundary for "CP-SAT proves optimality + full `_polish` refinement + all hard quality gates passing" (classic CLD small-graph domain, n10 ≈ 45–60 s per figure). Up to 12 nodes can trigger the ring-cycle layout (loop-dominated small graphs). n=20–30 do render after the WS5 fix (random sparse family: 20/20 at n20 and n30), but placement degrades from "proven optimal" to "best incumbent within a deterministic budget", box-over-line / clearance are no longer guaranteed at 0, and runtime stays 2–3 orders of magnitude above baselines; n≥50 is not recommended (19/20 at n50; the ortho recipe honestly skips non-planar inputs). Numbers per [benchmarks/REPORT.md](benchmarks/REPORT.md) §4/§5 (Chinese).
|
|
75
|
+
|
|
76
|
+
## Gallery
|
|
77
|
+
|
|
78
|
+
All images are real renderings produced by the current engine (v0.7.13) from the in-repo corpus — no mock-ups, no stale artifacts (click through to each demo).
|
|
79
|
+
|
|
80
|
+
<table>
|
|
81
|
+
<tr>
|
|
82
|
+
<td><a href="../demos/classic-cld-2d.html"><img src="docs/gallery/classic-cld-2d.png" alt="classic CLD 2D auto-v2 layout, edges colored by sign (+ amber / − sky, all solid)" width="420"/></a></td>
|
|
83
|
+
<td><a href="../demos/classic-cld-3d.html"><img src="docs/gallery/classic-cld-3d.png" alt="classic CLD 3D sphere layout, polarity tubes + cone arrowheads" width="420"/></a></td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td><strong>2D · auto-v2 spine-wing layout</strong> (classic-cld-growth-and-burden, 8n/13e, vertical spine + midline wing band + mirrored right wing, 0 crossings; edges colored by sign: + amber / − sky, all solid)</td>
|
|
87
|
+
<td><strong>3D · sphere arrangement</strong> (rotate freely; projection crossings solved by the camera; two-color polarity tubes + cone arrowheads on every edge)</td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td colspan="2" align="center"><a href="../demos/classic-cld-2d.html"><img src="docs/gallery/certificate.png" alt="quality certificate panel" width="420"/></a></td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr>
|
|
93
|
+
<td colspan="2" align="center"><strong>Quality certificate panel</strong> (hard gates + gate tally + honest grade, exactly as shipped in the demo; spine-wing caliber: spine-axis / midline-adherence / mirror deviation)</td>
|
|
94
|
+
</tr>
|
|
95
|
+
</table>
|
|
96
|
+
|
|
97
|
+
## Ask your agent in plain words
|
|
98
|
+
|
|
99
|
+
stdraw is agent-native: one `.cld` text file in, a certified diagram out. Tell your agent:
|
|
100
|
+
|
|
101
|
+
| You say | The agent runs |
|
|
102
|
+
|---|---|
|
|
103
|
+
| "Draw this causal-loop diagram with a quality certificate" | `stdraw layout model.cld --outdir out` |
|
|
104
|
+
| "Audit the declared feedback loops: unclosed loops? wrong polarity?" | reads `out/loops_audit.json` → `problems` (loops re-enumerated independently) |
|
|
105
|
+
| "Can this diagram be zero-crossing? Tell me straight, no bragging" | reads `out/certificate.json` → `grade` (honest grading, below) |
|
|
106
|
+
| "Give me a printable version" | opens `out/demo-2d.html` (inline SVG, zero network) |
|
|
107
|
+
|
|
108
|
+
## Project Structure
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
stdraw/
|
|
112
|
+
├── stdraw/ # Python package
|
|
113
|
+
│ ├── recipes.py # recipe registry: auto-v2 (default) / ortho-v1 (frozen, pin-safe)
|
|
114
|
+
│ ├── layout_auto.py # auto-v2 layout (layering + crossings-in-objective + optional CP-SAT + local refinement)
|
|
115
|
+
│ ├── routing.py # straight/arc/detour routing (box-over-line=0, clearance>=16px hard gates)
|
|
116
|
+
│ ├── textmetrics.py # ground-truth box bounds (layout side: worst-case real-font metrics)
|
|
117
|
+
│ ├── rendercheck.py # ground-truth box bounds (CI side: headless-browser DOM measurement, optional layer)
|
|
118
|
+
│ ├── visibility.py # WS3: routing math (inflated-obstacle visibility-graph shortest path + arc smoothing)
|
|
119
|
+
│ ├── crossing_exact.py # WS2: exact crossing-number layer (ordering insertion ILP + planarization oracle + B&B squeeze, optional ortools)
|
|
120
|
+
│ ├── regularity.py # WS2: structure detection + computable regularity definitions (polygon deviation / straightness / symmetry axis)
|
|
121
|
+
│ ├── placement.py # WS2: slot-discretized placement (structure-driven slot types; CP-SAT on the general branch)
|
|
122
|
+
│ ├── ringlayout.py # v0.5.0: loop-dominated ring layout (trigger detection + main-ring polygon slots + residual assignment)
|
|
123
|
+
│ ├── columnlayout.py # v0.6.0: column layout (long-chain/few-loop: spine in one column + side branches + closing side arc)
|
|
124
|
+
│ ├── symlayout.py # v0.6.0: bowtie-pair symmetric layout (two triangles sharing an edge: vertical-axis mirror + consistent line kinds)
|
|
125
|
+
│ ├── planar.py # ortho-v1: 2D planar layout (theorem-level 0 crossings, frozen behavior)
|
|
126
|
+
│ ├── layout3d.py # 3D five-solid arrangements (sphere/cube/tetrahedron/cone)
|
|
127
|
+
│ ├── metrics.py # quality certificates v1/v2 + honest grading
|
|
128
|
+
│ ├── check.py # geometry self-checks (CI-gate form: v1 11 checks / v2 hard-gate checks)
|
|
129
|
+
│ ├── loops.py # loop-semantics audit (lint)
|
|
130
|
+
│ ├── dsl.py # .cld DSL parser
|
|
131
|
+
│ ├── model.py # graph data model
|
|
132
|
+
│ ├── render.py # 2D HTML rendering (inline SVG, v1/v2 forms)
|
|
133
|
+
│ ├── render3d.py # 3D HTML rendering (three.js)
|
|
134
|
+
│ └── __main__.py # CLI: stdraw layout ... [--recipe auto|ortho] [--linestyle straight|arc]
|
|
135
|
+
├── examples/ # .cld cases (limits-to-growth / classic-cld-growth-and-burden)
|
|
136
|
+
├── demos/ # deliverable form: 2D/3D demos + certificate JSON
|
|
137
|
+
├── viewer/ # zero-dependency interactive viewer (four forms + live certificate)
|
|
138
|
+
├── skills/stdraw/ # Agent Skill (installable via skills add)
|
|
139
|
+
├── tests/baselines/ # ortho-v1 byte-regression baselines (frozen-behavior iron gate)
|
|
140
|
+
├── docs/ # DSL spec (EN + zh-CN) + gallery + OR-Tools comparison + crossing-exact math (WS2)
|
|
141
|
+
└── .github/workflows/ # CI: recipe regression + hard gates + DOM measurement + determinism
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Quickstart
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# 1. Install (pure pip; the only hard dependency is networkx)
|
|
148
|
+
pip install stdraw
|
|
149
|
+
|
|
150
|
+
# 2. Run a case: text DSL in
|
|
151
|
+
stdraw layout examples/limits-to-growth.cld --outdir out --pitch 62
|
|
152
|
+
|
|
153
|
+
# 3. Look at the deliverables
|
|
154
|
+
# out/demo-2d.html — 2D layout + embedded quality certificate
|
|
155
|
+
# out/demo-3d.html — 3D sphere/cube/tetrahedron/cone (needs the in-repo assets/)
|
|
156
|
+
# out/certificate.json — metrics + honest grade
|
|
157
|
+
# out/loops_audit.json — loop-semantics audit
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A ready-made deliverable ships in the repo: open [`demos/classic-cld-2d.html`](demos/classic-cld-2d.html) / [`demos/classic-cld-3d.html`](demos/classic-cld-3d.html).
|
|
161
|
+
Interactive viewer (four forms + node dragging + loop panel): open `viewer/index.html` in a browser, nothing to install.
|
|
162
|
+
|
|
163
|
+
### Manual touch-up loop (drag → export → re-certify)
|
|
164
|
+
|
|
165
|
+
Every `demo-2d.html` (auto recipe) ships with a built-in touch-up layer — drag
|
|
166
|
+
nodes (mouse or touch), live metrics recompute in the toolbar, then:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 1. Open out/demo-2d.html, drag cards, click "Export layout"
|
|
170
|
+
# -> downloads graph-manual-layout.json (layout.json-compatible subset)
|
|
171
|
+
|
|
172
|
+
# 2. Re-run routing + certificate on YOUR coordinates — same hard gates,
|
|
173
|
+
# honest scoring (a manual layout is never graded leniently):
|
|
174
|
+
stdraw layout input.cld --outdir out-manual --skip-3d --from-layout graph-manual-layout.json
|
|
175
|
+
|
|
176
|
+
# 3. (optional) Side-by-side auto-vs-manual certificate comparison
|
|
177
|
+
# (the gap is the engine-vs-human aesthetic distance):
|
|
178
|
+
stdraw layout input.cld --outdir out-manual --skip-3d \
|
|
179
|
+
--from-layout graph-manual-layout.json --compare-auto
|
|
180
|
+
|
|
181
|
+
# 4. Three-layer diff (node-level moves / structural metrics / loop semantics)
|
|
182
|
+
# — the data source for distilling aesthetic rules from human edits:
|
|
183
|
+
stdraw layout-diff out/layout.json out-manual/layout.json --cld input.cld --diff-out diff.json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Waypoint handles (detour fold points are draggable, not fixed)
|
|
187
|
+
|
|
188
|
+
Since v0.5.3 the touch-up layer re-routes **all line styles live** while you drag
|
|
189
|
+
(straight / arc / visibility-graph fold lines alike — fold vertices are
|
|
190
|
+
recomputed every frame against current obstacles; nothing is pinned). Edges that
|
|
191
|
+
detour around obstacles render each fold point as a small **drag handle**
|
|
192
|
+
(hover shows a move cursor):
|
|
193
|
+
|
|
194
|
+
- drag a handle → that edge becomes *user-pinned*; node drags afterwards only
|
|
195
|
+
re-anchor its endpoints, your waypoints stay;
|
|
196
|
+
- "Export layout" writes `edges: [{a, b, waypoints: [[x,y],...]}]` for pinned
|
|
197
|
+
edges; `--from-layout` replays the exact same polyline (anchors recomputed on
|
|
198
|
+
the box borders toward the first/last waypoint) and the certificate re-checks
|
|
199
|
+
the delivered geometry honestly — including your waypoints;
|
|
200
|
+
- "Reset" clears all pins and restores the engine layout.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# after dragging handles + export:
|
|
204
|
+
stdraw layout input.cld --outdir out-manual --skip-3d --from-layout graph-manual-layout.json
|
|
205
|
+
# layout.json now carries route kind "waypoints" with via = your fold points
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Line style control & manual fine-tuning (v0.7.7)
|
|
209
|
+
|
|
210
|
+
Automatic line-style rule: **straight is the default**; an arc is used only
|
|
211
|
+
when it is structurally justified — mutual-pair lens corridors, a straight
|
|
212
|
+
line that cannot keep the >=16px clearance but a shallow arc can, or
|
|
213
|
+
mode-specific aesthetics (e.g. the spine-wing midline arcs). Bilateral
|
|
214
|
+
supply-chain graphs (K_{m,2}: every source feeding two non-adjacent hubs)
|
|
215
|
+
get the new **comb-bilateral** placement — sources in one vertical column,
|
|
216
|
+
the two hubs in mirrored left/right slots, all hub edges a straight fan.
|
|
217
|
+
|
|
218
|
+
Per-edge manual control on the demo page:
|
|
219
|
+
|
|
220
|
+
- **double-click a line** to cycle its style `auto → straight → arc → free →
|
|
221
|
+
auto` (a >=10px hover hit zone, pointer cursor + highlight; node drags
|
|
222
|
+
are never disturbed). Forced styles re-route live and the top-bar metrics
|
|
223
|
+
recompute honestly — red when a hard gate fails, no leniency;
|
|
224
|
+
- **free style**: click anywhere on the line to insert a fold handle and
|
|
225
|
+
drag it — the user segment is an **exact polyline** (WYSIWYG, no
|
|
226
|
+
smoothing), anchors recomputed on the box borders toward the first/last
|
|
227
|
+
fold point. At most **3 fold points per edge**; at the cap a click
|
|
228
|
+
selects the nearest handle with the tooltip "max 3 points per edge";
|
|
229
|
+
- **Export layout** now also writes `edges[].style` (free fold points ride
|
|
230
|
+
along as `waypoints`); `--from-layout` replays forced straight/arc/free
|
|
231
|
+
styles under the same deterministic rules and the certificate re-checks
|
|
232
|
+
them honestly;
|
|
233
|
+
- **Reset** clears pins, styles and fold points back to the engine default.
|
|
234
|
+
|
|
235
|
+
Two integration hooks for embedding pages:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
// apply a layout programmatically (the inverse of "Export layout")
|
|
239
|
+
window.__stdrawApplyLayout({
|
|
240
|
+
nodes: {id: {x: 100, y: 200}},
|
|
241
|
+
edge_styles: {"a->b": "arc"}, // auto | straight | arc | free
|
|
242
|
+
waypoints: {"a->b": [[300, 240]]} // free fold points (max 3)
|
|
243
|
+
});
|
|
244
|
+
// be notified instead of polling pointerup
|
|
245
|
+
window.addEventListener("stdraw:layoutchanged", e => {
|
|
246
|
+
// e.detail = the current layout (nodes + styled edges + waypoints)
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
For long second-line values (ticker / price strings) that overflow the
|
|
251
|
+
planned card width, `--value-pad PX` reserves extra per-side padding for
|
|
252
|
+
the value line (`AutoConfig.value_pad_extra` in-process; the default keeps
|
|
253
|
+
the historical sizing byte-for-byte).
|
|
254
|
+
|
|
255
|
+
## DSL in One Minute
|
|
256
|
+
|
|
257
|
+
Line-oriented text, `#` starts a comment:
|
|
258
|
+
|
|
259
|
+
```ld
|
|
260
|
+
graph "limits-to-growth × shifting-the-burden"
|
|
261
|
+
var rate "adoption rate"
|
|
262
|
+
rate -> + adopters @loops R1,B1,B2 # polarity edge + loop tags
|
|
263
|
+
loop R1 "word of mouth" "R" [rate, adopters, word]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
A minimal closed loop = variables / polarity edges / loop tags / groups (optional; the layout
|
|
267
|
+
layer does not consume groups — layout is determined by relations alone). Loop tags are not
|
|
268
|
+
comments — the lint layer verifies closure and R/B polarity parity against an independent
|
|
269
|
+
enumeration of all loops. Full spec: [docs/dsl-spec-v0.1.md](docs/dsl-spec-v0.1.md)
|
|
270
|
+
(Chinese: [docs/dsl-spec-v0.1.zh-CN.md](docs/dsl-spec-v0.1.zh-CN.md)).
|
|
271
|
+
|
|
272
|
+
## Certificates · Quality Gates
|
|
273
|
+
|
|
274
|
+
Every deliverable directory contains `certificate.json`:
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"metrics": { "crossings": 0, "box_press_1px": 0, "edge_box_min_clear": 18.0,
|
|
279
|
+
"min_card_gap": 36.0, "row_align_rate": null,
|
|
280
|
+
"spacing_cv": 0.0, "column_axis_deviation": 0.0,
|
|
281
|
+
"loop_cohesion_spread": { "R1": 0.43, "B1": 0.43 },
|
|
282
|
+
"line_kind_counts": { "line": 5, "arc": 1 } },
|
|
283
|
+
"gates": { "box_press_zero": true, "edge_box_clear_ge_16": true,
|
|
284
|
+
"anchors_smooth": true, "min_gap_ge_16": true,
|
|
285
|
+
"cv_le_035": true, "column_axis_aligned": true,
|
|
286
|
+
"spine_pitch_uniform": true,
|
|
287
|
+
"mutual_pairs_separated": true, "routing_consistent": true,
|
|
288
|
+
"lines_no_coincidence": true },
|
|
289
|
+
"reports": { "crossings_zero": true,
|
|
290
|
+
"crossings_note": "soft target: <=2 crossings allowed when arrangement regularity passes (order & beauty first)",
|
|
291
|
+
"row_align_ge_060": true },
|
|
292
|
+
"placement": "column-chain",
|
|
293
|
+
"grade": "best"
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
(Ring-layout certificate: row alignment / column snap are honestly `null` — the
|
|
298
|
+
regularity gates switch to ring spacing CV + polygon template fit, per placement;
|
|
299
|
+
v0.6.0 adds column/symmetric calibers and demotes crossings_zero and row alignment
|
|
300
|
+
to **reported soft targets** — see docs/layout-modes.md (zh original; English edition: docs/layout-modes.en.md)
|
|
301
|
+
mode. Layered layouts keep the row-alignment caliber.)
|
|
302
|
+
|
|
303
|
+
Honest grading semantics (iron rule: **never pass off a non-certificate as one**):
|
|
304
|
+
|
|
305
|
+
| Grade | Definition (auto-v2) |
|
|
306
|
+
|---|---|
|
|
307
|
+
| **`best`** | all hard gates pass (box-over-line 0 / clearance ≥ 16 / smooth anchors / regular arrangement) + 0 crossings |
|
|
308
|
+
| **`second`** | all hard gates pass + ≤ 2 crossings (coords in the certificate for review; when gates and crossings conflict, gates win) |
|
|
309
|
+
| **`3d`** | dimension-lift to 3D: rotate freely, no spatial crossings, projection crossings solved by the camera |
|
|
310
|
+
| **`uncertified`** | everything else — honestly ungraded |
|
|
311
|
+
|
|
312
|
+
`geometry_report.json` (geometry self-checks in CI-gate form; the CLI exits 1 on fail) can be used directly as a regression gate.
|
|
313
|
+
|
|
314
|
+
**Exactness layer (WS2, `--exact`)**: the CLI can compute the provable crossing
|
|
315
|
+
number cr(G) (ordering-style insertion ILP + planarization oracle + exhaustive
|
|
316
|
+
B&B over planar subgraphs × embeddings). The certificate then carries
|
|
317
|
+
`proven_optimal` / `gap` / `lb` / `crossing_number` in an `exactness` block —
|
|
318
|
+
with a rigorous scope note: optimality is claimed only relative to the model
|
|
319
|
+
class defined in [docs/crossing-exact.md](docs/crossing-exact.md) (Chinese) §0–5, never
|
|
320
|
+
over all conceivable metric sets. Requires the optional solver extra
|
|
321
|
+
(`pip install "stdraw[solver]"`); without it the fields honestly stay
|
|
322
|
+
`computed: false`. Layouts also carry a computable **regularity** block
|
|
323
|
+
(structure-driven: cycle → regular-polygon deviation, chain → straightness,
|
|
324
|
+
symmetry-axis alignment; definitions in docs/crossing-exact.md (Chinese) §7).
|
|
325
|
+
|
|
326
|
+
### Recipes
|
|
327
|
+
|
|
328
|
+
| Recipe | Line style | Guarantee | Use when |
|
|
329
|
+
|---|---|---|---|
|
|
330
|
+
| `auto-v2` (default) | straight/arc first-class (`--linestyle straight\|arc`) | hard quality gates + crossing minimization (optional CP-SAT via `pip install "stdraw[solver]"`); placement modes by structure: ring (loop-dominated) / vertical column (long-chain, few loops) / mirror-symmetric (bowtie), each with mode-caliber regularity gates; loop edges colored R=amber / B=sky, negative dashed | general CLDs / teaching / reports |
|
|
331
|
+
| `ortho-v1` (`--recipe ortho`) | all-vertical orthogonal | theorem-level 0 crossings on planar graphs (**frozen behavior**, byte-regression in CI) | production callers pinning behavior / orthogonal style preference |
|
|
332
|
+
|
|
333
|
+
## Agent Integration
|
|
334
|
+
|
|
335
|
+
**Decide by outcome, not by habit.** The most common integration mistake
|
|
336
|
+
is baking a static SVG from `layout.json` coordinates — that silently
|
|
337
|
+
loses everything stdraw exists to deliver (live drag re-routing, waypoint
|
|
338
|
+
handles, the export→recompute loop, the honest certificate). Coordinate
|
|
339
|
+
outputs carry an explicit delivery contract (`stdraw/delivery.py`):
|
|
340
|
+
`"delivery": "coordinates-only"`, `"interactive": false`,
|
|
341
|
+
`"engine_version"` and a one-line warning on coordinates-only outputs;
|
|
342
|
+
the CLI marks full deliverables `"delivery": "stdraw-cli"`,
|
|
343
|
+
`"interactive": true`. The certificate confesses its delivery face via
|
|
344
|
+
`"interactive"` / `"drag_layer"` (usable as a host CI gate):
|
|
345
|
+
|
|
346
|
+
| You want | Use | If you take the last row anyway, you lose |
|
|
347
|
+
|---|---|---|
|
|
348
|
+
| One finished diagram (touch-up + certificate) | `stdraw layout model.cld` → deliver **`demo-2d.html`** | — |
|
|
349
|
+
| The diagram inside your own page | embed `demo-2d.html` (iframe) + persist the exported layout + `--from-layout` (below) | — |
|
|
350
|
+
| Coordinates for your own renderer | `layout.json` — self-declares coordinates-only + warning; explicit opt-out: `stdraw.recipes.run(..., suppress_delivery_warning=True)` | live drag re-routing, waypoint handles, export→recompute loop, quality certificate, CI gate |
|
|
351
|
+
|
|
352
|
+
### Minimal embed (official posture)
|
|
353
|
+
|
|
354
|
+
```html
|
|
355
|
+
<!-- embed the deliverable page, not the coordinates -->
|
|
356
|
+
<iframe src="demo-2d.html" style="width:100%;height:820px;border:0"
|
|
357
|
+
title="stdraw diagram (drag to touch up)"></iframe>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# after manual touch-up in demo-2d.html: "Export layout" -> manual-layout.json
|
|
362
|
+
stdraw layout model.cld --from-layout manual-layout.json --outdir out-manual
|
|
363
|
+
# -> demo-2d.html re-rendered on the touched-up coordinates + re-graded certificate
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
| Agent host | Integration |
|
|
367
|
+
|---|---|
|
|
368
|
+
| Any agent that can read files | read [AGENTS.md](AGENTS.md) (repo root, agent manual, English) |
|
|
369
|
+
| Agent Skills ecosystem | `npx skills add jacky510/stdraw` → installs [skills/stdraw/SKILL.md](skills/stdraw/SKILL.md) (English) |
|
|
370
|
+
| Scripts / CI gates | `pip install stdraw` → `stdraw layout ...` → assert on `certificate.json` / `geometry_report.json` |
|
|
371
|
+
| MCP server | planned (see the placeholder in AGENTS.md) |
|
|
372
|
+
|
|
373
|
+
## Limitations (honest boundaries)
|
|
374
|
+
|
|
375
|
+
- **Never claims 0 crossings for arbitrary graphs**: in auto-v2, crossings are a minimized objective term, not a theorem guarantee; ortho-v1 raises `NotPlanarError` on non-planar input and degrades honestly instead of forcing a drawing.
|
|
376
|
+
- ortho-v1 (frozen recipe) allows "unrelated wires occluded under cards" as legacy design (cards are opaque) and counts it honestly in the certificate — a known v0.1 boundary; **the v0.2 default recipe auto-v2 makes box-over-line a hard gate (= 0)**.
|
|
377
|
+
- The 3D layer makes no 2D crossing promises — spatial chords don't intersect thanks to the extra dimension; projection crossings are solved by the camera.
|
|
378
|
+
- The layout layer is a replaceable implementation (currently: layering + crossing minimization + straight/arc routing; CP-SAT is an optional refinement layer — without it the engine degrades to the constructive method: full features, construct-tier quality).
|
|
379
|
+
- The local-search neighborhood covers in-row shifts and adjacent swaps, not cross-row moves — the last crossing of some graphs may need a larger neighborhood (roadmap M3).
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
[MIT](LICENSE) © stdraw contributors
|