Skip to content

Commit

Permalink
fix: auto detect rectangular pads #28
Browse files Browse the repository at this point in the history
EasyEDA sometimes uses polygon pads, even through the shape is a reactangle.
Unfortunately, KiCad does not handle polygon pads well in ground zones.
Thus, as a workaround, we automatically detect polygon pads that form a rectangle, and convert them to a rectangular pad instead.
  • Loading branch information
urish committed Mar 20, 2020
1 parent 08a953b commit c50b2a7
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 58 deletions.
91 changes: 36 additions & 55 deletions src/board.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,9 @@ describe('convertLib()', () => {
]);
});

it('should correctly convert polygon-shaped pads (issue #28)', () => {
it('should correctly convert non-rectangular polygon-shaped pads (issue #28)', () => {
const input =
'LIB~612.25~388.7~package`0603`value`1.00k~~~rep30~1~c25f29e5d54148509f1fe8ecc29bd248~1549637911~0~#@$PAD~POLYGON~613.999~396.939~3.9399~3.14~1~GND~1~0~612.03 398.51 612.03 395.37 615.97 395.37 615.97 398.51~90~rep28~0~~Y~0~0~0.4~613.999,396.939#@$PAD~POLYGON~613.999~389.459~3.94~3.15~1~B-IN~2~0~612.03 391.03 612.03 387.88 615.97 387.88 615.97 391.03~90~rep29~0~~Y~0~0~0.4~613.999,389.459';
'LIB~612.25~388.7~package`0603`value`1.00k~~~rep30~1~c25f29e5d54148509f1fe8ecc29bd248~1549637911~0~#@$PAD~POLYGON~613.999~396.939~3.9399~3.14~1~GND~1~0~612.03 398.51 612.03 395.37 615.97 398.51~90~rep28~0~~Y~0~0~0.4~613.999,396.939';
const nets = ['', 'GND', 'B-IN'];
expect(normalize(convertShape(input, nets)[0])).toEqual([
'module',
Expand All @@ -420,53 +420,62 @@ describe('convertLib()', () => {
'primitives',
[
'gr_poly',
[
'pts',
['xy', -0.399, -0.5],
['xy', 0.399, -0.5],
['xy', 0.399, 0.501],
['xy', -0.399, 0.501]
],
['pts', ['xy', -0.399, -0.5], ['xy', 0.399, -0.5], ['xy', -0.399, 0.501]],
['width', 0.1]
]
]
],
[
'fp_text',
'user',
'rep30',
['at', 0, 0],
['layer', 'Cmts.User'],
['effects', ['font', ['size', 1, 1], ['thickness', 0.15]]]
]
]);
});

it('should enforce minimal width and height for polygon pads', () => {
const input =
'LIB~585.7~338.9~package`0603`value`1.00k~90~~gge35720~1~c25f29e5d54148509f1fe8ecc29bd248~1549637911~0~#@$PAD~POLYGON~593.939~338.901~0~0~1~SYNC-OUT~1~0~595.51 340.87 592.37 340.87 595.51 336.93~180~gge35721~0~~Y~0~0~0.4~593.939,338.901';
const nets = ['', 'GND', 'B-IN'];
expect(normalize(convertShape(input, nets)[0])).toEqual([
'module',
'easyeda:0603',
['layer', 'F.Cu'],
['at', -867.232, -675.919, 90],
['attr', 'smd'],
[
'pad',
2,
1,
'smd',
'custom',
['at', 0.444, 0.193, 90],
['size', 1.001, 0.8],
['at', 0, 2.093, 180],
['size', 0.01, 0.01],
['layers', 'F.Cu', 'F.Paste', 'F.Mask'],
['net', 2, 'B-IN'],
['net', 3, 'SYNC-OUT'],
[
'primitives',
[
'gr_poly',
[
'pts',
['xy', -0.399, -0.5],
['xy', 0.401, -0.5],
['xy', 0.401, 0.501],
['xy', -0.399, 0.501]
],
['pts', ['xy', -0.399, -0.5], ['xy', 0.399, -0.5], ['xy', -0.399, 0.501]],
['width', 0.1]
]
]
],
[
'fp_text',
'user',
'rep30',
'gge35720',
['at', 0, 0],
['layer', 'Cmts.User'],
['effects', ['font', ['size', 1, 1], ['thickness', 0.15]]]
]
]);
});

it('should enforce minimal width and height for polygon pads', () => {
it('should automatically detect rectangular pads that are defined as polygons (issue #28)', () => {
const input =
'LIB~585.7~338.9~package`0603`value`1.00k~90~~gge35720~1~c25f29e5d54148509f1fe8ecc29bd248~1549637911~0~#@$PAD~POLYGON~593.939~338.901~0~0~1~SYNC-OUT~1~0~595.51 340.87 592.37 340.87 592.37 336.93 595.51 336.93~180~gge35721~0~~Y~0~0~0.4~593.939,338.901#@$PAD~POLYGON~586.459~338.901~3.15~3.94~1~SYNC-OUT~2~0~588.03 340.87 584.88 340.87 584.88 336.93 588.03 336.93~180~gge35727~0~~Y~0~0~0.4~586.459,338.901';
const nets = ['', 'GND', 'B-IN'];
Expand All @@ -480,49 +489,21 @@ describe('convertLib()', () => {
'pad',
1,
'smd',
'custom',
'rect',
['at', 0, 2.093, 180],
['size', 0.01, 0.01],
['size', 0.798, 1.001],
['layers', 'F.Cu', 'F.Paste', 'F.Mask'],
['net', 3, 'SYNC-OUT'],
[
'primitives',
[
'gr_poly',
[
'pts',
['xy', -0.399, -0.5],
['xy', 0.399, -0.5],
['xy', 0.399, 0.501],
['xy', -0.399, 0.501]
],
['width', 0.1]
]
]
['net', 3, 'SYNC-OUT']
],
[
'pad',
2,
'smd',
'custom',
'rect',
['at', 0, 0.193, 180],
['size', 0.8, 1.001],
['layers', 'F.Cu', 'F.Paste', 'F.Mask'],
['net', 3, 'SYNC-OUT'],
[
'primitives',
[
'gr_poly',
[
'pts',
['xy', -0.399, -0.5],
['xy', 0.401, -0.5],
['xy', 0.401, 0.501],
['xy', -0.399, 0.501]
],
['width', 0.1]
]
]
['net', 3, 'SYNC-OUT']
],
[
'fp_text',
Expand Down
33 changes: 30 additions & 3 deletions src/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,27 @@ function getDrill(holeRadius: number, holeLength: number) {
return null;
}

function isRectangle(points: number[]) {
if (points.length !== 8) {
return false;
}

const eq = (a: number, b: number) => Math.abs(a - b) < 0.01;

const [x1, y1, x2, y2, x3, y3, x4, y4] = points;
return (
(eq(x1, x2) && eq(y2, y3) && eq(x3, x4) && eq(y4, y1)) ||
(eq(y1, y2) && eq(x2, x3) && eq(y3, y4) && eq(x4, x1))
);
}

function rectangleSize(points: number[], rotation: number) {
const [x1, y1, x2, y2, x3, y3, x4, y4] = points;
const width = Math.max(x1, x2, x3, x4) - Math.min(x1, x2, x3, x4);
const height = Math.max(y1, y2, y3, y4) - Math.min(y1, y2, y3, y4);
return Math.round(Math.abs(rotation)) % 180 === 90 ? [height, width] : [width, height];
}

function convertPad(args: string[], nets: string[], transform: IParentTransform) {
const [
shape,
Expand Down Expand Up @@ -278,7 +299,10 @@ function convertPad(args: string[], nets: string[], transform: IParentTransform)
y: centerCoords.y,
angle: parseFloat(rotation)
};
const isCustomShape = shapes[shape] === 'custom';
const pointList = points.split(' ').map(parseFloat);
const pointsAreRectangle = shapes[shape] === 'custom' && isRectangle(pointList);
const actualShape = pointsAreRectangle ? 'RECT' : shape;
const isCustomShape = shapes[actualShape] === 'custom';
if (isCustomShape && !points.length) {
console.warn(`PAD ${id} is a polygon, but has no points defined`);
return null;
Expand All @@ -290,14 +314,17 @@ function convertPad(args: string[], nets: string[], transform: IParentTransform)
2: ['B.Cu', 'B.Paste', 'B.Mask'],
11: ['*.Cu', '*.Paste', '*.Mask']
};
const [actualWidth, actualHeight] = pointsAreRectangle
? rectangleSize(pointList, parseFloat(rotation))
: [width, height];
const padNum = parseInt(num, 10);
return [
'pad',
isNaN(padNum) ? num : padNum,
kiUnits(holeRadius) > 0 ? 'thru_hole' : 'smd',
shapes[shape],
shapes[actualShape],
kiAt(x, y, rotation, transform),
['size', Math.max(kiUnits(width), 0.01), Math.max(kiUnits(height), 0.01)],
['size', Math.max(kiUnits(actualWidth), 0.01), Math.max(kiUnits(actualHeight), 0.01)],
['layers', ...layers[layerId]],
getDrill(kiUnits(holeRadius), kiUnits(holeLength)),
netId > 0 ? ['net', netId, net] : null,
Expand Down

0 comments on commit c50b2a7

Please sign in to comment.