← Commit history

0.2.15: add_symbol_art clear + line-segment mode; symbol_prims models gate

John Lauer ·3f75c3be37 ·1mo ago ·parent 45369ee
6 files changed +95−32
bridge-rs/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "altium-bridge"-version = "0.2.14"+version = "0.2.15" edition = "2021" description = "Adom altium-bridge — the bridge process, in Rust. Calls ab's verbs for the desktop; speaks file IPC to the resident DelphiScript agent inside Altium." license = "MIT"
bridge-rs/src/library.rs+3−3
@@ -342,11 +342,11 @@ pub fn add_symbol_art(args: &Value) -> Value {     let kr = format!("{:.0},{:.0},{:.0},{:.0}", rect.0 * MIL, rect.1 * MIL, rect.2 * MIL, rect.3 * MIL);     let payload: String = polys.iter().map(|pl| pl.iter().map(|(x, y)| format!("{:.0},{:.0}", x * MIL, y * MIL)).collect::<Vec<_>>().join(" ")).collect::<Vec<_>>().join("\n");     support::agent_open(&lib, 60);-    let mut r = agent::call("add_symbol_art", &[("libref", name.clone()), ("kicadRect", kr)], Some(&payload), timeout_of(args, 180));+    let mut r = agent::call("add_symbol_art", &[("libref", name.clone()), ("kicadRect", kr), ("mode", s_or(args, "mode", "lines")), ("clear", if args.get("clear").and_then(|b| b.as_bool()) == Some(false) { "0".into() } else { "1".into() })], Some(&payload), timeout_of(args, 240));     r["_hints"] = json!([         format!("{} polylines drawn into {name} from the KiCad symbol's baked outline; altium_save {{filePath}} the SchLib, then altium_show_symbol to check the chip sits between the pin-name columns.", polys.len()),         "The outline is the reviewed chip artwork (side-etched MPN for optical tops); do not re-etch or re-generate it here, reuse the wiki page's artwork cache.",-        "This verb APPENDS: running it twice draws the outline twice. remove_symbol + rebuild, or clone the part first, to redo it."]);+        "Replaces earlier art by default (clear:true removes the component's polylines and lines first); pass clear:false to stack. mode 'lines' (default) draws eLine segments, mode 'polyline' draws ISch_Polyline objects."]);     r } @@ -357,7 +357,7 @@ pub fn symbol_prims(args: &Value) -> Value {         _ => return json!({"success": false, "error": "libraryPath (.SchLib) required"}),     };     support::agent_open(&lib, 60);-    agent::call("symbol_prims", &[("libref", s_or(args, "symbolName", ""))], None, timeout_of(args, 60))+    agent::call("symbol_prims", &[("libref", s_or(args, "symbolName", "")), ("models", if args.get("models").and_then(|b| b.as_bool()) == Some(true) { "1".into() } else { "0".into() })], None, timeout_of(args, 60)) }  /// Replace every footprint model on a SchLib component with one PCBLIB model.
bridge-rs/src/main.rs+1−1
@@ -25,7 +25,7 @@ mod win32; use std::time::Instant; use tiny_http::{Header, Method, Response, Server}; -pub const BRIDGE_VERSION: &str = "0.2.14";+pub const BRIDGE_VERSION: &str = "0.2.15";  fn json_response(status: u16, body: &serde_json::Value) -> Response<std::io::Cursor<Vec<u8>>> {     let data = serde_json::to_vec(body).unwrap_or_else(|_| b"{}".to_vec());
bridge-rs/src/verbs.rs+3−3
@@ -108,16 +108,16 @@ fn describe() -> Value {             &["place_on_sch", "show_3d_board", "add_body"], &["assigns/stubs apply to the LAST part placed.", "pcb is empty when pcbPath is omitted: the board exists only unsaved as PCB<n>.PcbDoc."],             json!({"libraryPath": "C:/lib/Adom_ToF.PcbLib", "footprintName": "VL53L5CX;VL53L7CX;VL53L8CX", "wMm": 39, "hMm": 14, "pcbPath": "C:/proj/tof.PcbDoc"})),         verb_doc("symbol_prims", "Read a SchLib component's drawing: primitive counts by kind, body rectangle, polyline bounding box, display mode and the footprint models. Read-only, use it to verify add_symbol_art / set_models.",-            json!({"libraryPath": "path (.SchLib)", "symbolName": "string"}), json!({"pins": "int", "rectangles": "int", "polylines": "int", "bodyRectMil": "[..]", "polyBboxMil": "[..]", "models": "[\"PCBLIB:NAME\"]"}), 60,+            json!({"libraryPath": "path (.SchLib)", "symbolName": "string", "models": "bool, default false (the model walk halts the engine on some builds)"}), json!({"pins": "int", "rectangles": "int", "lines": "int", "polylines": "int", "bodyRectMil": "[..]", "polyBboxMil": "[..]", "models": "[\"PCBLIB:NAME\"]"}), 60,             "polylines should equal what add_symbol_art reported and polyBboxMil should sit inside bodyRectMil.", &["add_symbol_art", "set_models", "read_symbol"], &[], json!({})),         verb_doc("set_models", "Replace every footprint model on a SchLib component with ONE PCBLIB model (set_params footprint and add_models APPEND, which is how a symbol ends up with two).",             json!({"libraryPath": "path (.SchLib)", "symbolName": "string", "footprint": "string (PcbLib footprint name)"}), json!({"removed": "int", "footprint": "string"}), 60,             "altium_save the SchLib afterwards.", &["symbol_prims", "set_params", "add_models"], &["Removes every existing model, including 3D-only ones."], json!({})),         verb_doc("add_symbol_art", "Draw the chip-outline artwork (the chip-thumbnailer polylines adom-symbol baked into the KiCad symbol) into a SchLib component, fitted to its body rectangle, so the Altium symbol shows the same chip as KiCad and Fusion.",-            json!({"libraryPath": "path (.SchLib)", "symbolName": "string (LibReference)", "kicadSym": "Windows path to the .kicad_sym that carries the baked outline (stage it with send_files)"}),+            json!({"libraryPath": "path (.SchLib)", "symbolName": "string (LibReference)", "kicadSym": "Windows path to the .kicad_sym that carries the baked outline (stage it with send_files)", "clear": "bool, default true: remove the component's existing polylines/lines first", "mode": "lines (default) | polyline"}),             json!({"polylines": "int", "points": "int", "bodyRectMil": "[x0,y0,x1,y1]"}), 180,             "Run once per part right after assemble-altium / build_symbol_modes, then altium_save the SchLib and altium_show_symbol to check.",-            &["show_symbol", "read_symbol", "save"], &["APPENDS on every call: run it once per symbol.", "The KiCad symbol must already carry the outline (adom-symbol create bakes it; --outline-name none for side-etched chips)."],+            &["show_symbol", "symbol_prims", "save"], &["clear:true (default) also removes hand-drawn lines on the symbol; only the pins and the body rectangle survive.", "The KiCad symbol must already carry the outline (adom-symbol create bakes it; --outline-name none for side-etched chips)."],             json!({"libraryPath": "C:/lib/Adom_ToF.SchLib", "symbolName": "VL53L5CX", "kicadSym": "C:/lib/VL53L5CX.kicad_sym"})),         verb_doc("place_on_sch", "Place one symbol, or a ';' list of symbols, from the open SchLib on ONE new sheet (sized A4 or B for the part count, parts inside the frame as U1..Un with the part number as Comment); fills the title block from title/number/revision/drawnBy and places description as a text frame; saves to schPath when given.",             json!({"libraryPath": "path (.SchLib)", "symbolName": "string | 'A;B;C'", "schPath": "path (.SchDoc, optional save)", "title": "string (Title)", "number": "string (DocumentNumber)", "revision": "string", "drawnBy": "string", "description": "string (what the sheet shows, 2-3 sentences)"}),
page.json+1−1
@@ -139,4 +139,4 @@     "skills/altium-wiki-import/SKILL.md",     "uninstall.sh"   ]-}\ No newline at end of file+}
src/agent/AdomAgent.pas.tmpl+86−23
@@ -1401,13 +1401,22 @@ Var     Comp   : ISch_Component;     Rect   : ISch_Rectangle;     Poly   : ISch_Polyline;+    Seg    : ISch_Line;+    Prim   : ISch_GraphicalObject;+    Victims : TInterfaceList;     PL, Pts : TStringList;-    I, J, N, NPoly, NPts : Integer;-    Name, KR, Ln, Tok : String;-    KX0, KY0, KX1, KY1, AX0, AY0, AX1, AY1, SX, SY, MX, MY, Tmp : Double;+    I, J, N, NPoly, NPts, NCleared : Integer;+    Name, KR, Ln, Tok, Mode : String;+    KX0, KY0, KX1, KY1, AX0, AY0, AX1, AY1, SX, SY, MX, MY, PX, PY, Tmp : Double;+    UseLines : Boolean; Begin     Name := Param(Lines, 'libref');     KR := Param(Lines, 'kicadRect');+    Mode := Param(Lines, 'mode');+    { lines (default): every polyline becomes N-1 eLine segments, the primitive BuildSymbolModes has+      always drawn with; polyline: ISch_Polyline objects (measured 2026-09-19: they render on one+      part and stay invisible on another with identical geometry). }+    UseLines := (Mode <> 'polyline');     SLib := SchServer.GetCurrentSchDocument;     If (SLib = Nil) Or (SLib.ObjectID <> eSchLib) Then     Begin@@ -1457,10 +1466,34 @@ Begin     End;     SX := (AX1 - AX0) / (KX1 - KX0);     SY := (AY1 - AY0) / (KY1 - KY0);+    SchServer.ProcessControl.PreProcess(SLib, '');+    { replace, never stack: every earlier polyline and every line that is not part of the pins+      or the body outline is art from a previous run. Collect first, remove after the walk. }+    NCleared := 0;+    If Param(Lines, 'clear') <> '0' Then+    Begin+        Victims := TInterfaceList.Create;+        Iter := Comp.SchIterator_Create;+        Iter.AddFilter_ObjectSet(MkSet(ePolyline, eLine));+        Prim := Iter.FirstSchObject;+        While Prim <> Nil Do+        Begin+            Victims.Add(Prim);+            Prim := Iter.NextSchObject;+        End;+        Comp.SchIterator_Destroy(Iter);+        For I := 0 To Victims.Count - 1 Do+        Begin+            Prim := Victims[I];+            Comp.RemoveSchObject(Prim);+            SchServer.DestroySchObject(Prim);+            NCleared := NCleared + 1;+        End;+        Victims.Free;+    End;     PL := TStringList.Create;     PL.Text := Payload;     NPoly := 0; NPts := 0;-    SchServer.ProcessControl.PreProcess(SLib, '');     For I := 0 To PL.Count - 1 Do     Begin         Ln := Trim(PL[I]);@@ -1469,21 +1502,47 @@ Begin         N := Pts.Count;         If N >= 2 Then         Begin-            Poly := SchServer.SchObjectFactory(ePolyline, eCreate_Default);-            Poly.OwnerPartId := 1;-            Poly.OwnerPartDisplayMode := 0;-            Poly.LineWidth := eSmall;-            Poly.Color := $8A8A8A;-            Poly.VerticesCount := N;-            For J := 1 To N Do+            If UseLines Then+            Begin+                PX := 0; PY := 0;+                For J := 1 To N Do+                Begin+                    Tok := Pts[J - 1];+                    MX := AX0 + (StrToFloat(Field(Tok, 1)) - KX0) * SX;+                    MY := AY0 + (StrToFloat(Field(Tok, 2)) - KY0) * SY;+                    If J > 1 Then+                    Begin+                        Seg := SchServer.SchObjectFactory(eLine, eCreate_Default);+                        Seg.OwnerPartId := 1;+                        Seg.OwnerPartDisplayMode := 0;+                        Seg.LineWidth := eSmall;+                        Seg.Color := $8A8A8A;+                        Seg.Location := Point(MilsToCoord(PX), MilsToCoord(PY));+                        Seg.Corner := Point(MilsToCoord(MX), MilsToCoord(MY));+                        Comp.AddSchObject(Seg);+                        SchServer.RobotManager.SendMessage(Comp.I_ObjectAddress, c_BroadCast, SCHM_PrimitiveRegistration, Seg.I_ObjectAddress);+                    End;+                    PX := MX; PY := MY;+                End;+            End+            Else             Begin-                Tok := Pts[J - 1];-                MX := AX0 + (StrToFloat(Field(Tok, 1)) - KX0) * SX;-                MY := AY0 + (StrToFloat(Field(Tok, 2)) - KY0) * SY;-                Poly.Vertex[J] := Point(MilsToCoord(MX), MilsToCoord(MY));+                Poly := SchServer.SchObjectFactory(ePolyline, eCreate_Default);+                Poly.OwnerPartId := 1;+                Poly.OwnerPartDisplayMode := 0;+                Poly.LineWidth := eSmall;+                Poly.Color := $8A8A8A;+                Poly.VerticesCount := N;+                For J := 1 To N Do+                Begin+                    Tok := Pts[J - 1];+                    MX := AX0 + (StrToFloat(Field(Tok, 1)) - KX0) * SX;+                    MY := AY0 + (StrToFloat(Field(Tok, 2)) - KY0) * SY;+                    Poly.Vertex[J] := Point(MilsToCoord(MX), MilsToCoord(MY));+                End;+                Comp.AddSchObject(Poly);+                SchServer.RobotManager.SendMessage(Comp.I_ObjectAddress, c_BroadCast, SCHM_PrimitiveRegistration, Poly.I_ObjectAddress);             End;-            Comp.AddSchObject(Poly);-            SchServer.RobotManager.SendMessage(Comp.I_ObjectAddress, c_BroadCast, SCHM_PrimitiveRegistration, Poly.I_ObjectAddress);             NPoly := NPoly + 1;             NPts := NPts + N;         End;@@ -1493,7 +1552,8 @@ Begin     PL.Free;     SLib.GraphicallyInvalidate;     Result := '{' + JRaw('ok', 'true') + ',' + JStr('libref', Comp.LibReference) + ',' + JRaw('polylines', IntToStr(NPoly)) + ',' +-        JRaw('points', IntToStr(NPts)) + ',' + JRaw('bodyRectMil', '[' + IntToStr(Round(AX0)) + ',' + IntToStr(Round(AY0)) + ',' + IntToStr(Round(AX1)) + ',' + IntToStr(Round(AY1)) + ']') + '}';+        JRaw('points', IntToStr(NPts)) + ',' + JRaw('cleared', IntToStr(NCleared)) + ',' + JStr('mode', Mode) + ',' ++        JRaw('bodyRectMil', '[' + IntToStr(Round(AX0)) + ',' + IntToStr(Round(AY0)) + ',' + IntToStr(Round(AX1)) + ',' + IntToStr(Round(AY1)) + ']') + '}'; End;  { Read a SchLib component drawing: primitive counts by kind, the body rectangle, the polyline@@ -1576,17 +1636,20 @@ Begin     End;     Comp.SchIterator_Destroy(Iter);     Models := '';-    For I := 0 To Comp.ImplementationCount - 1 Do+    If Param(Lines, 'models') = '1' Then     Begin-        Impl := Comp.Implementation[I];-        If Models <> '' Then Models := Models + ',';-        Models := Models + '"' + Impl.ModelType + ':' + Impl.ModelName + '"';+        For I := 0 To Comp.ImplementationCount - 1 Do+        Begin+            Impl := Comp.Implementation[I];+            If Models <> '' Then Models := Models + ',';+            Models := Models + '"' + Impl.ModelType + ':' + Impl.ModelName + '"';+        End;     End;     Result := '{' + JRaw('ok', 'true') + ',' + JStr('libref', Comp.LibReference) + ',' +         JRaw('pins', IntToStr(NPin)) + ',' + JRaw('rectangles', IntToStr(NRect)) + ',' + JRaw('lines', IntToStr(NLine)) + ',' +         JRaw('polylines', IntToStr(NPoly)) + ',' + JRaw('other', IntToStr(NOther)) + ',' +         JRaw('displayMode', IntToStr(Comp.DisplayMode)) + ',' + JRaw('displayModeCount', IntToStr(Comp.DisplayModeCount)) + ',' +-        JRaw('currentPartId', IntToStr(Comp.CurrentPartID)) + ',' + JRaw('partCount', IntToStr(Comp.PartCount)) + ',' ++        JRaw('currentPartId', IntToStr(Comp.CurrentPartID)) + ',' +         JRaw('bodyRectMil', '[' + IntToStr(RX0) + ',' + IntToStr(RY0) + ',' + IntToStr(RX1) + ',' + IntToStr(RY1) + ']') + ',' +         JRaw('polyBboxMil', '[' + IntToStr(PX0) + ',' + IntToStr(PY0) + ',' + IntToStr(PX1) + ',' + IntToStr(PY1) + ']') + ',' +         JRaw('models', '[' + Models + ']') + '}';