app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
main
John Lauer
Publish 0.1.94
bca797f
//! The route plan JSON shape `gate --plan` and `land route --plan` accept (docs/plan-schema.md).
use aiflow_board::Board;
use aiflow_copper::{apply, normalize_plan, plan_entries, plan_to_copper};
use serde_json::json;
fn board() -> Board {
Board::from_text("plan.kicad_pcb", r#"(kicad_pcb
(layers (0 "F.Cu" signal) (31 "B.Cu" signal))
(net 1 "SIG")
(gr_rect (start 0 0) (end 20 20) (layer "Edge.Cuts"))
(footprint "smd" (layer "F.Cu") (at 5 5) (property "Reference" "U1")
(pad "1" smd rect (at 0 0) (size 1 1) (layers "F.Cu") (net 1 "SIG")))
(footprint "tht" (layer "F.Cu") (at 15 5) (property "Reference" "J1")
(pad "1" thru_hole circle (at 0 0) (size 1.6 1.6) (drill 0.8) (layers "*.Cu" "*.Mask") (net 1 "SIG"))))"#.into()).unwrap()
}
#[test]
fn every_point_form_and_layer_marker() {
let b = board();
// pad key, [x, y], {x, y, layer} (a via where the layer changes), {pad, layer}
let e = json!({"net": "SIG", "width": 0.3, "viaSize": 0.7, "viaDrill": 0.35,
"paths": [["U1.1", [10.0, 5.0], {"x": 10.0, "y": 8.0, "layer": "B.Cu"}, {"pad": "J1.1", "layer": "B.Cu"}]]});
let (segs, vias) = plan_to_copper(&b, &e).unwrap();
assert_eq!(segs.len(), 3);
assert_eq!((segs[0].layer.as_str(), segs[1].layer.as_str(), segs[2].layer.as_str()), ("F.Cu", "F.Cu", "B.Cu"));
assert!(segs.iter().all(|s| s.width == 0.3));
assert_eq!(vias.len(), 1);
assert_eq!((vias[0].x, vias[0].y, vias[0].size, vias[0].drill), (10.0, 8.0, 0.7, 0.35));
// defaults: width 0.25, via 0.6 / 0.3; one path may be given as "points"
let (segs, _) = plan_to_copper(&b, &json!({"net": "SIG", "points": ["U1.1", [8.0, 5.0]]})).unwrap();
assert_eq!(segs[0].width, 0.25);
// a via ending a path: copper on the arrival layer only, the other side meets a pour
let (segs, vias) = plan_to_copper(&b, &json!({"net": "SIG", "paths": [["U1.1", [7.0, 5.0], {"x": 7.0, "y": 5.0, "layer": "B.Cu"}]]})).unwrap();
assert_eq!((segs.len(), vias.len()), (1, 1));
assert_eq!(segs[0].layer, "F.Cu");
// an SMD pad reached on the wrong layer is refused
assert!(plan_to_copper(&b, &json!({"net": "SIG", "paths": [[{"pad": "U1.1", "layer": "B.Cu"}, [8.0, 5.0]]]})).unwrap_err().contains("no copper on B.Cu"));
}
#[test]
fn object_and_bare_array_plans_are_the_same() {
let b = board();
let entry = json!({"net": "SIG", "paths": [["U1.1", "J1.1"]]});
let (_, a) = apply(&b, &json!({"nets": [entry.clone()]}), false).unwrap();
let (_, bare) = apply(&b, &json!([entry.clone()]), false).unwrap();
assert_eq!(a, bare);
assert_eq!(normalize_plan(json!([entry.clone()])), json!({"nets": [entry]}));
assert_eq!(normalize_plan(json!({"nets": [], "note": "x"}))["note"], "x");
let e = plan_entries(&json!({"traces": []})).unwrap_err();
assert!(e.starts_with("plan needs nets") && e.contains("bare [entry") && e.contains("{\"pad\"") && e.contains("plan-schema.md"), "{e}");
assert!(apply(&b, &json!({"nets": [{"paths": [["U1.1", "J1.1"]]}]}), false).unwrap_err().contains("entry needs net"));
}