stdraw 0.7.13__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.
@@ -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
+ [![CI](https://github.com/jacky510/stdraw/actions/workflows/ci.yml/badge.svg)](https://github.com/jacky510/stdraw/actions/workflows/ci.yml)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
44
+ [![Version](https://img.shields.io/badge/version-0.7.13-blue.svg)](pyproject.toml)
45
+ [![Stars](https://img.shields.io/github/stars/jacky510/stdraw.svg?style=flat)](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
@@ -0,0 +1,36 @@
1
+ stdraw/__init__.py,sha256=KJY8LM4YBco7ASAFKAyct1pPQ5Fgvroh-On-T00rOSo,1386
2
+ stdraw/__main__.py,sha256=CYqn4_DABPcNO9qD8U7n7XaJ4_YVlsCdDPGJaOaOQF0,18666
3
+ stdraw/_interactive_js.py,sha256=Tm7cv3TVVRaxYtPZP7vgyCjhjM0O0BX9xZPHelnEWXc,91338
4
+ stdraw/anchors.py,sha256=UZd3dLDZ4jQxwtPvCvdznxzfbqsTQsiwedgqLdttLUw,4191
5
+ stdraw/bowtielayout.py,sha256=0zAf7b2nyb_wP1Ma-bqVvXMzMALlB-YV9wPE4o1MbOA,16960
6
+ stdraw/check.py,sha256=0ftBe3BjmD-8BoqLbHPTpu9yfygwqNSots3cbrf6zGs,30892
7
+ stdraw/columnlayout.py,sha256=ZxzmNyaKnzhwOSYMojoDfrdXS_VMFO-wHbGP0cqISPs,22653
8
+ stdraw/comblayout.py,sha256=Hw_qMKHKaC-XCr9qkByjinili7RW4pCBMnm5JZq3SHY,24816
9
+ stdraw/crossing_exact.py,sha256=rcaRd0RXhYq9ptPVuOuqD3J0Tik6RQsQ2QSSkLRhv4Y,43125
10
+ stdraw/delivery.py,sha256=8jY0MGtnRm9I3azOGn9RMfeXXJ6w-2SWsZYuI1SdPuo,3459
11
+ stdraw/dsl.py,sha256=pnB8N1gV_of2MHeT9jjXbtBEn1cM_mfA8v_UTvO1qFo,3826
12
+ stdraw/layout3d.py,sha256=YwJPkUsI7mU0ZDdcp_aOOPIcu0dA5Vrb0jlD9EF-djA,6727
13
+ stdraw/layout_auto.py,sha256=9JcpX93cxgi2a8kCPnrrcy3l7KpqSJqxBKpSRaLJ_QM,95995
14
+ stdraw/layoutdiff.py,sha256=TbaWAphS4MsWOCtyeAgvrTB_Dj08V_GIb7y8rEXHBtA,18429
15
+ stdraw/loops.py,sha256=HDv_qf8m5b-3QNLgdX8yZ0TD1hiJDeDmXVExsQ4nJ44,6299
16
+ stdraw/metrics.py,sha256=ywo8yLAngJFou7SfFmYUcZSiRIXjbdrwOXKN4v-VstM,64588
17
+ stdraw/model.py,sha256=y8Z2ykzOEFp5aRpL0aouXX-epABaSBKAjjj8Ca5BPCI,5175
18
+ stdraw/placement.py,sha256=XBjL7AbmZqdWscGZDVVLV0j4qgcp6J1bsm6V9MVP2No,10052
19
+ stdraw/planar.py,sha256=d0OBrZA9EHUvO1lrAlN28J1PVEiiEl7iOxBuoFfp25M,26169
20
+ stdraw/recipes.py,sha256=2iN3OZDXZxg6X15HgDuJ4q9fR7UvHPHktf9gqIWFzmQ,3182
21
+ stdraw/regularity.py,sha256=xL9vGyW_SOKnfZAAN35lHg_QVTRa6JEuMHAOPv-tI2A,6929
22
+ stdraw/render.py,sha256=0-RHGPmCXkmkhV_jADKZlR2ZC-BbfPZw86CmRtO1JaA,19949
23
+ stdraw/render3d.py,sha256=_9KXHKL8QSn-v0CJlldxgE0HRabf-Mmtkv6mPsjvY8o,8366
24
+ stdraw/rendercheck.py,sha256=kLO__AV3JjSrDm-kk0WJx7yFurgyp4XZtVp-b0oSaBc,3854
25
+ stdraw/ringlayout.py,sha256=1m12r-2FQzdPinjscozkqE64sfvAb5lL195Tw3NDdao,19388
26
+ stdraw/routing.py,sha256=RmnYUru6fkceOeUXTlKANdIHZEJaD_p7IVeNNzaqGrc,149402
27
+ stdraw/spinewinglayout.py,sha256=QeO_nKH--8VOHlENR0HcmRJ7a-yJJTHtDBlb-OoghEg,15545
28
+ stdraw/symlayout.py,sha256=JmAoi72a2i34kxS2mCtrwNKe2NPCJ_-uG-E6yvVrXY8,10868
29
+ stdraw/textmetrics.py,sha256=9TPNNknzY3u1tWjYAPelW9ki6X2tyfPmcyvxsq3Ra48,4104
30
+ stdraw/visibility.py,sha256=aR8Yu7V5mbpp6Vhg_UAxybpxRWvkIl3TFuCaVrNr_1M,17606
31
+ stdraw-0.7.13.dist-info/licenses/LICENSE,sha256=TuWI3ujLgBTKT1L8ighz8gGWh2_OyP8lGxwfibtF5N8,1076
32
+ stdraw-0.7.13.dist-info/METADATA,sha256=C3sZTnrl-Kj9-Z96RyQ7H66BCbI3OQ3bbwBdZVvQHM4,28849
33
+ stdraw-0.7.13.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
34
+ stdraw-0.7.13.dist-info/entry_points.txt,sha256=4yI3mbX94K6UD_qKSoa9vCo5PTP16I2n0-UJF7BW-Dk,48
35
+ stdraw-0.7.13.dist-info/top_level.txt,sha256=XbWCYGe6rB5xqhb885Dta_NqFE1gS-0B9cWk-a8yFdM,7
36
+ stdraw-0.7.13.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ stdraw = stdraw.__main__:main
@@ -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.
@@ -0,0 +1 @@
1
+ stdraw