← Commit history

1.0.7: 1.0.7: kicad_remove_zone (zones by id, one Undo step); kicad_zone_state names zone types (copper, keepout, teardrop)

John Lauer ·1480fb2620 ·1mo ago ·parent 5f98584
4 files changed +61−4
rust/Cargo.lock+3−3
@@ -244,7 +244,7 @@ dependencies = [  [[package]] name = "kicad-bridge"-version = "1.0.6"+version = "1.0.7" dependencies = [  "kicad-core",  "kicad-platform",@@ -255,7 +255,7 @@ dependencies = [  [[package]] name = "kicad-core"-version = "1.0.6"+version = "1.0.7" dependencies = [  "kicad-ipc-rs",  "kicad-platform",@@ -281,7 +281,7 @@ dependencies = [  [[package]] name = "kicad-platform"-version = "1.0.6"+version = "1.0.7" dependencies = [  "serde",  "serde_json",
rust/Cargo.toml+1−1
@@ -3,7 +3,7 @@ resolver = "2" members = ["crates/*"]  [workspace.package]-version = "1.0.6"+version = "1.0.7" edition = "2021" license = "MIT" publish = false
rust/crates/kicad-bridge/src/verbs_routing.rs+13
@@ -67,6 +67,16 @@ pub static VERBS: &[Verb] = &[         related: &["kicad_add_zone", "kicad_routing_validate"],         pitfalls: &["Coverage is against the outline's bounding box; a non-rectangular board reads low.", "Pads, tracks and vias outside zones are not counted."],     },+    Verb {+        name: "kicad_remove_zone",+        summary: "Remove zones (pours or keepouts) by id live as one Undo step.",+        mechanism: Mechanism::Ipc, risk: "write", timeout_sec: 130,+        input: "{\"filePath\", \"expectedRevision\", \"itemIds\": [\"<zone uuid>\", ...]}",+        example: "kicad_remove_zone {\"filePath\":\"C:/p/b.kicad_pcb\",\"expectedRevision\":\"<rev>\",\"itemIds\":[\"7c1e...\"]}",+        hint: "Ids from kicad_add_zone or kicad_zone_state. Refuses ids that are not zones; nothing removed then.",+        related: &["kicad_add_zone", "kicad_zone_state"],+        pitfalls: &["Does not save the file.", "KiCad's own teardrop zones are zones too; remove only what you added."],+    },     Verb {         name: "kicad_routing_validate",         summary: "Run KiCad DRC against the current unsaved live board snapshot.",@@ -147,6 +157,7 @@ pub fn dispatch(state: &mut State, command: &str, args: &Value) -> Option<Value>         "kicad_routing_validate" => live(state, args, Live::Validate),         "kicad_add_zone" => live(state, args, Live::AddZone),         "kicad_zone_state" => live(state, args, Live::ZoneState),+        "kicad_remove_zone" => live(state, args, Live::RemoveZone),         "kicad_board_pads" => board_pads(args),         "kicad_add_track" => add_track(args),         "kicad_add_via" => add_via(args),@@ -169,6 +180,7 @@ enum Live {     Validate,     AddZone,     ZoneState,+    RemoveZone, }  #[cfg(feature = "ipc")]@@ -186,6 +198,7 @@ fn live(state: &mut State, args: &Value, which: Live) -> Value {         Live::Validate => ipc::routing_validate(&ctx, args),         Live::AddZone => ipc::add_zone(&ctx, args),         Live::ZoneState => ipc::zone_state(&ctx, args),+        Live::RemoveZone => ipc::remove_zone(&ctx, args),     };     match result {         Ok(v) => v,
rust/crates/kicad-core/src/ipc.rs+44
@@ -852,6 +852,50 @@ pub fn add_zone(ctx: &Ctx, args: &Value) -> RResult<Value> {     Ok(result) } +/// `kicad_remove_zone`: delete zones (copper pours or keepouts) by id as one Undo step. Only+/// zone items are accepted, so a pad, track or footprint id is refused with nothing removed.+pub fn remove_zone(ctx: &Ctx, args: &Value) -> RResult<Value> {+    let s = connect(ctx, args)?;+    check_revision(args, &s.revision()?)?;+    let ids: Vec<String> = match args.get("itemIds").or_else(|| args.get("zoneIds")) {+        Some(Value::Array(a)) if !a.is_empty() && a.len() <= 256 => a.iter().filter_map(Value::as_str).map(str::to_string).collect(),+        _ => return Err(RoutingError::new("invalid_item_ids", "Supply 1..256 zone itemIds returned by kicad_add_zone or kicad_zone_state")),+    };+    let raws = s.client.get_items_raw_by_type_codes(vec![PcbObjectTypeCode::new_zone().code]).map_err(map_err)?;+    let mut found = Vec::new();+    for a in &raws {+        if let Ok(z) = zones::decode_zone(&a.value) {+            if ids.contains(&z.id) {+                found.push(z);+            }+        }+    }+    if found.len() != ids.len() {+        let have: Vec<&str> = found.iter().map(|z| z.id.as_str()).collect();+        let missing: Vec<&String> = ids.iter().filter(|i| !have.contains(&i.as_str())).collect();+        return Err(RoutingError::new("invalid_item_ids", "Some IDs are not zones on the live board; nothing removed").with("missing", json!(missing)).with("mutated", json!(false)));+    }+    let count = ids.len();+    let label = format!("Adom remove {} zone(s)", count);+    let removed = transaction(&s, &label, || {+        let removed = s.client.delete_items(ids.clone())?;+        if removed.len() != count {+            return Err(IpcFailure::from(format!("KiCad removed {} of {} zones", removed.len(), count)));+        }+        Ok(removed)+    })?;+    let mut result = json!({+        "success": true, "mutated": true, "source": "live-editor", "removed": removed, "undoSteps": 1, "saved": false,+        "zones": found.iter().map(|z| json!({"id": z.id, "name": z.name, "net": z.net, "type": if z.rule_area { "keepout" } else { "copper" }})).collect::<Vec<_>>(),+        "_hint": "Zones removed as one KiCad Undo step. Other zones' fills may be stale: kicad_zone_state {refill:true} refills them.",+    });+    match s.revision() {+        Ok(r) => result["revision"] = json!(r),+        Err(e) => result["postCommitError"] = json!(e.message),+    }+    Ok(result)+}+ /// `kicad_zone_state`: every zone on the live board with its filled copper area per layer /// (KiCad's own fill polygons), the sum per layer and the coverage against the outline /// box. `refill:true` refills every zone first (the fill can be stale after routing).