← Commit history

standard 0.4.4, engine 0.1.3: min_clearance counts only copper-to-copper clearance; rules naming the edge, mask, silk, holes, keepouts, the courtyard or the same net are skipped (fusion-lane #13)

Colby Knox ·c7aebf67fd ·14d ago ·parent 46d0167
8 files changed +30−9
README.md+2
@@ -155,6 +155,8 @@ Decisions so far (proposals from the KiCad and Fusion bridges, 2026-10-27; issue  ## Status +Version 0.4.4: the engine (0.1.3) counts only copper-to-copper clearance rules: a rule naming the edge, mask, silk,+holes, a keepout or the same net is skipped (fusion-lane #13). Version 0.4.3: the engine (0.1.2) reads a clearance rule written as a `value` with its unit (`0.1mm`, Fusion's form) and leaves same-net spacing rules out; Altium and Fusion now both answer their checks from the engine. Version 0.4.2: the engine (0.1.1) names a stack's copper layers by the stack entry's `layer`, or by place (`In<n>.Cu`),
engine/Cargo.lock+1−1
@@ -4,7 +4,7 @@ version = 4  [[package]] name = "adom-eda-checks"-version = "0.1.2"+version = "0.1.3" dependencies = [  "serde_json", ]
engine/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-eda-checks"-version = "0.1.2"+version = "0.1.3" edition = "2021" description = "The EDA Bridge Standard's checks, computed from the board model JSON: no bridge calls, no tool." license = "MIT"
engine/README.md+1−1
@@ -33,7 +33,7 @@ Every check takes one JSON object: | `board` | the board model, `<prefix>_routing_state` (`schema/board.schema.json`) | | `stack` | the stack-up companion, `<prefix>_layer_stack` | | `pours` | poured copper, `<prefix>_pour_state` (its `zones`) |-| `rules` | the board's rules, `<prefix>_read_rules`: a clearance rule as `{kind: "clearance", clearanceMm}` |+| `rules` | the board's rules, `<prefix>_read_rules`: a clearance rule as `{kind: "clearance", clearanceMm}` (a `value` with its unit, `"0.1mm"`, works too). Only copper-to-copper clearance counts: a rule whose kind or name names the board edge, mask, silk, paste, holes, a keepout or restrict area, the courtyard or the same net is skipped | | `silk` | `<prefix>_silk_state` | | `fab` | one record from adom/pcb-design-rules `rules/records/<vendor>.json`, with `vendor` | | `args` | the check's own arguments, as the standard verb documents them |
engine/src/fab.rs+22−3
@@ -132,14 +132,19 @@ fn length_mm(text: &str) -> Option<f64> {     } } -/// The smallest clearance among the board's enabled clearance rules, in mm. A rule gives it as a number+/// A clearance rule that names another object than copper of another net: the board edge, solder mask, silkscreen,+/// holes, keepouts, the courtyard, or the same net (Fusion's Solder-Mask Clearance is a mask expansion, often 0.05 mm).+const NOT_COPPER_CLEARANCE: &[&str] = &["dimension", "edge", "board", "mask", "silk", "paste", "hole", "restrict", "keepout", "keep-out",+                                        "courtyard", "component", "body", "same net", "samenet", "same-net"];++/// The smallest copper-to-copper clearance among the board's enabled rules, in mm. A rule gives it as a number /// (`clearanceMm`, `gapMm` or `minMm`), as a `value` with its unit ("0.1mm": Fusion's form), or in a descriptor the tool-/// wrote ("Gap=10mil", "Gap=0.2mm": Altium's form). Same-net spacing rules are not clearance between nets: left out.+/// wrote ("Gap=10mil", "Gap=0.2mm": Altium's form). Clearance rules about anything but copper of another net are left out. pub fn min_clearance(rules: &[Value]) -> Option<f64> {     let text = |r: &Value, k: &str| r[k].as_str().map(|s| s.to_lowercase()).unwrap_or_default();     rules.iter().filter(|r| r["kind"].as_i64() == Some(0) || text(r, "kind").contains("clearance")             || r["kindName"].as_str() == Some("Clearance") || text(r, "name").contains("clearance"))-        .filter(|r| !["kind", "name", "kindName"].iter().any(|k| { let t = text(r, k); t.contains("same net") || t.contains("samenet") || t.contains("same-net") }))+        .filter(|r| !["kind", "name", "kindName"].iter().any(|k| { let t = text(r, k); NOT_COPPER_CLEARANCE.iter().any(|w| t.contains(w)) }))         .filter(|r| r["enabled"].as_bool() != Some(false))         .filter_map(|r| {             if let Some(v) = ["clearanceMm", "gapMm", "minMm"].iter().find_map(|k| r[*k].as_f64()) { return Some(v); }@@ -320,4 +325,18 @@ mod tests {         assert_eq!(length_mm("100um"), Some(0.1));         assert_eq!(length_mm("0.2"), None, "a bare number has no unit: never guessed");     }++    #[test]+    fn only_copper_to_copper_clearance_counts() {+        // Fusion's rule set (#13): only Copper Clearance is the clearance between nets+        let fusion = vec![json!({"kind": "Copper Clearance", "value": "0.1mm"}), json!({"kind": "Dimension Clearance", "value": "0.3mm"}),+                          json!({"kind": "Solder-Mask Clearance", "value": "0.05mm"}), json!({"kind": "Restrict Clearance", "value": "0.02mm"}),+                          json!({"kind": "Via Restrict Clearance", "value": "0.02mm"}), json!({"kind": "Keepout Clearance", "value": "0.01mm"})];+        assert_eq!(min_clearance(&fusion), Some(0.1));+        // Altium names its other clearances by object too+        let altium = vec![json!({"kind": 0, "name": "Clearance", "descriptor": "Clearance Constraint (Gap=0.2mm) (All),(All)"}),+                          json!({"kind": 17, "name": "HoleToHoleClearance", "descriptor": "Gap=0.1mm"}),+                          json!({"kind": 20, "name": "SilkToSolderMaskClearance", "descriptor": "Gap=0.05mm"})];+        assert_eq!(min_clearance(&altium), Some(0.2));+    } }
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "eda-bridge-standard",-  "version": "0.4.3",+  "version": "0.4.4",   "type": "skill",   "title": "EDA Bridge Standard: one way for an AI to drive every PCB tool",   "description": "The standard every Adom EDA bridge follows so one AI can drive Altium, KiCad, Fusion 360 and any future PCB tool the same way: shared verb names in tiers, the board model JSON, reply and refusal conventions, and a coverage table of each bridge against the Altium reference.",
verbs/coverage.json+1−1
@@ -1,5 +1,5 @@ {- "standard": "0.4.3",+ "standard": "0.4.4",  "reference": {   "bridge": "altium",   "version": "0.2.76"
verbs/standard.json+1−1
@@ -1,5 +1,5 @@ {- "standard": "0.4.3",+ "standard": "0.4.4",  "reference": {   "bridge": "altium",   "version": "0.2.76"