app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
← Commit history
0.2.19: altium_plate_vias (drilled PcbLib pads -> plated Multi-Layer); test_board U1..Un designators + centred silkscreen names
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" ]-}+}
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",