Integrate Measured ESP32 PCB Geometry

- Add measured F.Fab bounds and USB mouth markers
- Add guarded preservation and regression validation
- Refresh review exports and remove draft presentation labels
This commit is contained in:
2026-09-21 22:54:37 +02:00
parent ab6a8010ba
commit 901afbe754
25 changed files with 20490 additions and 850 deletions
+21 -19
View File
@@ -10,6 +10,7 @@ import uuid
import pcbnew as p
from shapely.geometry import LineString, Point, box
from shapely.ops import unary_union, polygonize
from update_u1_graphics import verify
ROOT = Path(__file__).resolve().parent
PCB = ROOT / "serial-carrier.kicad_pcb"
@@ -38,6 +39,7 @@ def outline_lines(shape):
def add_outlines(board):
verify(board)
owned = {str(uuid.uuid5(NS, str(i))) for i in range(4096)}
for drawing in list(board.GetDrawings()):
if drawing.m_Uuid.AsString() in owned:
@@ -82,8 +84,11 @@ def add_outlines(board):
board.Add(shape)
count+=1
def line(a,b):
clipped=LineString([a,b]).intersection(silk_area).difference(blocked)
def line(a,b,width=WIDTH):
extra = max(0, (width-WIDTH)/2)
area = silk_area.buffer(-extra) if extra else silk_area
obstacles = blocked.buffer(extra) if extra else blocked
clipped=LineString([a,b]).intersection(area).difference(obstacles)
pieces=list(clipped.geoms) if hasattr(clipped,"geoms") else [clipped]
for piece in pieces:
if piece.geom_type!="LineString" or piece.length < 0.25:
@@ -91,7 +96,7 @@ def add_outlines(board):
shape=p.PCB_SHAPE(board)
shape.SetShape(p.SHAPE_T_SEGMENT)
shape.SetStart(vec(piece.coords[0])); shape.SetEnd(vec(piece.coords[-1]))
shape.SetWidth(p.FromMM(WIDTH))
shape.SetWidth(p.FromMM(width))
add(shape)
for ref in ("DS1","U2"):
@@ -137,22 +142,19 @@ def add_outlines(board):
distance+=1.3
else:
line(edge.coords[0],edge.coords[-1])
# These are pad-row guides, not inferred socket/body or ESP32 PCB dimensions.
fp=footprints["U1"]
assert fp.GetOrientationDegrees()==0, "Review guide construction for rotated U1"
pads={pad.GetNumber():xy(pad.GetPosition()) for pad in fp.Pads()}
for first,last in (("1","22"),("23","44")):
x,ytop=pads[first]; _,ybottom=pads[last]
corners=[(x-1.2,ytop-1.2),(x+1.2,ytop-1.2),(x+1.2,ybottom+1.2),(x-1.2,ybottom+1.2)]
for a,b in zip(corners,corners[1:]+corners[:1]):
line(a,b)
label=p.PCB_TEXT(board)
label.SetText("HEADER GUIDES")
a,b=pads["1"],pads["23"]
label.SetPosition(vec(((a[0]+b[0])/2,a[1]+28)))
label.SetTextSize(p.VECTOR2I(p.FromMM(0.85),p.FromMM(0.85)))
label.SetTextThickness(p.FromMM(0.12))
add(label)
# Union removes the shared body/antenna edge duplication. Mouth spans replace
# the coincident bottom-edge strokes, with their measured library line weight;
# this marks width only, without inventing connector depth or an RF keepout.
sources=[g for g in footprints["U1"].GraphicalItems()
if isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab]
mouths=[g for g in sources if g.GetShape()==p.SHAPE_T_SEGMENT]
mouth_lines=unary_union([edge for g in mouths for edge in outline_lines(g)])
perimeter=unary_union([edge for g in sources if g.GetShape()==p.SHAPE_T_RECT
for edge in outline_lines(g)]).difference(mouth_lines)
for edge in perimeter.geoms:
line(edge.coords[0],edge.coords[-1])
for mouth in mouths:
line(xy(mouth.GetStart()),xy(mouth.GetEnd()),p.ToMM(mouth.GetWidth()))
return count