8.8 KiB
Ground Fill Clearance
Let a copper ground fill keep an isolation gap around traces and pads on its own layer, so a board can be poured with ground without every trace shorting to it. The fill sets the gap; a trace or pad can override it, including overriding it to zero so that it connects to the fill.
1. Behaviour
Fill clearance. GroundFill gains a Clearance property (Size). The fill draws its
polygon minus, for every clearance source on the same layer, that source's copper grown by the
effective clearance. A clearance of zero (or null, which is what old files deserialize to)
cuts nothing, so existing projects render and net exactly as before.
Override. Every clearance source gains a Fill Clearance property (Size):
| Value | Effect |
|---|---|
blank (null, the default) |
Use the clearance of whichever fill it overlaps |
0 |
No gap: the source overlaps the fill and is electrically connected to it |
| any positive size | Use this gap instead of the fill's |
A ground trace is therefore "a trace with Fill Clearance 0". The override is a property of the source, not of a source/fill pair; a trace overlapping two fills with different clearances takes each fill's own value unless overridden.
Layers. A fill is cut only by sources whose copper is on the fill's layer
(ILayeredComponent.getLayerId() / PCBLayer.getId()). A bottom-layer trace passing under a
top-layer fill does not cut it. Sources that put copper on several layers report their shapes
per layer (see §2).
Z-order does not matter. Every source on the layer cuts the fill regardless of whether it is drawn before or after it. "Send to back" never changes the copper.
What does not cut a fill. Other fills (two overlapping fills on one layer still merge, as
today), boards (VeroBoard, PerfBoard, TriPadBoard), TraceCut, and anything not on a
copper layer.
Islands. Clearance can split a fill into disjoint pieces. Each piece is a separate copper area for continuity and netlist purposes. Islands are kept, not removed; an orphaned island is visible and the user can fix it by moving the fill's points.
2. Architecture
diylc-core IFillClearanceSource (interface: copper shapes per layer + override)
^ implemented by
diylc-library CopperTrace, CurvedTrace, SolderPad, PCBText (phase 1)
footprint components with copper pads (phase 2)
^ queried by
GroundFill.draw() -> FillClearanceCalculator -> cut Area, split into islands
|
+- netlist: one positive continuity area per island
+- canvas: fill() per island
+- Gerber: same fill() calls through GerberG2DWrapper, dark polarity
The fill does all the cutting. Continuity analysis (DrawingManager.getContinuityAreas),
the drawing pipeline and GerberExporter stay unchanged, because the fill already draws its
final copper. Negative continuity areas are deliberately not used: a negative area subtracts
from every earlier positive area on the layer, so a trace's halo would sever traces and pads
it legitimately connects to.
diylc-core
org.diylc.core.IFillClearanceSource, next to ILayeredComponent, Javadoc with
@author, in the shape of IGerberComponentSimple:
public interface IFillClearanceSource {
/** Copper this component places on the given PCB layer, in canvas coordinates; empty if none. */
List<Shape> getCopperShapes(int layerId);
/** Gap to keep from a ground fill; null inherits the fill's clearance, zero connects. */
Size getFillClearance();
}
Per-layer shapes rather than a single shape plus getLayerId(), so that phase-2 footprint
components with pads on both sides fit the same interface.
diylc-library
GroundFill
- New field
private Size clearance;with getter@EditableProperty(name = "Clearance"), null-tolerant (treatnullas zero). New fills default toCLEARANCE, apublic staticnon-final constant (see §4, open question 1). draw()usesprojectto collect theIFillClearanceSourcecomponents, gets the cut area fromFillClearanceCalculator, splits it withAreaUtils.tryAreaBreakoutandfill()s each island inside the existingstartTrackingContinuityArea(true)block. Eachfill()with no continuity marker becomes its own continuity area inG2DWrapper.- Memoization. The cut is expensive (
Areaoperations over every source) anddraw()runs on every repaint. Keep atransientcachedAreaplus a signature of its inputs: the fill's points, layer, clearance, and for each source on the layer its shapes' bounds and effective clearance. Recompute only when the signature changes.enableCachestaysfalse, because the fill now depends on other components andDrawingCachekeys only on the component itself.
FillClearanceCalculator (new, org.diylc.components.connectivity): pure geometry,
no Swing, unit-testable.
- Input: fill polygon, fill layer id, fill clearance, the sources.
- For each source: skip it if its effective clearance is zero or it has no shapes on the layer. Otherwise grow each shape by the clearance and union the result into a cut area.
- Return the fill area minus the cut area.
- Growing a shape:
shape ∪ roundStroke(2 × clearance).createStrokedShape(shape).CopperTraceandCurvedTracecan skip the union. Instead they report a centerline, and the calculator strokes it atthickness + 2 × clearance, which is exact and cheaper. Decide during implementation whether that special case is worth a second interface method. Otherwise use the generic path.
Phase-1 sources. Each one gets a nullable Size fillClearance field, a getter annotated
@EditableProperty(name = "Fill Clearance") that may return null, and getCopperShapes.
draw() and getCopperShapes must build their copper from the same private helper, so
the gap always matches the drawn copper.
| Component | Copper shape |
|---|---|
CopperTrace |
Stroked line, current thickness and LineEndingStyle cap |
CurvedTrace |
Stroked curve, same stroke as draw() |
SolderPad |
Pad outline per Type (round, oval H/V, square); the drill hole is ignored |
PCBText |
Glyph outlines (confirm it is copper and not silkscreen before including) |
PCBTerminalBlock tracks no continuity and is not layered today, so it waits for phase 2.
3. File format
- New fields only, nothing renamed.
GroundFill.clearance == nullbehaves as zero, andfillClearance == nullmeans "inherit". Both are safe for every existing file. - With zero clearance an old file must produce identical canvas, netlist and Gerber output. Run the regression suite after implementation to confirm this. Expect zero diffs.
4. Open questions
- Default clearance on new fills. Zero means a new fill behaves like today and clearance
is opt-in. A non-zero default (e.g. 0.5 mm) is more useful, but a fill dropped onto an
existing layout silently isolates every trace not set to
0. Leaning towards zero. - Phase-2 scope. About 40 library components output copper pads to Gerber (
DIL_IC,SIL_IC,Resistor,AbstractRadialComponent,AbstractTransistorPackage,AbstractPotentiometer, jacks, switches, …). Until they implement the interface, their pads short to a fill they overlap. Implementing it in the abstract bases covers most of them. - Thermal relief (spoked joins between a zero-clearance pad and the fill) is out of
scope. If added later, it belongs in
FillClearanceCalculatoras a pattern of spokes added back after the cut.
5. Next steps
- Add
IFillClearanceSourcetodiylc-core. - Add
FillClearanceCalculatorwith unit tests indiylc-library/src/test. Cover: layer filtering, inheritance vs. override vs. zero, a trace that crosses the fill edge, a cut that splits the fill into islands, and an empty source list returning the original polygon. GroundFill: theClearanceproperty, the cut and islands indraw(), and memoization.- Implement the interface on
CopperTrace,CurvedTraceandSolderPad, refactoring theirdraw()to share the shape helper. ThenPCBTextif question in §2 resolves to copper. - Check by hand: highlighting continuity on a fill with gaps, a netlist with a zero-clearance ground trace, dragging a trace across a large fill (check that repaints stay responsive), and a two-layer board with fills on both sides.
- Check that Gerber export of a fill with holes produces correct regions with clear polarity
inside them (
GerberPathRenderer.assignNegativeFlags). Open the output in a Gerber viewer. - Build and test:
mvn -q -pl diylc-library -am test,mvn -q -pl diylc-swing -am compile. Then run the regression suite. - Phase 2: footprint pads through the abstract component bases.