← Commit history

0.2.19: altium_plate_vias (drilled PcbLib pads -> plated Multi-Layer); test_board U1..Un designators + centred silkscreen names

John Lauer ·9918f234f9 ·1mo ago ·parent 2b02058
5 files changed +82−3
bridge-rs/src/library.rs+4
@@ -137,6 +137,10 @@ pub fn normalize_bodies(args: &Value) -> Value {     sch_lib_verb(args, "normalize_bodies", vec![("name", s_or(args, "footprintName", ""))], 120) } +pub fn plate_vias(args: &Value) -> Value {+    sch_lib_verb(args, "plate_vias", vec![("name", s_or(args, "footprintName", ""))], 90)+}+ pub fn set_params(args: &Value) -> Value {     let lib = match s(args, "libraryPath") {         Some(l) if !l.is_empty() => l,
bridge-rs/src/verbs.rs+5−1
@@ -31,7 +31,7 @@ pub const KNOWN: &[&str] = &[     "install_extension", "extension_ping",     // library authoring     "create_footprint", "create_symbol", "build_footprint", "build_symbol_modes", "add_body",-    "add_models", "normalize_bodies", "set_params", "set_visibility", "read_symbol",+    "add_models", "normalize_bodies", "plate_vias", "set_params", "set_visibility", "read_symbol",     "clone_symbol", "rename_symbol", "rename_footprint", "remove_symbol", "remove_footprint",     "copy_footprint", "xcopy_footprint", "swap_footprint", "symbol_mode", "cleanup_libs",     "install_library", "install_library_bundle", "install_receipt", "compile_intlib", "extract_intlib",@@ -107,6 +107,9 @@ fn describe() -> Value {             "Three parts on one board: footprintName 'VL53L5CX;VL53L7CX;VL53L8CX' + pcbPath, then altium_show_3d_board {filePath: pcbPath}. Every call creates a NEW PcbDoc.",             &["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("plate_vias", "Make every drilled pad in a PcbLib a plated MULTI-LAYER pad (all footprints, or footprintName). adom-lbr writes heatsink vias as pads with a hole; Altium loaded them on the Top Layer, so a thermal pad's vias were not through-holes until this ran.",+            json!({"libraryPath": "path (.PcbLib)", "footprintName": "string (optional filter)"}), json!({"holes": "int", "plated": "int", "fixed": "Fp:Pad;..."}), 90,+            "altium_save the PcbLib afterwards; footprint_primitives on a placed part should then report layerId 74 for the vias.", &["normalize_bodies", "footprint_primitives", "save"], &["Read-modify in place: run once per library install."], json!({"libraryPath": "C:/lib/Adom_ToF.PcbLib"})),         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", "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!({})),@@ -468,6 +471,7 @@ pub fn dispatch(command: &str, args: &Value, _forwarded: &[(String, String)]) ->         "add_body" => library::add_body(args),         "add_models" => library::add_models(args),         "normalize_bodies" => library::normalize_bodies(args),+        "plate_vias" => library::plate_vias(args),         "set_params" => library::set_params(args),         "set_visibility" => library::set_visibility(args),         "read_symbol" => library::read_symbol(args),
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+71−1
@@ -2718,6 +2718,66 @@ Begin End;  +{ ------------------------------------------------------ plate_vias --- }++{ Every drilled pad in the current PcbLib (all footprints, or name=<filter>) becomes a+  plated MULTI-LAYER pad. adom-lbr writes heatsink vias as pads with a hole, and Altium+  loaded them on the Top Layer (John, 2026-09-20: "did Altium support bringing in those+  heatsink vias?" - the holes came, the layer did not). Saving the library afterwards+  also yields a file whose bytes show the encoding Altium wants. }+Function CmdPlateVias(Lines : TStringList) : String;+Var+    PLib : IPCB_Library;+    Fp   : IPCB_LibComponent;+    GIt  : IPCB_GroupIterator;+    Pad  : IPCB_Pad;+    I, NFixed, NHoles : Integer;+    Name, S : String;+Begin+    PLib := PCBServer.GetCurrentPCBLibrary;+    Name := Param(Lines, 'name');+    If PLib = Nil Then+    Begin+        Result := '{' + JRaw('ok','false') + ',' + JStr('error','no PCB library is current') + '}';+        Exit;+    End;+    NFixed := 0; NHoles := 0; S := '';+    PCBServer.PreProcess;+    For I := 0 To PLib.ComponentCount - 1 Do+    Begin+        Fp := PLib.GetComponent(I);+        If (Name <> '') And (UpperCase(Fp.Name) <> UpperCase(Name)) Then Continue;+        GIt := Fp.GroupIterator_Create;+        GIt.AddFilter_ObjectSet(MkSet(ePadObject));+        GIt.AddFilter_LayerSet(AllLayers);+        Pad := GIt.FirstPCBObject;+        While Pad <> Nil Do+        Begin+            If Pad.HoleSize > 0 Then+            Begin+                NHoles := NHoles + 1;+                If Pad.Layer <> eMultiLayer Then+                Begin+                    Pad.BeginModify;+                    Pad.Layer := eMultiLayer;+                    Pad.Plated := True;+                    Pad.EndModify;+                    NFixed := NFixed + 1;+                    If S <> '' Then S := S + ';';+                    S := S + Fp.Name + ':' + Pad.Name;+                End;+            End;+            Pad := GIt.NextPCBObject;+        End;+        Fp.GroupIterator_Destroy(GIt);+    End;+    PCBServer.PostProcess;+    PLib.Board.ViewManager_FullUpdate;+    PLib.RefreshView;+    Result := '{' + JRaw('ok','true') + ',' + JRaw('holes', IntToStr(NHoles)) + ',' + JRaw('plated', IntToStr(NFixed)) + ',' + JStr('fixed', S) + '}';+End;++ { ------------------------------------------------------ normalize_bodies --- }  { The PcbLib canvas origin sits 50000 mil from the stored component origin, so a@@ -2940,6 +3000,7 @@ Var     Seg   : TPolySegment;     Trk   : IPCB_Track;     Txt   : IPCB_Text;+    R     : TCoordRect;     Net   : IPCB_Net;     GIt   : IPCB_GroupIterator;     Pad   : IPCB_Pad;@@ -3019,6 +3080,10 @@ Begin         End;         CX := (WC * (K + 1)) Div (FpList.Count + 1);         Inst := PlaceCopy(Board, LibFp, CX, CY);+        { U1..Un so the read verbs (component_pins, footprint_primitives) can address each part;+          the factory left every part named Designator1 (measured 2026-09-20). Name stays hidden. }+        Inst.Name.Text := 'U' + IntToStr(K + 1);+        Inst.Comment.Text := LibFp.Name;         { Part name on the top overlay under each footprint so a human reading the board           knows which chip is which (John, 2026-09-20). 35 mil stroke text, ~21 mil per           character, centred on the part; the text TOP sits 3.4 - 0.9 = 2.5 mm below the part@@ -3028,10 +3093,14 @@ Begin         Txt.Text := LibFp.Name;         Txt.Size := MilsToCoord(35);         Txt.Width := MilsToCoord(5);-        Txt.XLocation := CX - MilsToCoord((Length(LibFp.Name) * 21) Div 2);+        Txt.XLocation := CX;         Txt.YLocation := CY - MMsToCoord(3.4);         Board.AddPCBObject(Txt);         PCBServer.SendMessageToRobots(Board.I_ObjectAddress, c_Broadcast, PCBM_BoardRegisteration, Txt.I_ObjectAddress);+        { centre on the text's REAL extent (a per-character guess put the labels off to the+          right of every part; John, 2026-09-20): measure the bounding rectangle and shift }+        R := Txt.BoundingRectangle;+        Txt.MoveByXY(CX - ((R.Left + R.Right) Div 2), 0);     End;     AssignPadNets(Board, Inst, Param(Lines, 'assigns'));     { stub tracks: des=x,y (mm from centre) -> a track from that pad to the point, on its net }@@ -4481,6 +4550,7 @@ Begin     Else If Cmd = 'set_visibility' Then Result := CmdSetVisibility(Lines)     Else If Cmd = 'xcopy_footprint' Then Result := CmdXCopyFootprint(Lines)     Else If Cmd = 'normalize_bodies' Then Result := CmdNormalizeBodies(Lines)+    Else If Cmd = 'plate_vias' Then Result := CmdPlateVias(Lines)     Else If Cmd = 'test_board' Then Result := CmdTestBoard(Lines)     Else If Cmd = 'board_shape' Then Result := CmdBoardShape(Lines)     Else If Cmd = 'swap_footprint' Then Result := CmdSwapFootprint(Lines)
src/bridge.json+1
@@ -92,6 +92,7 @@     "altium_list_nets",     "altium_net_connections",     "altium_normalize_bodies",+    "altium_plate_vias",     "altium_open_document",     "altium_open_project",     "altium_pcb_report",