This commit is contained in:
cpu
2026-05-21 13:14:48 +02:00
parent b14223904c
commit 0d2e272ed7
42 changed files with 583 additions and 62665 deletions

4
scripts/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
__pycache__/
panel/
gerbers/
gcode/

View File

@@ -8,319 +8,65 @@ pipx install --system-site-packages kikit
![Panel](../images/Flow_Controller_panel.png) ![Panel](../images/Flow_Controller_panel.png)
# KiKit Fixture Post-Processor # KiKit Fixture Processor
Post-processing script for KiKit panelized PCBs that places any generated panel Processing script for KiKit panelizes PCBs and draws a fixture sketch and positions the whole panel for easy CNC processing.
inside a fixed CNC fixture coordinate system.
The script: The script:
- Centres the finished panel inside a predefined fixture opening
- Adds mechanical alignment pin holes
- Draws fixture reference geometry - Draws fixture reference geometry
- Moves the complete design so the fixture origin is always at `(0, 0)` - Centres the finished panel inside a predefined fixture frame
- Adds mechanical alignment pin holes
- Moves the finished panel to the origin `(0, 0)`
The resulting output is intended for repeatable CNC manufacturing workflows The resulting output is intended for repeatable CNC manufacturing workflows
where drilling, routing, and UV exposure all share the same physical fixture where drilling, routing, and UV exposure all share the same physical fixture
and machine datum. and alignment holes.
---
# Fixture Geometry
## Outer Fixture Frame
| Parameter | Value |
|---|---|
| Width | 200.0 mm |
| Height | 130.0 mm |
## Inner Fixture Opening
| Parameter | Value |
|---|---|
| Width | 153.4 mm |
| Height | 87.0 mm |
The inner opening defines the usable panel area.
The region between the outer frame and inner opening forms the fixture rails.
---
# Alignment Pins
The script inserts four non-plated alignment holes:
- Diameter: `Ø 3.172 mm NPTH`
- Naming: `PIN1``PIN4`
- Positioning: fully user-defined absolute coordinates
This allows:
- deterministic CNC coordinates
- compatibility with existing jigs
- custom asymmetric fixtures
- reuse across multiple panel layouts
---
# Coordinate System
After processing:
- The fixture outer top-left corner is positioned at `(0, 0)`
- All PCB data is translated accordingly
- Every generated panel shares the same global coordinate system
This creates deterministic CAM output suitable for automated tooling and
repeatable fixture-based manufacturing.
--- ---
# Usage # Usage
Place `fixture_postprocess.py` next to the source `.kicad_pcb` file and run KiKit:
```bash ```bash
# Panelize the PCB using the preset defined in `myPreset.json`.
kikit panelize \ kikit panelize \
-p myPreset.json \ -p myPreset.json \
../Flow_Controller.kicad_pcb \ ../Flow_Controller.kicad_pcb \
panel/Flow_Controller_Panel.kicad_pcb panel/Flow_Controller_Panel.kicad_pcb
``` ```
--- Have a look at the new `panel/Flow_Controller_Panel.kicad_pcb` file. It will contain the panel out ot layout specified in `myPreset.json`. E.g.: Grid of 1 x 2 with space 2.1 mm.
# Panel Layout Parameters ```json
"layout": {
## Grid Layout "type": "grid",
"rows": 1,
Adjust the panel dimensions using: "cols": 2,
"hspace": "2.1mm",
```text "vspace": "2.1mm"
rows: <n> }
cols: <n>
``` ```
Examples:
- `1×1`
- `1×2`
- `2×2`
The script automatically recalculates centering offsets for the resulting panel size.
---
## Tool Diameter Compensation
The following values should match the routing tool diameter:
```text
space
slotwidth
```
Example:
```text
space: 2.1mm
slotwidth: 2.1mm
```
This is typically equal to the outline routing bit diameter.
---
# Processing Pipeline
The post-processing script performs the following operations:
1. Reads the fully panelized KiKit output
2. Calculates the panel bounding box
3. Calculates the offset required to centre the panel inside the fixture opening
4. Translates the complete board dataset:
- footprints
- tracks
- vias
- zones
- drawings
- board outlines
- substrate geometry
- locked items
5. Places four NPTH alignment pin footprints
6. Draws fixture reference rectangles on the `User.1` layer
7. Repositions the design so the fixture outer top-left corner equals `(0, 0)`
---
# Fixture Visualization
The script draws two rectangles on the `User.1` layer:
- Outer fixture boundary
- Inner fixture opening
These graphics:
- are visible in KiCad
- help verify positioning visually
- are NOT placed on `Edge.Cuts`
- are NOT exported into manufacturing Gerbers or NC drill files
They exist purely as fixture reference geometry.
---
# CNC Workflow
| Operation | File | Purpose |
|---|---|---|
| Drilling | Excellon `.drl` | PCB holes + fixture alignment pins |
| Routing | `Edge.Cuts` Gerber | Board outlines and mousebite tabs |
| UV exposure | Copper Gerbers | Soldermask/alignment registration |
Because all outputs share:
- identical `(0,0)` origin
- identical alignment pin locations
- identical fixture geometry
the PCB can remain mounted in the same physical fixture for the complete process.
No re-alignment or re-fixturing is required between operations.
---
# Alignment Pins
The script inserts four non-plated alignment holes:
- Diameter: `Ø 3.172 mm NPTH`
- Naming: `PIN1``PIN4`
Users may override any coordinate manually.
---
# Default Pin Placement Calculation
For the default fixture dimensions:
```python
OUTER_W_MM = 200.0
OUTER_H_MM = 130.0
INNER_W_MM = 153.4
INNER_H_MM = 87.0
```
the resulting suggested coordinates are:
| Pin | X (mm) | Y (mm) |
|---|---|---|
| PIN1 (top) | 100.0 | 10.75 |
| PIN2 (right) | 188.35 | 65.0 |
| PIN3 (bottom) | 100.0 | 119.25 |
| PIN4 (left) | 11.65 | 65.0 |
---
# Fixture Configuration
Edit the constants at the top of `fixture_postprocess.py`:
```python
# ============================================================
# Fixture geometry
# ============================================================
OUTER_W_MM = 200.0
OUTER_H_MM = 130.0
INNER_W_MM = 153.4
INNER_H_MM = 87.0
# ============================================================
# Alignment pins
# Absolute fixture-space coordinates in millimetres
# ============================================================
PIN_DIAMETER_MM = 3.172
# Suggested defaults:
# top = centered in top rail
# right = centered in right rail
# bottom = centered in bottom rail
# left = centered in left rail
PIN1_X_MM = 100.0
PIN1_Y_MM = 10.75
PIN2_X_MM = 188.35
PIN2_Y_MM = 65.0
PIN3_X_MM = 100.0
PIN3_Y_MM = 119.25
PIN4_X_MM = 11.65
PIN4_Y_MM = 65.0
```
These values may be adjusted freely without changing the placement logic.
---
# Design Constraints
The generated panel must fit completely inside the inner fixture opening.
If the panel exceeds the available space, the script emits:
```text
WARNING: panel … is larger than the inner opening
```
Possible solutions:
- reduce `rows`
- reduce `cols`
- reduce KiKit frame width
- reduce spacing
- use a smaller PCB design
---
# Intended Use Case
This workflow is intended for:
- CNC isolation milling
- UV mask alignment systems
- repeatable prototyping fixtures
- hybrid PCB manufacturing workflows
- automated or semi-automated PCB handling
The primary design goal is deterministic panel placement independent of PCB size.
## Exporting gcode files from KiCad ## Exporting gcode files from KiCad
Adapt milling and drilling parameters in `millproject`. Look up [pcb2gcode/wiki](https://github.com/pcb2gcode/pcb2gcode/wiki) for help. Adapt milling and drilling parameters in `millproject`. Look up [pcb2gcode/wiki](https://github.com/pcb2gcode/pcb2gcode/wiki) for help.
```bash ```bash
nano millproject nano millproject
``` ```
*Make sure to set `mirror-axis` in the `millproject` to half of your board width!!!*
Run the export by providing the `.kicad_pcb` file as a first argument: Run the export by providing the `.kicad_pcb` file as a first argument:
```bash ```bash
chmod +x export.sh ./export.sh -o gcode panel/Flow_Controller_Panel.kicad_pcb
./export.sh panel/Flow_Controller_Panel.kicad_pcb
``` ```
The script will first generate gerber files in the `output` directory and then convert them into `ngc` format in the `gcode` directory. The script will first generate gerber files in the `gerbers` directory and then generate gcode files in the `gcode` directory.
Launch the `gSender` program. Launch the `gSender` program.
* Load the `gcode/drill.ngc` file for drilling holes. * Load the `gcode/drill.ngc` file for drilling holes.
* Load the `gcode/outline.ngc` file for milling the board outlines. * Load the `gcode/outline.ngc` file for milling the board outlines.
* Load the `gcode/back.ngc` file if you want to mill the isolation traces. * Load the `gcode/back.ngc` file if you want to mill the isolation traces.
* Load the `gcode/front.ngc` file if you want to mill the isolation traces.
## Milling tip: Increase the thermal spoke and trace width ## Milling tip: Increase the thermal spoke and trace width
When routing for milling, use the widest traces possible. 1mm, 2mm and wider, the machine doesn't care, but later you won't be soldering leads to small fragile strips of copper. You can use copper pours for routing too. When routing for milling, use the widest traces possible. 1mm, 2mm and wider, the machine doesn't care, but later you won't be soldering leads to small fragile strips of copper. You can use copper pours for routing too.

96
scripts/cleanup.py Normal file
View File

@@ -0,0 +1,96 @@
import pcbnew
from shapely.geometry import MultiPolygon, Polygon
# ---------------------------------------------------------------------------
# KiKit postprocess hook cleanup.py
#
# 1. Copy all Edge.Cuts content (substrate rings) to Eco1_User as segments,
# EXCEPT the outermost panel rectangle.
# ---------------------------------------------------------------------------
TOLERANCE_NM = 1_000 # 1 µm
def _mm(nm_val):
return f"{pcbnew.ToMM(int(nm_val)):.4f} mm"
def _collect_rings(geom):
"""Return all rings as list of (shapely_ring, coord_list)."""
rings = []
if isinstance(geom, Polygon):
rings.append((geom.exterior, list(geom.exterior.coords)))
for interior in geom.interiors:
rings.append((interior, list(interior.coords)))
elif isinstance(geom, MultiPolygon):
for poly in geom.geoms:
rings.extend(_collect_rings(poly))
return rings
def _is_outer_frame(ring_geom, x0, y0, x1, y1, tol=TOLERANCE_NM):
"""True if the ring's bounding box equals the panel bounding box."""
b = ring_geom.bounds
return (abs(b[0] - x0) <= tol and abs(b[1] - y0) <= tol and
abs(b[2] - x1) <= tol and abs(b[3] - y1) <= tol)
def _shapely_to_segments(board, coords, layer, width_mm=0.05):
pts = list(coords)
if not pts:
return 0
if pts[0] != pts[-1]:
pts.append(pts[0])
count = 0
for i in range(len(pts) - 1):
seg = pcbnew.PCB_SHAPE(board)
seg.SetShape(pcbnew.SHAPE_T_SEGMENT)
seg.SetLayer(layer)
seg.SetWidth(pcbnew.FromMM(width_mm))
seg.SetStart(pcbnew.VECTOR2I(int(pts[i][0]), int(pts[i][1])))
seg.SetEnd (pcbnew.VECTOR2I(int(pts[i+1][0]), int(pts[i+1][1])))
board.Add(seg)
count += 1
return count
def kikitPostprocess(panel, arg):
print("=" * 60)
print("[cleanup] START")
board = panel.board
substrate = panel.boardSubstrate
# outer panel bbox
b = substrate.bounds()
x0, y0, x1, y1 = int(b[0]), int(b[1]), int(b[2]), int(b[3])
print(f"[cleanup] Panel bbox: ({_mm(x0)},{_mm(y0)})({_mm(x1)},{_mm(y1)})")
# get substrate geometry
geom = None
for attr in ("substrates", "substrate", "_substrate", "geometry"):
if hasattr(substrate, attr):
geom = getattr(substrate, attr)
break
if geom is None:
print("[cleanup] ERROR: cannot access substrate geometry")
return
rings = _collect_rings(geom)
print(f"[cleanup] Total rings in substrate: {len(rings)}")
total = 0
for i, (ring_geom, coords) in enumerate(rings):
rb = ring_geom.bounds
if _is_outer_frame(ring_geom, x0, y0, x1, y1):
print(f"[cleanup] ring[{i}] pts={len(coords)-1} → OUTER FRAME, skipped")
continue
n = _shapely_to_segments(board, coords, pcbnew.Eco1_User)
total += n
print(f"[cleanup] ring[{i}] pts={len(coords)-1}"
f" bbox=({_mm(rb[0])},{_mm(rb[1])})({_mm(rb[2])},{_mm(rb[3])})"
f"{n} segments on Eco1_User")
print(f"[cleanup] Total Eco1_User segments added: {total}")
print("[cleanup] END")
print("=" * 60)

View File

@@ -1,18 +1,32 @@
#!/bin/bash #!/bin/bash
set -e set -euo pipefail
filename=$(basename -- "$1") GERBERS_DIR="gerbers"
filename="${filename%.*}"
kicad-cli pcb export gerbers -o output -l Back $1 usage() {
kicad-cli pcb export gerbers -o output -l Edge.Cuts $1 echo "Usage: $0 <kicad_pcb_file>"
kicad-cli pcb export drill -o output $1 exit 1
}
mkdir -p gcode PCB_FILE="$1"
docker run --rm -i -t -u "$(id -u):$(id -g)" -v ".:/data" ptodorov/pcb2gcode \ mkdir -p "$GERBERS_DIR"
--back="output/$filename-Back.gbl" \
--drill="output/$filename.drl" \
--outline="output/$filename-Edge_Cuts.gm1"
# export drill, front and back layers as gerber files
kicad-cli pcb export drill -o "$GERBERS_DIR" "$PCB_FILE"
kicad-cli pcb export gerbers -o "$GERBERS_DIR" -l Front "$PCB_FILE"
kicad-cli pcb export gerbers -o "$GERBERS_DIR" -l Back "$PCB_FILE"
kicad-cli pcb export gerbers -o "$GERBERS_DIR" -l Edge.Cuts "$PCB_FILE"
# Export outlines of the penelized boards only; Use the layer 'User.Eco1'.
python3 export_panel_outlines_gerber.py \
--layers User.Eco1 \
--output "$GERBERS_DIR" \
"$PCB_FILE"
# set input filenames and all milling parameters in `millproject` config file
docker run --rm -i -t \
-u "$(id -u):$(id -g)" \
-v "$(pwd):/data" \
ptodorov/pcb2gcode

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import argparse
import os
import pcbnew
def main():
parser = argparse.ArgumentParser(
description="Export a single PCB layer to a Gerber file using pcbnew."
)
parser.add_argument("input", help="Input .kicad_pcb file")
parser.add_argument("--output", "-o", required=True, help="Output directory")
parser.add_argument("--layers", "-l", required=True,
help="Layer name to export (e.g. User.Eco1)")
args = parser.parse_args()
board = pcbnew.LoadBoard(args.input)
# Resolve layer name to ID
layer_id = board.GetLayerID(args.layers)
if layer_id == -1:
parser.error(f"Unknown layer: {args.layers!r}. "
f"Run with a known layer name (e.g. User.Eco1, Edge.Cuts).")
# Resolve output directory to absolute path so pcbnew doesn't make it
# relative to the board file location
output_dir = os.path.abspath(args.output)
os.makedirs(output_dir, exist_ok=True)
prefix = args.layers.replace(".", "-")
pc = pcbnew.PLOT_CONTROLLER(board)
po = pc.GetPlotOptions()
po.SetOutputDirectory(output_dir)
po.SetPlotFrameRef(False)
pc.SetLayer(layer_id)
pc.OpenPlotfile(prefix, pcbnew.PLOT_FORMAT_GERBER, args.layers)
print(f"Plotting layer {args.layers!r} (id={layer_id}) to {pc.GetPlotFileName()}")
pc.PlotLayer()
pc.ClosePlot()
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -1,232 +0,0 @@
import math
import pcbnew
from shapely.affinity import translate
# ============================================================
# Fixture geometry
# ============================================================
OUTER_W_MM = 200.0
OUTER_H_MM = 130.0
INNER_W_MM = 153.4
INNER_H_MM = 87.0
INNER_X_MM = (OUTER_W_MM - INNER_W_MM) / 2 # 23.3
INNER_Y_MM = (OUTER_H_MM - INNER_H_MM) / 2 # 21.5
# ============================================================
# Alignment pins
# Absolute fixture-space coordinates in millimetres
# ============================================================
PIN_DIAMETER_MM = 3.172
# Suggested defaults:
# top = centered in top rail
# right = centered in right rail
# bottom = centered in bottom rail
# left = centered in left rail
PIN1_X_MM = 100.0
PIN1_Y_MM = 10.75
PIN2_X_MM = 188.35
PIN2_Y_MM = 65.0
PIN3_X_MM = 100.0
PIN3_Y_MM = 119.25
PIN4_X_MM = 11.65
PIN4_Y_MM = 65.0
# KiCad internal unit: 1 mm = 1 000 000 nm
MM = 1_000_000
def mm(v: float) -> int:
"""Convert mm float to KiCad internal units (nm)."""
return int(round(v * MM))
def _move_all(panel, dx_nm: int, dy_nm: int) -> None:
"""Translate every object on the board by (dx_nm, dy_nm)."""
vec = pcbnew.VECTOR2I(dx_nm, dy_nm)
for fp in panel.board.GetFootprints():
fp.Move(vec)
for track in panel.board.GetTracks():
track.Move(vec)
for drawing in panel.board.GetDrawings():
drawing.Move(vec)
for zone in panel.board.Zones():
zone.Move(vec)
# Keep the Shapely substrate in sync
panel.boardSubstrate.substrates = translate(
panel.boardSubstrate.substrates,
xoff=dx_nm,
yoff=dy_nm,
)
def _edge_cuts_bbox(panel):
"""Return (minx, miny, maxx, maxy) in nm from the Shapely substrate."""
return panel.boardSubstrate.substrates.bounds # already in nm
def _draw_rect(board, layer, x0_nm, y0_nm, x1_nm, y1_nm, width_nm=None):
"""Draw an axis-aligned rectangle on *layer* using four PCB_SHAPE lines."""
if width_nm is None:
width_nm = mm(0.1) # 0.1 mm line width thin reference line
corners = [
(x0_nm, y0_nm), (x1_nm, y0_nm),
(x1_nm, y1_nm), (x0_nm, y1_nm),
]
for i in range(4):
seg = pcbnew.PCB_SHAPE(board)
seg.SetShape(pcbnew.SHAPE_T_SEGMENT)
seg.SetLayer(layer)
seg.SetWidth(width_nm)
x_a, y_a = corners[i]
x_b, y_b = corners[(i + 1) % 4]
seg.SetStart(pcbnew.VECTOR2I(x_a, y_a))
seg.SetEnd(pcbnew.VECTOR2I(x_b, y_b))
board.Add(seg)
def _draw_circle(board, layer, cx_nm, cy_nm, r_nm, width_nm=None):
"""Draw a circle on *layer*."""
if width_nm is None:
width_nm = mm(0.1)
circ = pcbnew.PCB_SHAPE(board)
circ.SetShape(pcbnew.SHAPE_T_CIRCLE)
circ.SetLayer(layer)
circ.SetWidth(width_nm)
circ.SetCenter(pcbnew.VECTOR2I(cx_nm, cy_nm))
circ.SetEnd(pcbnew.VECTOR2I(cx_nm + r_nm, cy_nm))
board.Add(circ)
def _add_pin_footprint(board, x_nm, y_nm, diameter_mm, index):
"""
Add a non-plated through-hole (NPTH) mounting hole footprint at (x_nm, y_nm).
We create a minimal footprint with a single NPTH pad so that:
• The drill appears in the Excellon output → used for physical alignment pins.
• No copper ring is added.
"""
fp = pcbnew.FOOTPRINT(board)
fp.SetReference(f"PIN{index}")
fp.Reference().SetVisible(False)
fp.SetValue(f"Ø{diameter_mm:.3f}mm NPTH")
fp.Value().SetVisible(False)
fp.SetPosition(pcbnew.VECTOR2I(x_nm, y_nm))
pad = pcbnew.PAD(fp)
pad.SetAttribute(pcbnew.PAD_ATTRIB_NPTH)
pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE)
pad.SetDrillShape(pcbnew.PAD_DRILL_SHAPE_CIRCLE)
d_nm = mm(diameter_mm)
pad.SetSize(pcbnew.VECTOR2I(d_nm, d_nm))
pad.SetDrillSize(pcbnew.VECTOR2I(d_nm, d_nm))
pad.SetLayerSet(pcbnew.LSET.AllCuMask()) # NPTH pads span all Cu layers
pad.SetNumber("")
fp.Add(pad)
board.Add(fp)
# Also draw a circle on Dwgs_User so the pin is visible even without
# the fab/courtyard layers being turned on.
_draw_circle(
board,
pcbnew.Dwgs_User,
x_nm, y_nm,
mm(diameter_mm / 2),
mm(0.1),
)
return fp
# ──────────────────────────────────────────────────────────────
# MAIN ENTRY POINT
# ──────────────────────────────────────────────────────────────
def kikitPostprocess(panel, args):
board = panel.board
# ── Step 1: get current Edge.Cuts bounding box ────────────────────────────
bounds = _edge_cuts_bbox(panel) # (minx, miny, maxx, maxy) in nm
panel_w_nm = bounds[2] - bounds[0]
panel_h_nm = bounds[3] - bounds[1]
panel_w_mm = panel_w_nm / MM
panel_h_mm = panel_h_nm / MM
print(f"[fixture] Panel size: {panel_w_mm:.3f} x {panel_h_mm:.3f} mm")
# ── Step 2: compute where panel top-left should land ─────────────────────
# We want the panel centred inside the inner opening of the fixture.
# Inner opening: top-left is at (INNER_X_MM, INNER_Y_MM) relative to
# the fixture outer top-left.
panel_target_x_mm = INNER_X_MM + (INNER_W_MM - panel_w_mm) / 2
panel_target_y_mm = INNER_Y_MM + (INNER_H_MM - panel_h_mm) / 2
print(f"[fixture] Panel will be placed at ({panel_target_x_mm:.3f}, {panel_target_y_mm:.3f}) mm")
# Warn if the panel is larger than the inner opening (it will still be
# centred but will overlap the frame rails).
if panel_w_mm > INNER_W_MM or panel_h_mm > INNER_H_MM:
print(
f"[fixture] WARNING: panel ({panel_w_mm:.1f}×{panel_h_mm:.1f} mm) "
f"is larger than the inner opening ({INNER_W_MM}×{INNER_H_MM} mm)!"
)
# ── Step 3: move everything so that the panel lands at the target ─────────
# Current panel TL is at (bounds[0], bounds[1]).
# We need to shift by (target - current).
dx_nm = mm(panel_target_x_mm) - bounds[0]
dy_nm = mm(panel_target_y_mm) - bounds[1]
_move_all(panel, int(dx_nm), int(dy_nm))
# ── Step 4: verify final position ────────────────────────────────────────
new_bounds = _edge_cuts_bbox(panel)
print(
f"[fixture] Panel TL after move: "
f"({new_bounds[0]/MM:.3f}, {new_bounds[1]/MM:.3f}) mm"
)
# ── Step 5: draw fixture frame on Dwgs_User ───────────────────────────
# Outer rectangle: (0,0) → (200, 130) mm
_draw_rect(
board, pcbnew.Dwgs_User,
mm(0), mm(0),
mm(OUTER_W_MM), mm(OUTER_H_MM),
mm(0.15),
)
# Inner rectangle: (23.3, 21.5) → (176.7, 108.5) mm
_draw_rect(
board, pcbnew.Dwgs_User,
mm(INNER_X_MM), mm(INNER_Y_MM),
mm(INNER_X_MM + INNER_W_MM), mm(INNER_Y_MM + INNER_H_MM),
mm(0.15),
)
print("[fixture] Fixture frame drawn on Dwgs_User layer.")
# ── Step 6: add alignment pin footprints ──────────────────────────────────
# Place pins at the absolute coordinates defined in the configuration block above
pin_positions_mm = [
(PIN1_X_MM, PIN1_Y_MM, "PIN1 (Top)"),
(PIN2_X_MM, PIN2_Y_MM, "PIN2 (Right)"),
(PIN3_X_MM, PIN3_Y_MM, "PIN3 (Bottom)"),
(PIN4_X_MM, PIN4_Y_MM, "PIN4 (Left)"),
]
for idx, (px_mm, py_mm, label) in enumerate(pin_positions_mm, start=1):
_add_pin_footprint(board, mm(px_mm), mm(py_mm), PIN_DIAMETER_MM, idx)
print(
f"[fixture] {label} placed at "
f"({px_mm:.3f}, {py_mm:.3f}) mm"
)
print("[fixture] Post-processing complete.")

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="418.014" height="221.1" viewBox="0 0 8708.63 4606.25" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<polyline points="1970.1,4028.53 1970.01,4028.52 1969.62,4028.52 1969.55,4028.53" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1970.1,4028.53 1970.01,4028.52 1969.62,4028.52 1969.55,4028.53" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1969.52,4028.53 1969.62,4028.52 1970.01,4028.52 1970.12,4028.53" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1969.52,4028.53 1969.62,4028.52 1970.01,4028.52 1970.12,4028.53" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="6072.67,4028.57 6072.5,4028.56 6072.37,4028.54 6071.98,4028.53 6071.88,4028.53" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="6072.67,4028.57 6072.5,4028.56 6072.37,4028.54 6071.98,4028.53 6071.88,4028.53" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="6071.86,4028.54 6071.98,4028.53 6072.37,4028.54 6072.5,4028.56 6072.7,4028.57" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="6071.86,4028.54 6071.98,4028.53 6072.37,4028.54 6072.5,4028.56 6072.7,4028.57" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="418.014" height="221.1" viewBox="0 0 8708.63 4606.25" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<polyline points="1969.56,4028.53 1970.06,4028.53" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1969.56,4028.53 1970.06,4028.53" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1970.06,4028.53 1969.56,4028.53" style="stroke:rgb(255,0,0);stroke-width:3.937008;fill:none;stroke-opacity:0.5;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
<polyline points="1970.06,4028.53 1969.56,4028.53" style="stroke:rgb(0,0,0);stroke-width:1px;fill:none;stroke-opacity:1;stroke-linecap:round;stroke-linejoin:round;;fill:none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,372 +0,0 @@
( pcb2gcode 2.5.0 )
( Software-independent Gcode )
( This file uses only one drill bit. Forced by 'onedrill' option )
G94 (Millimeters per minute feed rate.)
G21 (Units == Millimeters.)
G91.1 (Incremental arc distance mode.)
G90 (Absolute coordinates.)
G00 S24000 (RPM spindle speed.)
G00 Z35.00000 (Retract)
T1
M5 (Spindle stop.)
G04 P1.00000
(MSG, Change tool bit to drill size 0.95mm)
M0 (Temporary machine stop.)
M3 (Spindle on clockwise.)
G0 Z2.00000
G04 P1.00000
G1 F500.00000
G0 X0.00000 Y-10.75000
G1 Z-2.20000
G1 Z2.00000
G0 X29.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X28.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X27.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X26.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X25.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X24.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X23.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X20.36700 Y-45.14700
G1 Z-2.20000
G1 Z2.00000
G0 X14.13000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X11.59000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X9.05000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X4.05000 Y-43.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-4.05000 Y-43.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.84800 Y-45.17900
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-56.12000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-58.66000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-61.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-63.74000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-66.28000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-68.82000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-71.36000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-62.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-63.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-64.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-66.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-67.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.41600 Y-73.90000
G1 Z-2.20000
G1 Z2.00000
G0 X-18.90000 Y-69.42500
G1 Z-2.20000
G1 Z2.00000
G0 X-18.80000 Y-65.67500
G1 Z-2.20000
G1 Z2.00000
G0 X-31.73300 Y-45.14700
G1 Z-2.20000
G1 Z2.00000
G0 X-23.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-24.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-25.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-26.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-27.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-28.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-29.05000 Y-40.20000
G1 Z-2.20000
G1 Z2.00000
G0 X-37.97000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X-40.51000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.60000 Y-53.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.60000 Y-55.50000
G1 Z-2.20000
G1 Z2.00000
G0 X-43.05000 Y-45.55000
G1 Z-2.20000
G1 Z2.00000
G0 X-48.05000 Y-43.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-88.35000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-62.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.60000 Y-58.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-63.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-64.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-66.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-67.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-50.85000 Y-68.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.65000 Y-73.85000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.65000 Y-76.35000
G1 Z-2.20000
G1 Z2.00000
G0 X-45.65000 Y-78.85000
G1 Z-2.20000
G1 Z2.00000
G0 X-48.05000 Y-87.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-31.78400 Y-84.82200
G1 Z-2.20000
G1 Z2.00000
G0 X-29.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-28.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-27.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-26.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-25.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-24.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-23.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X-8.92400 Y-84.84700
G1 Z-2.20000
G1 Z2.00000
G0 X-4.05000 Y-87.00000
G1 Z-2.20000
G1 Z2.00000
G0 X4.05000 Y-87.00000
G1 Z-2.20000
G1 Z2.00000
G0 X6.45000 Y-78.85000
G1 Z-2.20000
G1 Z2.00000
G0 X6.45000 Y-76.35000
G1 Z-2.20000
G1 Z2.00000
G0 X6.45000 Y-73.85000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-68.00000
G1 Z-2.20000
G1 Z2.00000
G0 X-1.25000 Y-68.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-67.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-66.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-64.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-63.00000
G1 Z-2.20000
G1 Z2.00000
G0 X1.25000 Y-62.00000
G1 Z-2.20000
G1 Z2.00000
G0 X6.50000 Y-58.00000
G1 Z-2.20000
G1 Z2.00000
G0 X6.50000 Y-55.50000
G1 Z-2.20000
G1 Z2.00000
G0 X6.50000 Y-53.00000
G1 Z-2.20000
G1 Z2.00000
G0 X33.30000 Y-65.67500
G1 Z-2.20000
G1 Z2.00000
G0 X33.20000 Y-69.42500
G1 Z-2.20000
G1 Z2.00000
G0 X20.31600 Y-84.82200
G1 Z-2.20000
G1 Z2.00000
G0 X23.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X24.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X25.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X26.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X27.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X0.00000 Y-119.25000
G1 Z-2.20000
G1 Z2.00000
G0 X28.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X29.05000 Y-89.80000
G1 Z-2.20000
G1 Z2.00000
G0 X43.17600 Y-84.84700
G1 Z-2.20000
G1 Z2.00000
G0 X48.05000 Y-87.00000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-73.90000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-71.36000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-68.82000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-66.28000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-63.74000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-68.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-67.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-66.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-64.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-63.00000
G1 Z-2.20000
G1 Z2.00000
G0 X50.85000 Y-62.00000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-61.20000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-58.66000
G1 Z-2.20000
G1 Z2.00000
G0 X43.68400 Y-56.12000
G1 Z-2.20000
G1 Z2.00000
G0 X43.25200 Y-45.17900
G1 Z-2.20000
G1 Z2.00000
G0 X48.05000 Y-43.00000
G1 Z-2.20000
G1 Z2.00000
G0 X88.35000 Y-65.00000
G1 Z-2.20000
G1 Z2.00000
G00 Z35.000 ( All done -- retract )
M5 (Spindle off.)
G04 P1.000000
M9 (Coolant off.)
M2 (Program end.)

View File

@@ -1,121 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="418.014" height="221.1" viewBox="0 0 8708.63 4606.25" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="4354.33" cy="-1968.53" r="37.4016" style=""/>
<circle cx="2066.93" cy="350.371" r="37.4016" style=""/>
<circle cx="2145.67" cy="350.371" r="37.4016" style=""/>
<circle cx="2224.41" cy="350.371" r="37.4016" style=""/>
<circle cx="2303.15" cy="350.371" r="37.4016" style=""/>
<circle cx="2381.89" cy="350.371" r="37.4016" style=""/>
<circle cx="2460.63" cy="350.371" r="37.4016" style=""/>
<circle cx="2539.37" cy="350.371" r="37.4016" style=""/>
<circle cx="2750.63" cy="739.899" r="37.4016" style=""/>
<circle cx="3241.73" cy="771.631" r="37.4016" style=""/>
<circle cx="3441.73" cy="771.631" r="37.4016" style=""/>
<circle cx="3641.73" cy="771.631" r="37.4016" style=""/>
<circle cx="4035.43" cy="570.844" r="37.4016" style=""/>
<circle cx="4673.23" cy="570.844" r="37.4016" style=""/>
<circle cx="5051.02" cy="742.419" r="37.4016" style=""/>
<circle cx="5017.01" cy="1603.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="1803.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="2003.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="2203.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="2403.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="2603.91" r="37.4016" style=""/>
<circle cx="5017.01" cy="2803.91" r="37.4016" style=""/>
<circle cx="4452.76" cy="2066.91" r="37.4016" style=""/>
<circle cx="4452.76" cy="2145.65" r="37.4016" style=""/>
<circle cx="4452.76" cy="2224.39" r="37.4016" style=""/>
<circle cx="4452.76" cy="2303.13" r="37.4016" style=""/>
<circle cx="4452.76" cy="2381.87" r="37.4016" style=""/>
<circle cx="4452.76" cy="2460.61" r="37.4016" style=""/>
<circle cx="5017.01" cy="3003.91" r="37.4016" style=""/>
<circle cx="5842.52" cy="2651.55" r="37.4016" style=""/>
<circle cx="5834.65" cy="2356.28" r="37.4016" style=""/>
<circle cx="6852.99" cy="739.899" r="37.4016" style=""/>
<circle cx="6169.29" cy="350.371" r="37.4016" style=""/>
<circle cx="6248.03" cy="350.371" r="37.4016" style=""/>
<circle cx="6326.77" cy="350.371" r="37.4016" style=""/>
<circle cx="6405.51" cy="350.371" r="37.4016" style=""/>
<circle cx="6484.25" cy="350.371" r="37.4016" style=""/>
<circle cx="6562.99" cy="350.371" r="37.4016" style=""/>
<circle cx="6641.73" cy="350.371" r="37.4016" style=""/>
<circle cx="7344.09" cy="771.631" r="37.4016" style=""/>
<circle cx="7544.09" cy="771.631" r="37.4016" style=""/>
<circle cx="7944.88" cy="1358.25" r="37.4016" style=""/>
<circle cx="7944.88" cy="1555.1" r="37.4016" style=""/>
<circle cx="7744.09" cy="771.631" r="37.4016" style=""/>
<circle cx="8137.8" cy="570.844" r="37.4016" style=""/>
<circle cx="11311" cy="2303.13" r="37.4016" style=""/>
<circle cx="8358.27" cy="2066.91" r="37.4016" style=""/>
<circle cx="7944.88" cy="1751.95" r="37.4016" style=""/>
<circle cx="8358.27" cy="2145.65" r="37.4016" style=""/>
<circle cx="8358.27" cy="2224.39" r="37.4016" style=""/>
<circle cx="8358.27" cy="2303.13" r="37.4016" style=""/>
<circle cx="8358.27" cy="2381.87" r="37.4016" style=""/>
<circle cx="8358.27" cy="2460.61" r="37.4016" style=""/>
<circle cx="8358.27" cy="2539.35" r="37.4016" style=""/>
<circle cx="7948.82" cy="2999.98" r="37.4016" style=""/>
<circle cx="7948.82" cy="3196.83" r="37.4016" style=""/>
<circle cx="7948.82" cy="3393.68" r="37.4016" style=""/>
<circle cx="8137.8" cy="4035.41" r="37.4016" style=""/>
<circle cx="6857.01" cy="3863.91" r="37.4016" style=""/>
<circle cx="6641.73" cy="4255.88" r="37.4016" style=""/>
<circle cx="6562.99" cy="4255.88" r="37.4016" style=""/>
<circle cx="6484.25" cy="4255.88" r="37.4016" style=""/>
<circle cx="6405.51" cy="4255.88" r="37.4016" style=""/>
<circle cx="6326.77" cy="4255.88" r="37.4016" style=""/>
<circle cx="6248.03" cy="4255.88" r="37.4016" style=""/>
<circle cx="6169.29" cy="4255.88" r="37.4016" style=""/>
<circle cx="5057.01" cy="3865.88" r="37.4016" style=""/>
<circle cx="4673.23" cy="4035.41" r="37.4016" style=""/>
<circle cx="4035.43" cy="4035.41" r="37.4016" style=""/>
<circle cx="3846.46" cy="3393.68" r="37.4016" style=""/>
<circle cx="3846.46" cy="3196.83" r="37.4016" style=""/>
<circle cx="3846.46" cy="2999.98" r="37.4016" style=""/>
<circle cx="4255.91" cy="2539.35" r="37.4016" style=""/>
<circle cx="4452.76" cy="2539.35" r="37.4016" style=""/>
<circle cx="4255.91" cy="2460.61" r="37.4016" style=""/>
<circle cx="4255.91" cy="2381.87" r="37.4016" style=""/>
<circle cx="4255.91" cy="2303.13" r="37.4016" style=""/>
<circle cx="4255.91" cy="2224.39" r="37.4016" style=""/>
<circle cx="4255.91" cy="2145.65" r="37.4016" style=""/>
<circle cx="4255.91" cy="2066.91" r="37.4016" style=""/>
<circle cx="3842.52" cy="1751.95" r="37.4016" style=""/>
<circle cx="3842.52" cy="1555.1" r="37.4016" style=""/>
<circle cx="3842.52" cy="1358.25" r="37.4016" style=""/>
<circle cx="1732.28" cy="2356.28" r="37.4016" style=""/>
<circle cx="1740.16" cy="2651.55" r="37.4016" style=""/>
<circle cx="2754.65" cy="3863.91" r="37.4016" style=""/>
<circle cx="2539.37" cy="4255.88" r="37.4016" style=""/>
<circle cx="2460.63" cy="4255.88" r="37.4016" style=""/>
<circle cx="2381.89" cy="4255.88" r="37.4016" style=""/>
<circle cx="2303.15" cy="4255.88" r="37.4016" style=""/>
<circle cx="2224.41" cy="4255.88" r="37.4016" style=""/>
<circle cx="4354.33" cy="6574.78" r="37.4016" style=""/>
<circle cx="2145.67" cy="4255.88" r="37.4016" style=""/>
<circle cx="2066.93" cy="4255.88" r="37.4016" style=""/>
<circle cx="954.646" cy="3865.88" r="37.4016" style=""/>
<circle cx="570.866" cy="4035.41" r="37.4016" style=""/>
<circle cx="914.646" cy="3003.91" r="37.4016" style=""/>
<circle cx="914.646" cy="2803.91" r="37.4016" style=""/>
<circle cx="914.646" cy="2603.91" r="37.4016" style=""/>
<circle cx="914.646" cy="2403.91" r="37.4016" style=""/>
<circle cx="914.646" cy="2203.91" r="37.4016" style=""/>
<circle cx="350.394" cy="2539.35" r="37.4016" style=""/>
<circle cx="350.394" cy="2460.61" r="37.4016" style=""/>
<circle cx="350.394" cy="2381.87" r="37.4016" style=""/>
<circle cx="350.394" cy="2303.13" r="37.4016" style=""/>
<circle cx="350.394" cy="2224.39" r="37.4016" style=""/>
<circle cx="350.394" cy="2145.65" r="37.4016" style=""/>
<circle cx="350.394" cy="2066.91" r="37.4016" style=""/>
<circle cx="914.646" cy="2003.91" r="37.4016" style=""/>
<circle cx="914.646" cy="1803.91" r="37.4016" style=""/>
<circle cx="914.646" cy="1603.91" r="37.4016" style=""/>
<circle cx="948.661" cy="742.419" r="37.4016" style=""/>
<circle cx="570.866" cy="570.844" r="37.4016" style=""/>
<circle cx="-2602.36" cy="2303.13" r="37.4016" style=""/>
</svg>

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -1,322 +0,0 @@
( pcb2gcode 2.5.0 )
( Software-independent Gcode )
G94 ( Millimeters per minute feed rate. )
G21 ( Units == Millimeters. )
G90 ( Absolute coordinates. )
G00 S24000 ( RPM spindle speed. )
G01 F400.00000 ( Feedrate. )
G00 Z35.00000 (Retract to tool change height)
T1
M5 (Spindle stop.)
G04 P1.00000 (Wait for spindle to stop)
(MSG, Change tool bit to cutter diameter 2.10000mm)
M0 (Temporary machine stop.)
M3 ( Spindle on clockwise. )
G04 P1.00000 (Wait for spindle to get up to speed)
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X52.00002 Y-90.92517 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X51.99966 Y-90.71861
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X51.99938 Y-90.55170
G00 Z0.00000
G01 X51.99922 Y-90.45729
G01 X51.99921 Y-90.45212
G01 X51.94965 Y-90.54069
G01 X51.90472 Y-90.60924
G01 X51.86050 Y-90.66640
G01 X51.85455 Y-90.67409
G01 X51.79225 Y-90.74211
G01 X51.72425 Y-90.80442
G01 X51.65941 Y-90.85458
G01 X51.64306 Y-90.86530
G01 X51.59085 Y-90.89954
G01 X51.50215 Y-90.94917
G01 X51.57808 Y-90.94930
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X51.59941 Y-90.94934
G00 Z0.00000
G01 X51.86031 Y-90.94979
G01 X51.97523 Y-90.94998
G01 X51.99885 Y-90.94881
G01 X52.00002 Y-90.92517
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X-51.49954 Y-90.94907 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X-51.50760 Y-90.94906
G01 X-51.83861 Y-90.94890
G01 X-51.99881 Y-90.94881
G01 X-51.99881 Y-90.81165
G01 X-51.99881 Y-90.78886
G01 X-51.99881 Y-90.54960
G01 X-51.99881 Y-90.45003
G01 X-51.94631 Y-90.53032
G01 X-51.90694 Y-90.58112
G01 X-51.88938 Y-90.60378
G01 X-51.72091 Y-90.77792
G01 X-51.70116 Y-90.79542
G01 X-51.63076 Y-90.85778
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-51.62078 Y-90.86662
G00 Z0.00000
G01 X-51.49954 Y-90.94907
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X52.00002 Y-39.53165 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X51.99904 Y-39.12963
G01 X51.99885 Y-39.05113
G01 X51.86234 Y-39.05103
G01 X51.82895 Y-39.05101
G01 X51.61072 Y-39.05085
G01 X51.50154 Y-39.05077
G01 X51.58783 Y-39.09875
G01 X51.65565 Y-39.14285
G01 X51.71988 Y-39.19205
G01 X51.75973 Y-39.22776
G01 X51.78013 Y-39.24605
G01 X51.84272 Y-39.31214
G01 X51.85955 Y-39.33333
G01 X51.89933 Y-39.38341
G01 X51.92100 Y-39.41578
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X51.94434 Y-39.45064
G00 Z0.00000
G01 X51.99679 Y-39.54362
G01 X51.99921 Y-39.54790
G01 X52.00002 Y-39.53165
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X-51.49954 Y-39.05087 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X-51.83765 Y-39.05105
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-51.86302 Y-39.05106
G00 Z0.00000
G01 X-51.99881 Y-39.05113
G01 X-51.99890 Y-39.21225
G01 X-51.99890 Y-39.21325
G01 X-51.99909 Y-39.55031
G01 X-51.91676 Y-39.42809
G01 X-51.90766 Y-39.41773
G01 X-51.84620 Y-39.34777
G01 X-51.82799 Y-39.32705
G01 X-51.68163 Y-39.18700
G01 X-51.65139 Y-39.15806
G01 X-51.63164 Y-39.14290
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-51.57884 Y-39.10236
G00 Z0.00000
G01 X-51.49954 Y-39.05087
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X0.59796 Y-90.94917 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X-0.38604 Y-90.94915
G01 X-0.59840 Y-90.94914
G01 X-0.57576 Y-90.93378
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-0.43737 Y-90.83987
G00 Z0.00000
G01 X-0.34275 Y-90.76319
G01 X-0.24445 Y-90.66063
G01 X-0.16635 Y-90.55430
G01 X-0.09787 Y-90.42982
G01 X-0.04682 Y-90.29723
G01 X-0.01413 Y-90.15898
G01 X-0.00041 Y-90.01757
G01 X-0.00040 Y-89.77894
G01 X-0.00038 Y-88.78861
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-0.00022 Y-80.67196
G00 Z0.00000
G01 X-0.00017 Y-78.07196
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X0.00002 Y-68.72635
G01 X0.00095 Y-89.80499
G00 Z0.00000
G01 X0.00096 Y-90.03035
G01 X0.01481 Y-90.17231
G01 X0.04775 Y-90.31107
G01 X0.09918 Y-90.44409
G01 X0.16817 Y-90.56893
G01 X0.25344 Y-90.68323
G01 X0.35341 Y-90.78496
G01 X0.46626 Y-90.87218
G01 X0.59250 Y-90.94598
G01 X0.59796 Y-90.94917
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X0.59796 Y-39.05077 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G00 Z0.00000
G01 X-0.38717 Y-39.05079
G01 X-0.59916 Y-39.05080
G01 X-0.57714 Y-39.06571
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-0.43132 Y-39.16444
G00 Z0.00000
G01 X-0.34450 Y-39.23515
G01 X-0.25230 Y-39.33002
G01 X-0.16724 Y-39.44427
G01 X-0.09840 Y-39.56901
G01 X-0.04707 Y-39.70190
G01 X-0.01420 Y-39.84051
G01 X-0.00041 Y-39.98229
G01 X-0.00040 Y-40.22440
G01 X-0.00038 Y-41.21107
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-0.00022 Y-49.32795
G00 Z0.00000
G01 X-0.00017 Y-51.92795
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X0.00002 Y-61.27361
G01 X0.00095 Y-40.19495
G00 Z0.00000
G01 X0.00096 Y-39.96959
G01 X0.01481 Y-39.82763
G01 X0.04775 Y-39.68887
G01 X0.09918 Y-39.55585
G01 X0.16817 Y-39.43101
G01 X0.25344 Y-39.31671
G01 X0.35341 Y-39.21498
G01 X0.46626 Y-39.12776
G01 X0.59665 Y-39.05154
G01 X0.59796 Y-39.05077
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z2.00000 ( retract )
G00 X54.20002 Y-37.91288 ( rapid move to begin. )
G01 Z-4.00000 F50.00000 ( plunge. )
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G01 F400.00000
G01 X54.19509 Y-37.81123
G01 X54.18033 Y-37.71054
G01 X54.15592 Y-37.61176
G01 X54.12204 Y-37.51580
G01 X54.07904 Y-37.42357
G01 X54.02732 Y-37.33592
G01 X53.96735 Y-37.25372
G01 X53.89969 Y-37.17768
G01 X53.82501 Y-37.10856
G01 X53.74398 Y-37.04699
G01 X53.65737 Y-36.99358
G01 X53.55663 Y-36.94478
G01 X53.46100 Y-36.90998
G01 X53.36245 Y-36.88458
G01 X53.26192 Y-36.86884
G01 X53.16032 Y-36.86292
G01 X1.30565 Y-36.85680
G00 Z0.00000
G01 X-1.29435 Y-36.85649
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-53.14902 Y-36.85037
G01 X-53.27092 Y-36.85746
G01 X-53.39118 Y-36.87864
G01 X-53.50818 Y-36.91367
G01 X-53.62029 Y-36.96200
G01 X-53.74295 Y-37.03442
G01 X-53.85468 Y-37.12276
G01 X-53.95344 Y-37.22540
G01 X-54.03739 Y-37.34046
G01 X-54.10500 Y-37.46584
G01 X-54.15501 Y-37.59919
G01 X-54.17533 Y-37.67806
G01 X-54.18806 Y-37.74816
G01 X-54.19908 Y-37.89017
G01 X-54.19932 Y-63.69465
G00 Z0.00000
G01 X-54.19935 Y-66.29465
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X-54.19959 Y-92.09913
G01 X-54.19250 Y-92.22102
G01 X-54.17129 Y-92.34129
G01 X-54.13627 Y-92.45826
G01 X-54.08791 Y-92.57038
G01 X-54.01552 Y-92.69303
G01 X-53.97323 Y-92.75039
G01 X-53.92029 Y-92.81226
G01 X-53.81668 Y-92.91000
G01 X-53.70081 Y-92.99280
G01 X-53.57480 Y-93.05920
G01 X-53.50871 Y-93.08582
G01 X-53.43119 Y-93.11068
G01 X-53.29169 Y-93.13949
G01 X-53.14960 Y-93.14914
G01 X-1.30022 Y-93.14935
G00 Z0.00000
G01 X1.29978 Y-93.14936
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X53.14917 Y-93.14957
G01 X53.27106 Y-93.14249
G01 X53.39133 Y-93.12128
G01 X53.50830 Y-93.08625
G01 X53.62041 Y-93.03789
G01 X53.74307 Y-92.96550
G01 X53.85478 Y-92.87716
G01 X53.95351 Y-92.77452
G01 X54.03746 Y-92.65945
G01 X54.07340 Y-92.59791
G01 X54.10924 Y-92.52481
G01 X54.15793 Y-92.39098
G01 X54.18810 Y-92.25176
G01 X54.19913 Y-92.10977
G01 X54.19955 Y-66.31133
G00 Z0.00000
G01 X54.19959 Y-63.71133
G01 Z-4.00000 F50.00000
G01 F400.00000
G01 X54.20002 Y-37.91288
G04 P0 ( dwell for no time -- G64 should not smooth over this point )
G00 Z35.000000 ( retract )
M5 ( Spindle off. )
G04 P1.000000
M9 ( Coolant off. )
M2 ( Program end. )

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 211 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 211 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 982 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1006 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 280 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,376 @@
"""
=============================================================================
MACRO: CNC Slot Skeletonizer (Centerlines Only)
=============================================================================
DESCRIPTION:
This macro is specifically designed for CAM/CNC routing where the router bit
diameter matches the slot width. It reads a DXF-style slot sketch, calculates
the mathematical centerlines, and produces a sketch containing ONLY the
toolpaths (centerlines).
WHAT IT DOES:
1. Safely duplicates the selected sketch.
2. Mathematically calculates true Arcs from faceted DXF micro-segments.
3. Calculates intersections for Straight, L-shape, and T-shape junctions.
4. Merges collinear centerline segments.
5. ADVANCED CAM OPTIMIZATION: Groups connected lines into continuous chains so
the tool doesn't lift unnecessarily. Starts the job at the origin (0,0) and
takes the shortest rapid paths between cut groups.
6. WIPES THE SKETCH COMPLETELY CLEAN of all original geometry.
7. Draws ONLY the pure skeleton centerlines.
=============================================================================
"""
import FreeCAD as App
import FreeCADGui as Gui
import Part
import math
try:
from PySide6.QtWidgets import QMessageBox
except ImportError:
try:
from PySide2.QtWidgets import QMessageBox
except ImportError:
from PySide.QtGui import QMessageBox
# =================================================================
# CONFIGURATION
MAX_SEGMENT_LENGTH = 0.5
# =================================================================
def get_circumcenter(p1, p2, p3):
temp = p2.x**2 + p2.y**2
bc = (p1.x**2 + p1.y**2 - temp) / 2.0
cd = (temp - p3.x**2 - p3.y**2) / 2.0
det = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y)
if abs(det) < 1e-6: return None, None
cx = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) / det
cy = ((p1.x - p2.x) * cd - (p2.x - p3.x) * bc) / det
center = App.Vector(cx, cy, 0)
radius = (p1 - center).Length
return center, radius
def prepare_working_sketch():
doc = App.ActiveDocument
if not doc: return None
target = None
edit_view = Gui.ActiveDocument.getInEdit()
if edit_view and edit_view.Object.isDerivedFrom("Sketcher::SketchObject"):
target = edit_view.Object
Gui.ActiveDocument.resetEdit()
if not target:
sel = Gui.Selection.getSelection()
if sel and sel[0].isDerivedFrom("Sketcher::SketchObject"):
target = sel[0]
if not target: return None
new_sketch = doc.copyObject(target, False)
new_sketch.Label = target.Label + "_Toolpath"
if hasattr(target, "ViewObject") and target.ViewObject:
target.ViewObject.Visibility = False
if hasattr(new_sketch, "ViewObject") and new_sketch.ViewObject:
new_sketch.ViewObject.Visibility = True
doc.recompute()
return new_sketch
def merge_collinear_lines(lines):
merged = True
while merged:
merged = False
for i in range(len(lines)):
for j in range(i + 1, len(lines)):
l1 = lines[i]
l2 = lines[j]
shared_pt, other1, other2 = None, None, None
tol = 1e-3
if (l1[0] - l2[0]).Length < tol: shared_pt, other1, other2 = l1[0], l1[1], l2[1]
elif (l1[0] - l2[1]).Length < tol: shared_pt, other1, other2 = l1[0], l1[1], l2[0]
elif (l1[1] - l2[0]).Length < tol: shared_pt, other1, other2 = l1[1], l1[0], l2[1]
elif (l1[1] - l2[1]).Length < tol: shared_pt, other1, other2 = l1[1], l1[0], l2[0]
if shared_pt:
v1 = (other1 - shared_pt)
v2 = (other2 - shared_pt)
if v1.Length > tol and v2.Length > tol:
v1.normalize()
v2.normalize()
if abs(v1.dot(v2) + 1.0) < 1e-4:
lines.pop(j)
lines.pop(i)
lines.append((other1, other2))
merged = True
break
if merged: break
return lines
def optimize_toolpath_order(lines):
if not lines: return []
# Phase 1: Build continuous chains (Polylines)
unvisited = lines.copy()
chains = []
tol = 1e-3
while unvisited:
current_chain = [unvisited.pop(0)]
growing = True
while growing:
growing = False
chain_start = current_chain[0][0]
chain_end = current_chain[-1][1]
for i, line in enumerate(unvisited):
l_start, l_end = line[0], line[1]
if (l_start - chain_end).Length < tol:
current_chain.append(unvisited.pop(i))
growing = True; break
elif (l_end - chain_end).Length < tol:
current_chain.append((l_end, l_start)) # Flip
unvisited.pop(i)
growing = True; break
elif (l_end - chain_start).Length < tol:
current_chain.insert(0, unvisited.pop(i))
growing = True; break
elif (l_start - chain_start).Length < tol:
current_chain.insert(0, (l_end, l_start)) # Flip
unvisited.pop(i)
growing = True; break
chains.append(current_chain)
# Phase 2: Traveling Salesperson between chains
unvisited_chains = chains.copy()
optimized_lines = []
# Start with the chain closest to global Origin (0,0,0)
start_idx = 0
best_origin_dist = float('inf')
for i, chain in enumerate(unvisited_chains):
d1 = chain[0][0].Length
d2 = chain[-1][1].Length
if min(d1, d2) < best_origin_dist:
best_origin_dist = min(d1, d2)
start_idx = i
current_chain = unvisited_chains.pop(start_idx)
# Ensure it starts at the point closer to origin
if current_chain[-1][1].Length < current_chain[0][0].Length:
current_chain.reverse()
current_chain = [(l[1], l[0]) for l in current_chain]
optimized_lines.extend(current_chain)
current_pos = current_chain[-1][1] # Position after cutting first chain
while unvisited_chains:
best_dist = float('inf')
best_idx = -1
reverse_chain = False
for i, chain in enumerate(unvisited_chains):
c_start = chain[0][0]
c_end = chain[-1][1]
dist_to_start = (c_start - current_pos).Length
dist_to_end = (c_end - current_pos).Length
if dist_to_start < best_dist:
best_dist = dist_to_start
best_idx = i
reverse_chain = False
if dist_to_end < best_dist:
best_dist = dist_to_end
best_idx = i
reverse_chain = True
next_chain = unvisited_chains.pop(best_idx)
if reverse_chain:
next_chain.reverse()
next_chain = [(l[1], l[0]) for l in next_chain]
optimized_lines.extend(next_chain)
current_pos = next_chain[-1][1]
return optimized_lines
def show_message(title, message):
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setWindowTitle(title)
msg.setText(message)
try:
msg.exec()
except AttributeError:
msg.exec_()
def process_sketch():
sketch = prepare_working_sketch()
if not sketch:
App.Console.PrintError("Could not find a sketch. Select one in the tree view and run.\n")
return
geo = sketch.Geometry
# --- STEP 1: IDENTIFY MICRO-SEGMENTS TO INFER ARC DATA ---
graph = {}
pt_dict = {}
def get_pt_key(pt): return (round(pt.x, 3), round(pt.y, 3))
for i, g in enumerate(geo):
if isinstance(g, Part.LineSegment) and not sketch.GeometryFacadeList[i].Construction:
if (g.EndPoint - g.StartPoint).Length < MAX_SEGMENT_LENGTH:
k1 = get_pt_key(g.StartPoint)
k2 = get_pt_key(g.EndPoint)
if k1 not in graph: graph[k1] = []
if k2 not in graph: graph[k2] = []
graph[k1].append((k2, i))
graph[k2].append((k1, i))
pt_dict[k1] = g.StartPoint
pt_dict[k2] = g.EndPoint
paths = []
visited_edges = set()
for node, edges in graph.items():
if len(edges) == 1:
first_edge = edges[0][1]
if first_edge in visited_edges: continue
path_nodes = [node]
curr = node; prev = None
while True:
neighbors = graph[curr]
next_node = None; next_idx = None
for n, idx in neighbors:
if n != prev:
next_node = n; next_idx = idx
break
if next_node is None: break
path_nodes.append(next_node)
visited_edges.add(next_idx)
if len(graph[next_node]) > 2: break
prev = curr; curr = next_node
if len(path_nodes) >= 3:
paths.append(path_nodes)
# --- STEP 2: CALCULATE TRUE ARC CENTERS & DIRECTIONS ---
arc_data = []
for path_nodes in paths:
p1 = pt_dict[path_nodes[0]]
p2 = pt_dict[path_nodes[len(path_nodes)//2]]
p3 = pt_dict[path_nodes[-1]]
center, radius = get_circumcenter(p1, p2, p3)
if center:
v1 = p1 - center
v3 = p3 - center
if v1.Length > 1e-4 and v3.Length > 1e-4:
dot = max(-1.0, min(1.0, v1.dot(v3) / (v1.Length * v3.Length)))
angle = math.acos(dot)
if angle > 2.0:
chord = p3 - p1
perp = App.Vector(-chord.y, chord.x, 0)
if perp.Length > 1e-6:
perp.normalize()
if perp.dot(center - p2) < 0: perp = -perp
arc_data.append((center, perp))
# Existing native Arcs
for i, g in enumerate(geo):
if isinstance(g, Part.ArcOfCircle) and not sketch.GeometryFacadeList[i].Construction:
p1 = g.StartPoint
p3 = g.EndPoint
center = g.Center
v1 = p1 - center
v3 = p3 - center
if v1.Length > 1e-4 and v3.Length > 1e-4:
dot = max(-1.0, min(1.0, v1.dot(v3) / (v1.Length * v3.Length)))
angle = math.acos(dot)
if angle > 2.0:
chord = p3 - p1
perp = App.Vector(-chord.y, chord.x, 0)
if perp.Length > 1e-6:
perp.normalize()
mid_u = (g.FirstParameter + g.LastParameter) / 2.0
p2 = g.value(mid_u)
if perp.dot(center - p2) < 0: perp = -perp
arc_data.append((center, perp))
# --- STEP 3: WIPE THE SKETCH CLEAN ---
for i in range(sketch.ConstraintCount - 1, -1, -1):
sketch.delConstraint(i)
for i in range(sketch.GeometryCount - 1, -1, -1):
sketch.delGeometry(i)
# --- STEP 4: CALCULATE TOOLPATHS ---
raw_lines = []
for i, (c1, dir1) in enumerate(arc_data):
min_t = float('inf')
best_stop = None
for j, (c2, dir2) in enumerate(arc_data):
if i == j: continue
cross = dir1.x * dir2.y - dir1.y * dir2.x
if abs(cross) < 1e-4:
vec = c2 - c1
dist_cross = vec.x * dir1.y - vec.y * dir1.x
if abs(dist_cross) < 1e-2:
t1 = vec.dot(dir1)
t2 = (-vec).dot(dir2)
if t1 > 1e-2 and t2 > 1e-2:
if t1 < min_t:
min_t = t1
best_stop = c2
else:
dx = c2.x - c1.x
dy = c2.y - c1.y
t1 = (dx * dir2.y - dy * dir2.x) / cross
t2 = (dx * dir1.y - dy * dir1.x) / cross
if t1 > 1e-2 and t2 > -1e-2:
if t1 < min_t:
min_t = t1
best_stop = c1 + dir1 * t1
if best_stop is not None:
if (c1 - best_stop).Length > 1e-3:
pts = sorted([(round(c1.x, 3), round(c1.y, 3)), (round(best_stop.x, 3), round(best_stop.y, 3))])
pA = App.Vector(pts[0][0], pts[0][1], 0)
pB = App.Vector(pts[1][0], pts[1][1], 0)
if not any((l[0]-pA).Length < 1e-3 and (l[1]-pB).Length < 1e-3 for l in raw_lines):
raw_lines.append((pA, pB))
# --- STEP 5: MERGE AND OPTIMIZE PATHS FOR CNC ---
merged_lines = merge_collinear_lines(raw_lines)
optimized_lines = optimize_toolpath_order(merged_lines)
# Draw the final optimized lines sequentially
for pA, pB in optimized_lines:
sketch.addGeometry(Part.LineSegment(pA, pB), False)
App.ActiveDocument.recompute()
App.Console.PrintMessage(f"Skeletonizer successful: Created '{sketch.Label}'.\n")
show_message("CAM Toolpath Complete", f"Successfully generated continuous, optimized CNC toolpaths in:\n\n{sketch.Label}")
# Run it
process_sketch()

View File

@@ -1,6 +1,10 @@
#back=gerbers/<Project Name>-B_Cu.gbr front=gerbers/Flow_Controller_Panel-Front.gtl
#drill=gerbers/<Project Name>.drl back=gerbers/Flow_Controller_Panel-Back.gbl
#outline=gerbers/<Project Name>-Edge_Cuts.gm1 drill=gerbers/Flow_Controller_Panel.drl
# Use the 'User-Eco1' layer instead as it contains panelized boards outlines only.
outline=gerbers/Flow_Controller_Panel-User-Eco1.gbr
# Do not use the 'Edge_Cuts' layer as it contains also the panel outline.
#outline=gerbers/Flow_Controller_Panel-Edge_Cuts.gm1
# Generic # Generic
metric=true # use metric units for parameters metric=true # use metric units for parameters
@@ -15,14 +19,15 @@ output-dir=gcode
# This will allow you to probe the fixed jaw of the vise for (0,0) on the CNC. # This will allow you to probe the fixed jaw of the vise for (0,0) on the CNC.
# For two-sided boards, the PCB needs to be flipped along the axis x=VALUE # For two-sided boards, the PCB needs to be flipped along the axis x=VALUE
mirror-axis=50 # set this to half of your board width mirror-axis=80 # set this to half of your board width
# Drilling # Drilling
zdrill=-2.2 # drilling depth zdrill=-2.2 # drilling depth
drill-feed=500 # Vertical mm/min feed drill-feed=500 # Vertical mm/min feed
drill-speed=24000 # Spindle RPM drill-speed=24000 # Spindle RPM
onedrill=true # Use a single drill for all holes #onedrill=true # Use a single drill for all holes
nog81=true # replace G81 with G0+G1 (no G81 in GRBL) nog81=true # replace G81 with G0+G1 (no G81 in GRBL)
drill-side=back
# Milling # Milling
zwork=-0.1 # V-bit plunge depth zwork=-0.1 # V-bit plunge depth
@@ -45,6 +50,6 @@ cut-speed=24000
cut-side=back cut-side=back
# Tabs # Tabs
bridgesnum=4 # Total 4 tabs #bridgesnum=4 # Total 4 tabs
bridges=0.5 # Tab width 0.5 mm #bridges=0.5 # Tab width 0.5 mm
zbridges=0 # bridges height (default to zsafe) #zbridges=0 # bridges height (default to zsafe)

View File

@@ -1,33 +0,0 @@
# move_to_origin.py
from shapely.affinity import translate
def kikitPostprocess(panel, args):
import pcbnew
# Get bounds from the substrate (Shapely polygon, units in nm)
bounds = panel.boardSubstrate.substrates.bounds # (minx, miny, maxx, maxy)
minx = bounds[0]
miny = bounds[1]
dx = int(-minx)
dy = int(-miny)
offset = pcbnew.VECTOR2I(dx, dy)
# Move all board items (footprints, tracks, drawings, zones)
for fp in panel.board.GetFootprints():
fp.Move(offset)
for track in panel.board.GetTracks():
track.Move(offset)
for drawing in panel.board.GetDrawings():
drawing.Move(offset)
for zone in panel.board.Zones():
zone.Move(offset)
# Translate the substrate geometry (this becomes the Edge.Cuts on save)
dx_nm = -bounds[0]
dy_nm = -bounds[1]
panel.boardSubstrate.substrates = translate(
panel.boardSubstrate.substrates,
xoff=dx_nm,
yoff=dy_nm
)

View File

@@ -60,11 +60,11 @@
"reconstructarcs": false, "reconstructarcs": false,
"millradius": "1mm", "millradius": "1mm",
"millradiusouter": "0mm", "millradiusouter": "0mm",
"script": "", "script": "cleanup.py",
"scriptarg": "", "scriptarg": "",
"origin": "tl", "origin": "tl",
"refillzones": false, "refillzones": false,
"dimensions": false, "dimensions": true,
"edgewidth": "0.1mm" "edgewidth": "0.1mm"
}, },
"page": { "page": {
@@ -75,7 +75,7 @@
"width": "1000mm", "width": "1000mm",
"height": "1000mm" "height": "1000mm"
}, },
"debug": { "debug": {
"type": "none", "type": "none",
"drawPartitionLines": false, "drawPartitionLines": false,
"drawBackboneLines": false, "drawBackboneLines": false,

View File

@@ -1,124 +0,0 @@
%TF.GenerationSoftware,KiCad,Pcbnew,9.0.9-9.0.9~ubuntu24.04.1*%
%TF.CreationDate,2026-05-16T13:37:08+02:00*%
%TF.ProjectId,Flow_Controller,466c6f77-5f43-46f6-9e74-726f6c6c6572,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Copper,L2,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.9-9.0.9~ubuntu24.04.1) date 2026-05-16 13:37:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%TA.AperFunction,ComponentPad*%
%ADD10RoundRect,0.250000X0.725000X-0.600000X0.725000X0.600000X-0.725000X0.600000X-0.725000X-0.600000X0*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD11O,1.950000X1.700000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD12R,1.700000X1.700000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD13C,1.700000*%
%TD*%
%TA.AperFunction,ViaPad*%
%ADD14C,1.700000*%
%TD*%
%TA.AperFunction,Conductor*%
%ADD15C,0.400000*%
%TD*%
G04 APERTURE END LIST*
D10*
%TO.P,J4,1,Pin_1*%
%TO.N,+5V*%
X46000000Y-17526000D03*
D11*
%TO.P,J4,2,Pin_2*%
%TO.N,GND*%
X46000000Y-15026000D03*
%TO.P,J4,3,Pin_3*%
%TO.N,/ALARM _CNC*%
X46000000Y-12526000D03*
%TD*%
D12*
%TO.P,J1,1,Pin_1*%
%TO.N,GND*%
X18415000Y-45000000D03*
D13*
%TO.P,J1,2,Pin_2*%
%TO.N,+5V*%
X18415000Y-42460000D03*
%TO.P,J1,3,Pin_3*%
%TO.N,/SWIO*%
X18415000Y-39920000D03*
%TD*%
D12*
%TO.P,J3,1,BUTTON_UP*%
%TO.N,Net-(J3-BUTTON_UP)*%
X7366000Y-33900000D03*
D13*
%TO.P,J3,2,BUTTON_DOWN*%
%TO.N,/BUTTON_ADC*%
X7366000Y-31360000D03*
%TO.P,J3,3,BUTTON_#*%
%TO.N,unconnected-(J3-BUTTON_#-Pad3)*%
X7366000Y-28820000D03*
%TO.P,J3,4,BUTTON_\u002A*%
%TO.N,unconnected-(J3-BUTTON_\u002A-Pad4)*%
X7366000Y-26280000D03*
%TO.P,J3,5,I2C_SDA*%
%TO.N,/I2C_SDA*%
X7366000Y-23740000D03*
%TO.P,J3,6,I2C_SCL*%
%TO.N,/I2C_SCL*%
X7366000Y-21200000D03*
%TO.P,J3,7,VCC*%
%TO.N,+5V*%
X7366000Y-18660000D03*
%TO.P,J3,8,GND*%
%TO.N,GND*%
X7366000Y-16120000D03*
%TD*%
D10*
%TO.P,J2,1,Pin_1*%
%TO.N,+5V*%
X30000000Y-44918000D03*
D11*
%TO.P,J2,2,Pin_2*%
%TO.N,GND*%
X30000000Y-42418000D03*
%TO.P,J2,3,Pin_3*%
%TO.N,/RAW_SENSOR*%
X30000000Y-39918000D03*
%TD*%
D14*
%TO.N,/I2C_SCL*%
X14605000Y-21590000D03*
X14732000Y-25400000D03*
%TD*%
D15*
%TO.N,/I2C_SCL*%
X14605000Y-21082000D02*
X14605000Y-25019000D01*
%TD*%
M02*

View File

@@ -1,23 +0,0 @@
%TF.GenerationSoftware,KiCad,Pcbnew,9.0.9-9.0.9~ubuntu24.04.1*%
%TF.CreationDate,2026-05-16T13:37:09+02:00*%
%TF.ProjectId,Flow_Controller,466c6f77-5f43-46f6-9e74-726f6c6c6572,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.9-9.0.9~ubuntu24.04.1) date 2026-05-16 13:37:09*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%TA.AperFunction,Profile*%
%ADD10C,0.050000*%
%TD*%
G04 APERTURE END LIST*
D10*
X0Y0D02*
X50000000Y0D01*
X50000000Y-50000000D01*
X0Y-50000000D01*
X0Y0D01*
M02*

View File

@@ -1,87 +0,0 @@
{
"Header": {
"GenerationSoftware": {
"Vendor": "KiCad",
"Application": "Pcbnew",
"Version": "9.0.9-9.0.9~ubuntu24.04.1"
},
"CreationDate": "2026-05-16T13:37:09+02:00"
},
"GeneralSpecs": {
"ProjectId": {
"Name": "Flow_Controller",
"GUID": "466c6f77-5f43-46f6-9e74-726f6c6c6572",
"Revision": "rev?"
},
"Size": {
"X": 50.05,
"Y": 50.05
},
"LayerNumber": 2,
"BoardThickness": 1.6002,
"Finish": "None"
},
"DesignRules": [
{
"Layers": "Outer",
"PadToPad": 0.127,
"PadToTrack": 0.127,
"TrackToTrack": 0.127,
"MinLineWidth": 0.4,
"TrackToRegion": 0.5,
"RegionToRegion": 0.5
}
],
"FilesAttributes": [
{
"Path": "Flow_Controller-Edge_Cuts.gm1",
"FileFunction": "Profile",
"FilePolarity": "Positive"
}
],
"MaterialStackup": [
{
"Type": "Legend",
"Name": "Top Silk Screen"
},
{
"Type": "SolderPaste",
"Name": "Top Solder Paste"
},
{
"Type": "SolderMask",
"Thickness": 0.01,
"Name": "Top Solder Mask"
},
{
"Type": "Copper",
"Thickness": 0.035,
"Name": "Front"
},
{
"Type": "Dielectric",
"Thickness": 1.5102,
"Material": "FR4",
"Name": "Front/Back",
"Notes": "Type: dielectric layer 1 (from Front to Back)"
},
{
"Type": "Copper",
"Thickness": 0.035,
"Name": "Back"
},
{
"Type": "SolderMask",
"Thickness": 0.01,
"Name": "Bottom Solder Mask"
},
{
"Type": "SolderPaste",
"Name": "Bottom Solder Paste"
},
{
"Type": "Legend",
"Name": "Bottom Silk Screen"
}
]
}

View File

@@ -1,47 +0,0 @@
M48
; DRILL file {KiCad 9.0.9-9.0.9~ubuntu24.04.1} date 2026-05-16T13:37:09+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2026-05-16T13:37:09+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,9.0.9-9.0.9~ubuntu24.04.1
; #@! TF.FileFunction,MixedPlating,1,2
FMAT,2
METRIC
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T1C0.950
; #@! TA.AperFunction,Plated,PTH,ViaDrill
T2C1.000
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T3C1.000
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T4C3.200
%
G90
G05
T1
X30.0Y-39.918
X30.0Y-42.418
X30.0Y-44.918
X46.0Y-12.526
X46.0Y-15.026
X46.0Y-17.526
T2
X14.605Y-21.59
X14.732Y-25.4
T3
X7.366Y-16.12
X7.366Y-18.66
X7.366Y-21.2
X7.366Y-23.74
X7.366Y-26.28
X7.366Y-28.82
X7.366Y-31.36
X7.366Y-33.9
X18.415Y-39.92
X18.415Y-42.46
X18.415Y-45.0
T4
X4.6Y-4.6
X4.6Y-45.4
X45.4Y-4.6
X45.4Y-45.4
M30

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,87 +0,0 @@
{
"Header": {
"GenerationSoftware": {
"Vendor": "KiCad",
"Application": "Pcbnew",
"Version": "9.0.9-9.0.9~ubuntu24.04.1"
},
"CreationDate": "2026-05-20T19:29:47+02:00"
},
"GeneralSpecs": {
"ProjectId": {
"Name": "Flow_Controller_Panel",
"GUID": "466c6f77-5f43-46f6-9e74-726f6c6c6572",
"Revision": "rev?"
},
"Size": {
"X": 106.3996,
"Y": 54.2993
},
"LayerNumber": 2,
"BoardThickness": 1.6002,
"Finish": "None"
},
"DesignRules": [
{
"Layers": "Outer",
"PadToPad": 0.127,
"PadToTrack": 0.127,
"TrackToTrack": 0.127,
"MinLineWidth": 0.4,
"TrackToRegion": 0.5,
"RegionToRegion": 0.5
}
],
"FilesAttributes": [
{
"Path": "Flow_Controller_Panel-Edge_Cuts.gm1",
"FileFunction": "Profile",
"FilePolarity": "Positive"
}
],
"MaterialStackup": [
{
"Type": "Legend",
"Name": "Top Silk Screen"
},
{
"Type": "SolderPaste",
"Name": "Top Solder Paste"
},
{
"Type": "SolderMask",
"Thickness": 0.01,
"Name": "Top Solder Mask"
},
{
"Type": "Copper",
"Thickness": 0.035,
"Name": "Front"
},
{
"Type": "Dielectric",
"Thickness": 1.5102,
"Material": "FR4",
"Name": "Front/Back",
"Notes": "Type: dielectric layer 1 (from Front to Back)"
},
{
"Type": "Copper",
"Thickness": 0.035,
"Name": "Back"
},
{
"Type": "SolderMask",
"Thickness": 0.01,
"Name": "Bottom Solder Mask"
},
{
"Type": "SolderPaste",
"Name": "Bottom Solder Paste"
},
{
"Type": "Legend",
"Name": "Bottom Silk Screen"
}
]
}

View File

@@ -1,147 +0,0 @@
M48
; DRILL file {KiCad 9.0.9-9.0.9~ubuntu24.04.1} date 2026-05-20T19:29:47+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2026-05-20T19:29:47+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,9.0.9-9.0.9~ubuntu24.04.1
; #@! TF.FileFunction,MixedPlating,1,2
FMAT,2
METRIC
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T1C0.950
; #@! TA.AperFunction,Plated,PTH,ViaDrill
T2C1.000
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T3C1.000
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T4C0.500
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T5C2.150
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T6C2.700
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T7C3.172
%
G90
G05
T1
X93.5Y-53.0
X93.5Y-55.5
X93.5Y-58.0
X93.55Y-73.85
X93.55Y-76.35
X93.55Y-78.85
X145.6Y-53.0
X145.6Y-55.5
X145.6Y-58.0
X145.65Y-73.85
X145.65Y-76.35
X145.65Y-78.85
T2
X66.7Y-65.675
X66.8Y-69.425
X118.8Y-65.675
X118.9Y-69.425
T3
X56.316Y-56.12
X56.316Y-58.66
X56.316Y-61.2
X56.316Y-63.74
X56.316Y-66.28
X56.316Y-68.82
X56.316Y-71.36
X56.316Y-73.9
X85.87Y-45.55
X88.41Y-45.55
X90.95Y-45.55
X108.416Y-56.12
X108.416Y-58.66
X108.416Y-61.2
X108.416Y-63.74
X108.416Y-66.28
X108.416Y-68.82
X108.416Y-71.36
X108.416Y-73.9
X137.97Y-45.55
X140.51Y-45.55
X143.05Y-45.55
T4
X49.15Y-62.0
X49.15Y-63.0
X49.15Y-64.0
X49.15Y-65.0
X49.15Y-66.0
X49.15Y-67.0
X49.15Y-68.0
X70.95Y-40.2
X70.95Y-89.8
X71.95Y-40.2
X71.95Y-89.8
X72.95Y-40.2
X72.95Y-89.8
X73.95Y-40.2
X73.95Y-89.8
X74.95Y-40.2
X74.95Y-89.8
X75.95Y-40.2
X75.95Y-89.8
X76.95Y-40.2
X76.95Y-89.8
X98.75Y-62.0
X98.75Y-63.0
X98.75Y-64.0
X98.75Y-65.0
X98.75Y-66.0
X98.75Y-67.0
X98.75Y-68.0
X101.25Y-62.0
X101.25Y-63.0
X101.25Y-64.0
X101.25Y-65.0
X101.25Y-66.0
X101.25Y-67.0
X101.25Y-68.0
X123.05Y-40.2
X123.05Y-89.8
X124.05Y-40.2
X124.05Y-89.8
X125.05Y-40.2
X125.05Y-89.8
X126.05Y-40.2
X126.05Y-89.8
X127.05Y-40.2
X127.05Y-89.8
X128.05Y-40.2
X128.05Y-89.8
X129.05Y-40.2
X129.05Y-89.8
X150.85Y-62.0
X150.85Y-63.0
X150.85Y-64.0
X150.85Y-65.0
X150.85Y-66.0
X150.85Y-67.0
X150.85Y-68.0
T5
X56.748Y-45.179
X56.824Y-84.847
X79.633Y-45.147
X79.684Y-84.822
X108.848Y-45.179
X108.924Y-84.847
X131.733Y-45.147
X131.784Y-84.822
T6
X51.95Y-43.0
X51.95Y-87.0
X95.95Y-43.0
X95.95Y-87.0
X104.05Y-43.0
X104.05Y-87.0
X148.05Y-43.0
X148.05Y-87.0
T7
X11.65Y-65.0
X100.0Y-10.75
X100.0Y-119.25
X188.35Y-65.0
M30

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -1,2 +0,0 @@
(version 1)

File diff suppressed because it is too large Load Diff

View File

@@ -1,131 +0,0 @@
{
"board": {
"active_layer": 0,
"active_layer_preset": "",
"auto_track_width": true,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"shapes": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
"vias",
"footprint_text",
"footprint_anchors",
"ratsnest",
"grid",
"footprints_front",
"footprints_back",
"footprint_values",
"footprint_references",
"tracks",
"drc_errors",
"bitmaps",
"pads",
"zones",
"drc_warnings",
"drc_exclusions",
"locked_item_shadows",
"conflict_shadows",
"shapes"
],
"visible_layers": "00000000_00000000_0fffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"integration_disabled": false,
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "Flow_Controller_Panel.kicad_prl",
"version": 5
},
"net_inspector_panel": {
"col_hidden": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
],
"col_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
],
"col_widths": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"custom_group_rules": [],
"expanded_rows": [],
"filter_by_net_name": true,
"filter_by_netclass": true,
"filter_text": "",
"group_by_constraint": false,
"group_by_netclass": false,
"show_unconnected_nets": false,
"show_zero_pad_nets": false,
"sort_ascending": true,
"sorting_column": 0
},
"open_jobsets": [],
"project": {
"files": []
},
"schematic": {
"selection_filter": {
"graphics": true,
"images": true,
"labels": true,
"lockedItems": false,
"otherItems": true,
"pins": true,
"symbols": true,
"text": true,
"wires": true
}
}
}

View File

@@ -1,466 +0,0 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.0381,
"copper_line_width": 0.254,
"copper_text_italic": false,
"copper_text_size_h": 1.524,
"copper_text_size_v": 1.524,
"copper_text_thickness": 0.3048,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": true,
"text_position": 0,
"units_format": 0
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.0,
"height": 0.4,
"width": 1.325
},
"silk_line_width": 0.1524,
"silk_text_italic": false,
"silk_text_size_h": 0.8128,
"silk_text_size_v": 0.8128,
"silk_text_thickness": 0.1524,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_to_hole": "warning",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_on_edge_cuts": "error",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.25,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_groove_width": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.0,
"min_microvia_drill": 0.0,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.127,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.6,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.15,
0.2,
0.4,
0.6
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.6,
"drill": 0.3
},
{
"diameter": 0.9,
"drill": 0.4
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "Flow_Controller_Panel.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.127,
"diff_pair_gap": 0.254,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.1524,
"line_style": 0,
"microvia_diameter": 0.6858,
"microvia_drill": 0.3302,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.127,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.127,
"diff_pair_gap": 0.254,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.1524,
"line_style": 0,
"microvia_diameter": 0.6858,
"microvia_drill": 0.3302,
"name": "Board_0-Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.127,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.127,
"diff_pair_gap": 0.254,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.1524,
"line_style": 0,
"microvia_diameter": 0.6858,
"microvia_drill": 0.3302,
"name": "Board_1-Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.127,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 4
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "Board_0-Default",
"pattern": "Board_0-GND"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/RAW_SENSOR"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-Net-(Q1-G)"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/ALARM_TRIGGER"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/SWIO"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-Net-(J3-UP)"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-unconnected-(J3-*-Pad5)"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/I2C_SDA"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-unconnected-(J3-#-Pad6)"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-+3.3V"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-+5V"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/ALARM _CNC"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/BUTTON_ADC"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/I2C_SCL"
},
{
"netclass": "Board_0-Default",
"pattern": "Board_0-/FLOW_PULSE_MCU"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/I2C_SCL"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/RAW_SENSOR"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/SWIO"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-Net-(Q1-G)"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-GND"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-unconnected-(J3-#-Pad6)"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-+5V"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/I2C_SDA"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-unconnected-(J3-*-Pad5)"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/ALARM_TRIGGER"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/BUTTON_ADC"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-+3.3V"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-Net-(J3-UP)"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/ALARM _CNC"
},
{
"netclass": "Board_1-Default",
"pattern": "Board_1-/FLOW_PULSE_MCU"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [],
"text_variables": {}
}

View File

@@ -128,3 +128,4 @@ class CustomTooling(ToolingPlugin):
fixture.SetWidth(pcbnew.FromMM(0.2)) fixture.SetWidth(pcbnew.FromMM(0.2))
board.Add(fixture) board.Add(fixture)