← Commit history

v0.9.112: single-component demo projects (John: extra components on a one-part demo board just confuse the user) - the companion resistor is gone, refdes comes from the part's own Reference property (D1/C1/...)

John Lauer ·978abccbb3 ·1mo ago ·parent a2d550d
5 files changed +47−94
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.111+0.9.112
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.111",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.111/adom-bridge-kicad-v0.9.111.zip",-  "sha256": "75a8178fff97a637f9b57e1c53c9e57796df45e736e2019a20cc5d79b4c7287c",-  "size": 407709,+  "version": "0.9.112",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.112/adom-bridge-kicad-v0.9.112.zip",+  "sha256": "a50bc546ef5b47f236626f0dee19126450adf2189e5b89064729f920d523e182",+  "size": 407166,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-16T01:08:24Z",+  "released_at": "2026-08-16T12:24:34Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.112.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.111",+  "version": "0.9.112",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
handlers/make_part_project.py+40−87
@@ -15,14 +15,20 @@ KiCad file formats; this verb generates them from the installed part:   kicad_make_part_project {"partName":"IN-S42ATR"}   kicad_show_project / show_schematic / show_2d_board / show_3d_board {...} +SINGLE-COMPONENT BY DESIGN (John, 2026-08-16: "when demoing a single component,+you don't need to add extra components to the example 2d board layout as its+just confusing for the user"). The generated design is JUST the part: the sheet+holds the one symbol with short stub wires off its pins, the board holds the one+footprint centered in tight edge cuts with two copper stubs. The reference+designator comes from the part's own Reference property (D1 for a diode, C1 for+a capacitor, ...), not a hardcoded prefix.+ Generated (formats identical to the proven kicad_demo generators):   <out>/<project>.kicad_pro   — minimal project-  <out>/<project>.kicad_sch   — the part as D1 wired to a 330R series resistor-                                (lib_symbols embedded => self-contained sheet)-  <out>/<project>.kicad_pcb   — the part's footprint + an 0805 R placed inside-                                edge cuts, with copper stubs; the footprint-                                keeps its (model ...) so the 3D board shows the-                                real part model.+  <out>/<project>.kicad_sch   — the part alone (lib_symbols embedded => self-contained)+  <out>/<project>.kicad_pcb   — the part's footprint, part-dominant edge cuts; the+                                footprint keeps its (model ...) so the 3D board+                                shows the real part model. """ import math import os@@ -60,37 +66,6 @@ def _pads(fp_text: str):             re.findall(r'\(pad\s+"[^"]*"[^()]*\(at\s+([-\d.]+)\s+([-\d.]+)', fp_text)]  -_R_LIB_ENTRY = '''  (symbol "Adom:R_330"-    (pin_numbers (hide yes)) (pin_names (offset 0) (hide yes))-    (exclude_from_sim no) (in_bom yes) (on_board yes)-    (property "Reference" "R" (at 2.54 1.27 0) (effects (font (size 1.27 1.27)) (justify left)))-    (property "Value" "330" (at 2.54 -1.27 0) (effects (font (size 1.27 1.27)) (justify left)))-    (property "Footprint" "Resistor_SMD:R_0805_2012Metric" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))-    (property "Datasheet" "~" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))-    (symbol "R_330_0_1"-      (rectangle (start -1.016 2.54) (end 1.016 -2.54)-        (stroke (width 0.254) (type default)) (fill (type none)))-    )-    (symbol "R_330_1_1"-      (pin passive line (at 0 3.81 270) (length 1.27) (name "~") (number "1"))-      (pin passive line (at 0 -3.81 90) (length 1.27) (name "~") (number "2"))-    )-  )-'''--_R_FP = '''(footprint "Resistor_SMD:R_0805_2012Metric"-  (version 20240108) (generator "adom-part-project") (layer "F.Cu")-  (at {rx} {ry})-  (attr smd)-  (property "Reference" "R1" (at 0 -1.6 0) (layer "F.SilkS") (effects (font (size 0.6 0.6) (thickness 0.1))))-  (property "Value" "330" (at 0 1.6 0) (layer "F.Fab") (effects (font (size 0.6 0.6) (thickness 0.1))))-  (fp_rect (start -1.7 -0.9) (end 1.7 0.9) (stroke (width 0.05) (type solid)) (fill none) (layer "F.CrtYd"))-  (pad "1" smd roundrect (at -0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25))-  (pad "2" smd roundrect (at 0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25))-)-'''-- def handle_make_part_project(kicad_info: dict, args: dict) -> dict:     """Generate <project>.kicad_pro/.kicad_sch/.kicad_pcb around an installed part. @@ -132,83 +107,62 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict:                                              / "Documents" / proj))     out_dir.mkdir(parents=True, exist_ok=True) -    # ---------------- schematic ----------------+    # The part names its own reference prefix (D for a diode, C for a capacitor…)+    ref_m = re.search(r'\(property\s+"Reference"\s+"([^"]*)"', block)+    refdes = ((ref_m.group(1).strip() if ref_m else "") or "U") + "1"++    # ---------------- schematic: JUST the part ----------------     sheet = _uuid(1)     ax, ay = 127.0, 80.0  # part anchor on the sheet (y-down)     lib_entry = "  " + block.replace(f'(symbol "{part}"', f'(symbol "Adom:{part}"', 1) -    pins = _pins(block)-    wires, r_inst = "", ""-    r_connected = False+    wires = ""     wire_n = 300-    for px, py, pa in pins[:2]:+    for px, py, pa in _pins(block)[:2]:         cx, cy = ax + px, ay - py  # symbol y-up -> sheet y-down         # outward = away from the body = opposite the pin's pointing direction         dx = -math.cos(math.radians(pa))         dy = math.sin(math.radians(pa))   # y flip folded in-        ex, ey = round(cx + 8 * dx, 3), round(cy + 8 * dy, 3)-        if not r_connected and abs(dy) < 0.01 and dx > 0.5:-            # rightward pin: run this wire into R1's bottom pin. A pin's (at) is-            # its CONNECTION point (the line extends toward the body by length),-            # so R_330's bottom pin (at 0 -3.81) connects 3.81 below the anchor.-            ex = 145.0-            r_y = round(ey - 3.81, 3)  # R center so its bottom pin lands on the wire end-            r_inst = f'''  (symbol (lib_id "Adom:R_330") (at 145 {r_y} 0) (unit 1)-    (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(202)}")-    (property "Reference" "R1" (at 147.5 {r_y - 1.3} 0) (effects (font (size 1.27 1.27)) (justify left)))-    (property "Value" "330" (at 147.5 {r_y + 1.5} 0) (effects (font (size 1.27 1.27)) (justify left)))-    (instances (project "{proj}" (path "/{sheet}" (reference "R1") (unit 1))))-  )-'''-            r_connected = True+        ex, ey = round(cx + 6 * dx, 3), round(cy + 6 * dy, 3)         wires += (f'  (wire (pts (xy {round(cx, 3)} {round(cy, 3)}) (xy {ex} {ey})) '                   f'(stroke (width 0) (type default)) (uuid "{_uuid(wire_n)}"))\n')         wire_n += 1-    if not r_inst:  # no rightward pin found — place R1 unwired (demo scaffold)-        r_inst = f'''  (symbol (lib_id "Adom:R_330") (at 145 74.92 0) (unit 1)-    (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(202)}")-    (property "Reference" "R1" (at 147.5 73.5 0) (effects (font (size 1.27 1.27)) (justify left)))-    (property "Value" "330" (at 147.5 76.4 0) (effects (font (size 1.27 1.27)) (justify left)))-    (instances (project "{proj}" (path "/{sheet}" (reference "R1") (unit 1))))-  )-'''      schematic = f'''(kicad_sch (version 20231120) (generator "eeschema") (generator_version "8.0")   (uuid "{sheet}") (paper "A4")   (title_block (title "{part} demo - generated by the Adom KiCad Bridge") (company "Adom"))   (lib_symbols {lib_entry}-{_R_LIB_ENTRY}  )+  ) {wires}  (symbol (lib_id "Adom:{part}") (at {ax} {ay} 0) (unit 1)     (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(201)}")-    (property "Reference" "D1" (at {ax} {ay - 7.5} 0) (effects (font (size 1.27 1.27))))+    (property "Reference" "{refdes}" (at {ax} {ay - 7.5} 0) (effects (font (size 1.27 1.27))))     (property "Value" "{part}" (at {ax} {ay + 7} 0) (effects (font (size 1.27 1.27))))     (property "Footprint" "Adom:{fp_name}" (at {ax} {ay} 0) (effects (font (size 1.27 1.27)) (hide yes)))-    (instances (project "{proj}" (path "/{sheet}" (reference "D1") (unit 1))))+    (instances (project "{proj}" (path "/{sheet}" (reference "{refdes}") (unit 1))))   )-{r_inst}  (sheet_instances (path "/" (page "1")))+  (sheet_instances (path "/" (page "1"))) ) ''' -    # ---------------- board ----------------+    # ---------------- board: JUST the part, centered ----------------     # Part-DOMINANT outline: tight margins so the part is clearly visible in the-    # 3D board view (John, 2026-08-15: on a roomy board a 0402 is an invisible-    # speck and the 3D board "isn't showing the chip").+    # 3D board view (a 0402 on a roomy board reads as an empty board).     part_at = (108.0, 68.0)     coords = [(float(a), float(b)) for a, b in               re.findall(r'\((?:start|end|at|center)\s+([-\d.]+)\s+([-\d.]+)', fp_text)]     half_w = max([abs(x) for x, _ in coords] + [1.0])     half_h = max([abs(y) for _, y in coords] + [1.0])     margin = max(2.0, half_h * 0.8)-    r_at = (round(part_at[0] + half_w + 4.6, 2), part_at[1])  # 0805 R just beside the part     x1 = round(part_at[0] - half_w - margin, 2)     y1 = round(part_at[1] - max(half_h, 1.0) - margin, 2)-    x2 = round(r_at[0] + 1.7 + margin, 2)+    x2 = round(part_at[0] + half_w + margin, 2)     y2 = round(part_at[1] + max(half_h, 1.0) + margin, 2)     fp_placed = fp_text.replace(f'(footprint "{fp_name}"', f'(footprint "Adom:{fp_name}"', 1)     fp_placed = fp_placed.replace('(layer "F.Cu")', f'(layer "F.Cu")\n  (at {part_at[0]} {part_at[1]})', 1)-    fp_placed = fp_placed.replace('(fp_text reference "REF**"', '(fp_text reference "D1"', 1)-    fp_placed = fp_placed.replace('(property "Reference" "REF**"', '(property "Reference" "D1"', 1)+    fp_placed = fp_placed.replace('(fp_text reference "REF**"', f'(fp_text reference "{refdes}"', 1)+    fp_placed = fp_placed.replace('(property "Reference" "REF**"', f'(property "Reference" "{refdes}"', 1)+     edge = ""     pts = [(x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)]     for i in range(4):@@ -216,20 +170,19 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict:         edge += (f'  (gr_line (start {ex1} {ey1}) (end {ex2} {ey2}) '                  f'(stroke (width 0.15) (type solid)) (layer "Edge.Cuts") (uuid "{_uuid(400 + i)}"))\n') +    # two short copper stubs off the pads keep the board looking real without+    # adding any other component     segments = ""     pads = _pads(fp_text)     if len(pads) >= 2 and abs(pads[0][1] - pads[1][1]) < 0.01:         left, right = sorted(pads[:2])         py_abs = part_at[1] + left[1]-        segments = (f'  (segment (start {round(part_at[0] + right[0], 3)} {round(py_abs, 3)}) '-                    f'(end {round(r_at[0] - 0.9125, 3)} {round(r_at[1], 3)}) (width 0.3) (layer "F.Cu"))\n'-                    f'  (segment (start {round(part_at[0] + left[0], 3)} {round(py_abs, 3)}) '-                    f'(end {round(x1 + 2, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n'-                    f'  (segment (start {round(r_at[0] + 0.9125, 3)} {round(r_at[1], 3)}) '-                    f'(end {round(x2 - 2, 3)} {round(r_at[1], 3)}) (width 0.3) (layer "F.Cu"))\n')+        segments = (f'  (segment (start {round(part_at[0] + left[0], 3)} {round(py_abs, 3)}) '+                    f'(end {round(x1 + 0.8, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n'+                    f'  (segment (start {round(part_at[0] + right[0], 3)} {round(py_abs, 3)}) '+                    f'(end {round(x2 - 0.8, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n') -    fps = ("  " + fp_placed.rstrip().replace("\n", "\n  ") + "\n  "-           + _R_FP.format(rx=r_at[0], ry=r_at[1]).rstrip().replace("\n", "\n  "))+    fps = "  " + fp_placed.rstrip().replace("\n", "\n  ")      board = f'''(kicad_pcb (version 20240108) (generator "pcbnew") (generator_version "8.0")   (general (thickness 1.6)) (paper "A4")@@ -277,9 +230,9 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict:         "boardFile": files["kicad_pcb"],         "partName": part,         "footprintName": fp_name,-        "resistorWired": r_connected,-        "output": (f"Generated {proj}: the part as D1 with a 330R series resistor: "-                   f"schematic wired={r_connected}, board with edge cuts, copper and the part's 3D model."),+        "reference": refdes,+        "output": (f"Generated {proj}: the part alone as {refdes}, single-component by design "+                   f"(schematic + part-dominant board with edge cuts, copper stubs and the part's 3D model)."),         "_hint": ("Project surfaces now have something real to show: kicad_show_project {filePath: "                   "projectFile}, kicad_show_schematic {filePath: schematicFile}, kicad_show_2d_board / "                   "kicad_show_3d_board {filePath: boardFile}. The footprint keeps its (model ...) "