circuit-json-to-tscircuit 0.0.83 → 0.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-B56TEDTF.js → chunk-UZVNOAO4.js} +90 -5
- package/dist/cli/main.js +3 -3
- package/dist/lib/index.js +1 -1
- package/lib/generate-board-schematic-tsx/convert-schematic-graphics.ts +18 -0
- package/lib/generate-board-schematic-tsx.ts +2 -0
- package/lib/generate-footprint-tsx/convert-keepouts.ts +12 -4
- package/lib/generate-footprint-tsx/get-filled-circle-from-keepout-outline.ts +104 -0
- package/package.json +2 -2
- package/tests/fixtures/ti-evms/README.md +4 -0
- package/tests/fixtures/ti-evms/tmds62levm-sheet-05.circuit.json.gz +0 -0
- package/tests/get-filled-circle-from-keepout-outline.test.ts +49 -0
- package/tests/test17-support-pcb-keepouts.test.tsx +2 -2
- package/tests/ti-evms/__snapshots__/ti-lm251772evm-pd-roundtrip-pcb-comparison.snap.svg +1 -1
- package/tests/ti-evms/__snapshots__/ti-lm251772evm-pd-roundtrip.test.tsx.snap +5 -0
- package/tests/ti-evms/__snapshots__/ti-lm5155evm-fly-roundtrip-pcb-comparison.snap.svg +1 -1
- package/tests/ti-evms/__snapshots__/ti-lmg342x-bb-evm-roundtrip-pcb-comparison.snap.svg +1 -1
- package/tests/ti-evms/__snapshots__/ti-lmg342x-bb-evm-roundtrip.test.tsx.snap +9 -0
- package/tests/ti-evms/__snapshots__/tmds62levm-sheet-05-schematic-graphic.snap.svg +29 -0
- package/tests/ti-evms/ti-lm251772evm-pd-roundtrip.test.tsx +29 -0
- package/tests/ti-evms/ti-lm5155evm-fly-roundtrip.test.tsx +4 -0
- package/tests/ti-evms/tmds62levm-sheet-05-schematic-graphic.test.tsx +76 -0
|
@@ -1020,6 +1020,19 @@ var convertComponents = (circuitJson) => su3(circuitJson).schematic_component.li
|
|
|
1020
1020
|
return schematicComponent.is_box_with_pins ? convertBoxComponent({ circuitJson, schematicComponent }) : [];
|
|
1021
1021
|
});
|
|
1022
1022
|
|
|
1023
|
+
// lib/generate-board-schematic-tsx/convert-schematic-graphics.ts
|
|
1024
|
+
var convertSchematicGraphics = (circuitJson) => circuitJson.flatMap((element) => {
|
|
1025
|
+
if (element.type !== "schematic_graphic") return [];
|
|
1026
|
+
return [
|
|
1027
|
+
formatElement("schematicgraphic", {
|
|
1028
|
+
imageUrl: element.asset?.url,
|
|
1029
|
+
svgContent: element.svg_content,
|
|
1030
|
+
width: element.width,
|
|
1031
|
+
height: element.height
|
|
1032
|
+
})
|
|
1033
|
+
];
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1023
1036
|
// lib/generate-board-schematic-tsx/convert-net-labels.ts
|
|
1024
1037
|
import { su as su4 } from "@tscircuit/soup-util";
|
|
1025
1038
|
import { schematic_text as schematic_text3 } from "circuit-json";
|
|
@@ -1113,7 +1126,8 @@ var generateBoardSchematicElements = (circuitJson) => {
|
|
|
1113
1126
|
...convertComponents(circuitJson),
|
|
1114
1127
|
...convertNetLabels(circuitJson),
|
|
1115
1128
|
...convertPrimitives(circuitJson),
|
|
1116
|
-
...convertTraces(circuitJson)
|
|
1129
|
+
...convertTraces(circuitJson),
|
|
1130
|
+
...convertSchematicGraphics(circuitJson)
|
|
1117
1131
|
];
|
|
1118
1132
|
};
|
|
1119
1133
|
|
|
@@ -1628,20 +1642,91 @@ var convertHoles = (circuitJson) => {
|
|
|
1628
1642
|
|
|
1629
1643
|
// lib/generate-footprint-tsx/convert-keepouts.ts
|
|
1630
1644
|
import { su as su12 } from "@tscircuit/soup-util";
|
|
1645
|
+
|
|
1646
|
+
// lib/generate-footprint-tsx/get-filled-circle-from-keepout-outline.ts
|
|
1647
|
+
var MINIMUM_CIRCLE_VERTEX_COUNT = 8;
|
|
1648
|
+
var CIRCLE_RADIUS_TOLERANCE = 1e-3;
|
|
1649
|
+
var MAXIMUM_ANGULAR_STEP_RATIO = 1.5;
|
|
1650
|
+
var FULL_ROTATION_RADIANS = Math.PI * 2;
|
|
1651
|
+
var getNormalizedAngleDelta = (angleDelta) => {
|
|
1652
|
+
if (angleDelta > Math.PI) return angleDelta - FULL_ROTATION_RADIANS;
|
|
1653
|
+
if (angleDelta <= -Math.PI) return angleDelta + FULL_ROTATION_RADIANS;
|
|
1654
|
+
return angleDelta;
|
|
1655
|
+
};
|
|
1656
|
+
var completesCircularTraversal = ({
|
|
1657
|
+
center,
|
|
1658
|
+
outline,
|
|
1659
|
+
radius
|
|
1660
|
+
}) => {
|
|
1661
|
+
const firstPoint = outline[0];
|
|
1662
|
+
const lastPoint = outline.at(-1);
|
|
1663
|
+
if (!firstPoint || !lastPoint) return false;
|
|
1664
|
+
if (Math.hypot(firstPoint.x - lastPoint.x, firstPoint.y - lastPoint.y) > radius * CIRCLE_RADIUS_TOLERANCE) {
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
const traversalPoints = outline.slice(0, -1);
|
|
1668
|
+
const maximumAngularStep = FULL_ROTATION_RADIANS / traversalPoints.length * MAXIMUM_ANGULAR_STEP_RATIO;
|
|
1669
|
+
const angleDeltas = traversalPoints.map((point, pointIndex) => {
|
|
1670
|
+
const nextPoint = traversalPoints[(pointIndex + 1) % traversalPoints.length];
|
|
1671
|
+
const pointAngle = Math.atan2(point.y - center.y, point.x - center.x);
|
|
1672
|
+
const nextPointAngle = Math.atan2(
|
|
1673
|
+
nextPoint.y - center.y,
|
|
1674
|
+
nextPoint.x - center.x
|
|
1675
|
+
);
|
|
1676
|
+
return getNormalizedAngleDelta(nextPointAngle - pointAngle);
|
|
1677
|
+
});
|
|
1678
|
+
const traversalDirection = Math.sign(angleDeltas[0] ?? 0);
|
|
1679
|
+
return traversalDirection !== 0 && angleDeltas.every(
|
|
1680
|
+
(angleDelta) => Math.sign(angleDelta) === traversalDirection && Math.abs(angleDelta) <= maximumAngularStep
|
|
1681
|
+
);
|
|
1682
|
+
};
|
|
1683
|
+
var getFilledCircleFromKeepoutOutline = (keepout) => {
|
|
1684
|
+
const outline = keepout.outline;
|
|
1685
|
+
if (outline.length < MINIMUM_CIRCLE_VERTEX_COUNT) return null;
|
|
1686
|
+
const minX = Math.min(...outline.map((point) => point.x));
|
|
1687
|
+
const maxX = Math.max(...outline.map((point) => point.x));
|
|
1688
|
+
const minY = Math.min(...outline.map((point) => point.y));
|
|
1689
|
+
const maxY = Math.max(...outline.map((point) => point.y));
|
|
1690
|
+
const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
|
|
1691
|
+
const horizontalRadius = (maxX - minX) / 2;
|
|
1692
|
+
const verticalRadius = (maxY - minY) / 2;
|
|
1693
|
+
const centerlineRadius = (horizontalRadius + verticalRadius) / 2;
|
|
1694
|
+
if (!Number.isFinite(centerlineRadius) || centerlineRadius <= 0) return null;
|
|
1695
|
+
if (Math.abs(horizontalRadius - verticalRadius) > centerlineRadius * CIRCLE_RADIUS_TOLERANCE) {
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
const isCircular = outline.every((point) => {
|
|
1699
|
+
const pointRadius = Math.hypot(point.x - center.x, point.y - center.y);
|
|
1700
|
+
return Math.abs(pointRadius - centerlineRadius) <= centerlineRadius * CIRCLE_RADIUS_TOLERANCE;
|
|
1701
|
+
});
|
|
1702
|
+
if (!isCircular) return null;
|
|
1703
|
+
if (!completesCircularTraversal({ center, outline, radius: centerlineRadius }))
|
|
1704
|
+
return null;
|
|
1705
|
+
const strokeRadius = keepout.stroke_width / 2;
|
|
1706
|
+
if (strokeRadius < centerlineRadius) return null;
|
|
1707
|
+
return { center, radius: centerlineRadius + strokeRadius };
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
// lib/generate-footprint-tsx/convert-keepouts.ts
|
|
1711
|
+
var formatLayersAttribute = (layers) => ` layers={${JSON.stringify(layers)}}`;
|
|
1631
1712
|
var convertKeepouts = (circuitJson) => {
|
|
1632
1713
|
const pcbKeepouts = su12(circuitJson).pcb_keepout.list();
|
|
1633
1714
|
const elementStrings = [];
|
|
1634
1715
|
for (const keepout of pcbKeepouts) {
|
|
1635
1716
|
if (keepout.shape === "rect") {
|
|
1636
1717
|
elementStrings.push(
|
|
1637
|
-
`<keepout shape="rect" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" width="${formatMm(keepout.width)}" height="${formatMm(keepout.height)}" />`
|
|
1718
|
+
`<keepout shape="rect" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" width="${formatMm(keepout.width)}" height="${formatMm(keepout.height)}"${formatLayersAttribute(keepout.layers)} />`
|
|
1638
1719
|
);
|
|
1639
1720
|
} else if (keepout.shape === "circle") {
|
|
1640
1721
|
elementStrings.push(
|
|
1641
|
-
`<keepout shape="circle" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" radius="${formatMm(keepout.radius)}" />`
|
|
1722
|
+
`<keepout shape="circle" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" radius="${formatMm(keepout.radius)}"${formatLayersAttribute(keepout.layers)} />`
|
|
1723
|
+
);
|
|
1724
|
+
} else if (keepout.shape === "outline") {
|
|
1725
|
+
const circle = getFilledCircleFromKeepoutOutline(keepout);
|
|
1726
|
+
if (!circle) continue;
|
|
1727
|
+
elementStrings.push(
|
|
1728
|
+
`<keepout shape="circle" pcbX="${formatMm(circle.center.x)}" pcbY="${formatMm(circle.center.y)}" radius="${formatMm(circle.radius)}"${formatLayersAttribute(keepout.layers)} />`
|
|
1642
1729
|
);
|
|
1643
|
-
} else {
|
|
1644
|
-
console.warn(`Unhandled pcb_keepout shape: ${keepout.shape}`);
|
|
1645
1730
|
}
|
|
1646
1731
|
}
|
|
1647
1732
|
return elementStrings;
|
package/dist/cli/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
convertCircuitJsonToTscircuit
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-UZVNOAO4.js";
|
|
5
5
|
|
|
6
6
|
// cli/main.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -16,7 +16,7 @@ var package_default = {
|
|
|
16
16
|
"./cli": "./dist/cli/main.js"
|
|
17
17
|
},
|
|
18
18
|
type: "module",
|
|
19
|
-
version: "0.0.
|
|
19
|
+
version: "0.0.84",
|
|
20
20
|
repository: {
|
|
21
21
|
type: "git",
|
|
22
22
|
url: "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
|
|
@@ -38,7 +38,7 @@ var package_default = {
|
|
|
38
38
|
"@types/react-dom": "18",
|
|
39
39
|
"bun-match-svg": "^0.0.15",
|
|
40
40
|
"circuit-json": "^0.0.507",
|
|
41
|
-
"circuit-to-svg": "
|
|
41
|
+
"circuit-to-svg": "https://pkg.pr.new/tscircuit/circuit-to-svg@738ef1d",
|
|
42
42
|
commander: "^13.1.0",
|
|
43
43
|
prettier: "^3.9.5",
|
|
44
44
|
"schematic-symbols": "0.0.248",
|
package/dist/lib/index.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SchematicElementConverter } from "./converter-types"
|
|
2
|
+
import { formatElement } from "./format-attributes"
|
|
3
|
+
|
|
4
|
+
export const convertSchematicGraphics: SchematicElementConverter = (
|
|
5
|
+
circuitJson,
|
|
6
|
+
) =>
|
|
7
|
+
circuitJson.flatMap((element) => {
|
|
8
|
+
if (element.type !== "schematic_graphic") return []
|
|
9
|
+
|
|
10
|
+
return [
|
|
11
|
+
formatElement("schematicgraphic", {
|
|
12
|
+
imageUrl: element.asset?.url,
|
|
13
|
+
svgContent: element.svg_content,
|
|
14
|
+
width: element.width,
|
|
15
|
+
height: element.height,
|
|
16
|
+
}),
|
|
17
|
+
]
|
|
18
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AnyCircuitElement } from "circuit-json"
|
|
2
2
|
import { convertPrimitives } from "./generate-board-schematic-tsx/convert-primitives"
|
|
3
3
|
import { convertComponents } from "./generate-board-schematic-tsx/convert-components"
|
|
4
|
+
import { convertSchematicGraphics } from "./generate-board-schematic-tsx/convert-schematic-graphics"
|
|
4
5
|
import { convertNetLabels } from "./generate-board-schematic-tsx/convert-net-labels"
|
|
5
6
|
import { convertTraces } from "./generate-board-schematic-tsx/convert-traces"
|
|
6
7
|
|
|
@@ -21,5 +22,6 @@ export const generateBoardSchematicElements = (
|
|
|
21
22
|
...convertNetLabels(circuitJson),
|
|
22
23
|
...convertPrimitives(circuitJson),
|
|
23
24
|
...convertTraces(circuitJson),
|
|
25
|
+
...convertSchematicGraphics(circuitJson),
|
|
24
26
|
]
|
|
25
27
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { su } from "@tscircuit/soup-util"
|
|
2
2
|
import type { FootprintElementConverter } from "./converter-types"
|
|
3
3
|
import { formatMm } from "./footprint-tsx-attribute-formatters/format-mm"
|
|
4
|
+
import { getFilledCircleFromKeepoutOutline } from "./get-filled-circle-from-keepout-outline"
|
|
5
|
+
|
|
6
|
+
const formatLayersAttribute = (layers: string[]): string =>
|
|
7
|
+
` layers={${JSON.stringify(layers)}}`
|
|
4
8
|
|
|
5
9
|
export const convertKeepouts: FootprintElementConverter = (circuitJson) => {
|
|
6
10
|
const pcbKeepouts = su(circuitJson).pcb_keepout.list()
|
|
@@ -9,14 +13,18 @@ export const convertKeepouts: FootprintElementConverter = (circuitJson) => {
|
|
|
9
13
|
for (const keepout of pcbKeepouts) {
|
|
10
14
|
if (keepout.shape === "rect") {
|
|
11
15
|
elementStrings.push(
|
|
12
|
-
`<keepout shape="rect" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" width="${formatMm(keepout.width)}" height="${formatMm(keepout.height)}" />`,
|
|
16
|
+
`<keepout shape="rect" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" width="${formatMm(keepout.width)}" height="${formatMm(keepout.height)}"${formatLayersAttribute(keepout.layers)} />`,
|
|
13
17
|
)
|
|
14
18
|
} else if (keepout.shape === "circle") {
|
|
15
19
|
elementStrings.push(
|
|
16
|
-
`<keepout shape="circle" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" radius="${formatMm(keepout.radius)}" />`,
|
|
20
|
+
`<keepout shape="circle" pcbX="${formatMm(keepout.center.x)}" pcbY="${formatMm(keepout.center.y)}" radius="${formatMm(keepout.radius)}"${formatLayersAttribute(keepout.layers)} />`,
|
|
21
|
+
)
|
|
22
|
+
} else if (keepout.shape === "outline") {
|
|
23
|
+
const circle = getFilledCircleFromKeepoutOutline(keepout)
|
|
24
|
+
if (!circle) continue
|
|
25
|
+
elementStrings.push(
|
|
26
|
+
`<keepout shape="circle" pcbX="${formatMm(circle.center.x)}" pcbY="${formatMm(circle.center.y)}" radius="${formatMm(circle.radius)}"${formatLayersAttribute(keepout.layers)} />`,
|
|
17
27
|
)
|
|
18
|
-
} else {
|
|
19
|
-
console.warn(`Unhandled pcb_keepout shape: ${(keepout as any).shape}`)
|
|
20
28
|
}
|
|
21
29
|
}
|
|
22
30
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { PcbKeepoutOutline } from "circuit-json"
|
|
2
|
+
|
|
3
|
+
interface FilledKeepoutCircle {
|
|
4
|
+
center: { x: number; y: number }
|
|
5
|
+
radius: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const MINIMUM_CIRCLE_VERTEX_COUNT = 8
|
|
9
|
+
const CIRCLE_RADIUS_TOLERANCE = 0.001
|
|
10
|
+
const MAXIMUM_ANGULAR_STEP_RATIO = 1.5
|
|
11
|
+
const FULL_ROTATION_RADIANS = Math.PI * 2
|
|
12
|
+
|
|
13
|
+
const getNormalizedAngleDelta = (angleDelta: number): number => {
|
|
14
|
+
if (angleDelta > Math.PI) return angleDelta - FULL_ROTATION_RADIANS
|
|
15
|
+
if (angleDelta <= -Math.PI) return angleDelta + FULL_ROTATION_RADIANS
|
|
16
|
+
return angleDelta
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const completesCircularTraversal = ({
|
|
20
|
+
center,
|
|
21
|
+
outline,
|
|
22
|
+
radius,
|
|
23
|
+
}: {
|
|
24
|
+
center: { x: number; y: number }
|
|
25
|
+
outline: PcbKeepoutOutline["outline"]
|
|
26
|
+
radius: number
|
|
27
|
+
}): boolean => {
|
|
28
|
+
const firstPoint = outline[0]
|
|
29
|
+
const lastPoint = outline.at(-1)
|
|
30
|
+
if (!firstPoint || !lastPoint) return false
|
|
31
|
+
if (
|
|
32
|
+
Math.hypot(firstPoint.x - lastPoint.x, firstPoint.y - lastPoint.y) >
|
|
33
|
+
radius * CIRCLE_RADIUS_TOLERANCE
|
|
34
|
+
) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const traversalPoints = outline.slice(0, -1)
|
|
39
|
+
const maximumAngularStep =
|
|
40
|
+
(FULL_ROTATION_RADIANS / traversalPoints.length) *
|
|
41
|
+
MAXIMUM_ANGULAR_STEP_RATIO
|
|
42
|
+
const angleDeltas = traversalPoints.map((point, pointIndex) => {
|
|
43
|
+
const nextPoint =
|
|
44
|
+
traversalPoints[(pointIndex + 1) % traversalPoints.length]!
|
|
45
|
+
const pointAngle = Math.atan2(point.y - center.y, point.x - center.x)
|
|
46
|
+
const nextPointAngle = Math.atan2(
|
|
47
|
+
nextPoint.y - center.y,
|
|
48
|
+
nextPoint.x - center.x,
|
|
49
|
+
)
|
|
50
|
+
return getNormalizedAngleDelta(nextPointAngle - pointAngle)
|
|
51
|
+
})
|
|
52
|
+
const traversalDirection = Math.sign(angleDeltas[0] ?? 0)
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
traversalDirection !== 0 &&
|
|
56
|
+
angleDeltas.every(
|
|
57
|
+
(angleDelta) =>
|
|
58
|
+
Math.sign(angleDelta) === traversalDirection &&
|
|
59
|
+
Math.abs(angleDelta) <= maximumAngularStep,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const getFilledCircleFromKeepoutOutline = (
|
|
65
|
+
keepout: PcbKeepoutOutline,
|
|
66
|
+
): FilledKeepoutCircle | null => {
|
|
67
|
+
const outline = keepout.outline
|
|
68
|
+
if (outline.length < MINIMUM_CIRCLE_VERTEX_COUNT) return null
|
|
69
|
+
|
|
70
|
+
const minX = Math.min(...outline.map((point) => point.x))
|
|
71
|
+
const maxX = Math.max(...outline.map((point) => point.x))
|
|
72
|
+
const minY = Math.min(...outline.map((point) => point.y))
|
|
73
|
+
const maxY = Math.max(...outline.map((point) => point.y))
|
|
74
|
+
const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 }
|
|
75
|
+
const horizontalRadius = (maxX - minX) / 2
|
|
76
|
+
const verticalRadius = (maxY - minY) / 2
|
|
77
|
+
const centerlineRadius = (horizontalRadius + verticalRadius) / 2
|
|
78
|
+
|
|
79
|
+
if (!Number.isFinite(centerlineRadius) || centerlineRadius <= 0) return null
|
|
80
|
+
if (
|
|
81
|
+
Math.abs(horizontalRadius - verticalRadius) >
|
|
82
|
+
centerlineRadius * CIRCLE_RADIUS_TOLERANCE
|
|
83
|
+
) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isCircular = outline.every((point) => {
|
|
88
|
+
const pointRadius = Math.hypot(point.x - center.x, point.y - center.y)
|
|
89
|
+
return (
|
|
90
|
+
Math.abs(pointRadius - centerlineRadius) <=
|
|
91
|
+
centerlineRadius * CIRCLE_RADIUS_TOLERANCE
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
if (!isCircular) return null
|
|
95
|
+
if (
|
|
96
|
+
!completesCircularTraversal({ center, outline, radius: centerlineRadius })
|
|
97
|
+
)
|
|
98
|
+
return null
|
|
99
|
+
|
|
100
|
+
const strokeRadius = keepout.stroke_width / 2
|
|
101
|
+
if (strokeRadius < centerlineRadius) return null
|
|
102
|
+
|
|
103
|
+
return { center, radius: centerlineRadius + strokeRadius }
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"./cli": "./dist/cli/main.js"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.85",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@types/react-dom": "18",
|
|
29
29
|
"bun-match-svg": "^0.0.15",
|
|
30
30
|
"circuit-json": "^0.0.507",
|
|
31
|
-
"circuit-to-svg": "
|
|
31
|
+
"circuit-to-svg": "https://pkg.pr.new/tscircuit/circuit-to-svg@738ef1d",
|
|
32
32
|
"commander": "^13.1.0",
|
|
33
33
|
"prettier": "^3.9.5",
|
|
34
34
|
"schematic-symbols": "0.0.248",
|
|
@@ -13,3 +13,7 @@ LMG342X-BB-EVM PCB fixtures were resolved from their matching `.PrjPcb` files us
|
|
|
13
13
|
The upstream files and checksums are maintained in that repository's
|
|
14
14
|
`scripts/references/reference-manifest.ts`. The gzip files only avoid committing
|
|
15
15
|
several megabytes of repetitive JSON; tests expand them before conversion.
|
|
16
|
+
|
|
17
|
+
`tmds62levm-sheet-05.circuit.json.gz` contains the unchanged conversion of
|
|
18
|
+
TMDS62LEVM revision B sheet 05 from `altium-to-circuit-json` v0.0.85
|
|
19
|
+
(`568824fb3ff64992133378b30e88fc23bbd43f48`).
|
|
Binary file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import type { PcbKeepoutOutline } from "circuit-json"
|
|
3
|
+
import { getFilledCircleFromKeepoutOutline } from "../lib/generate-footprint-tsx/get-filled-circle-from-keepout-outline"
|
|
4
|
+
|
|
5
|
+
const createCircularKeepout = ({
|
|
6
|
+
closeOutline,
|
|
7
|
+
sweepDegrees,
|
|
8
|
+
}: {
|
|
9
|
+
closeOutline: boolean
|
|
10
|
+
sweepDegrees: number
|
|
11
|
+
}): PcbKeepoutOutline => {
|
|
12
|
+
const segmentCount = 48
|
|
13
|
+
const outline = Array.from({ length: segmentCount + 1 }, (_, pointIndex) => {
|
|
14
|
+
const angle = ((pointIndex / segmentCount) * sweepDegrees * Math.PI) / 180
|
|
15
|
+
return { x: Math.cos(angle), y: Math.sin(angle) }
|
|
16
|
+
})
|
|
17
|
+
if (closeOutline && sweepDegrees < 360) outline.push(outline[0]!)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
type: "pcb_keepout",
|
|
21
|
+
pcb_keepout_id: "circular_outline_keepout",
|
|
22
|
+
shape: "outline",
|
|
23
|
+
outline,
|
|
24
|
+
stroke_width: 2,
|
|
25
|
+
layers: ["top"],
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test("only complete closed circular outlines become filled disks", () => {
|
|
30
|
+
const fullCircle = createCircularKeepout({
|
|
31
|
+
closeOutline: true,
|
|
32
|
+
sweepDegrees: 360,
|
|
33
|
+
})
|
|
34
|
+
const openPartialArc = createCircularKeepout({
|
|
35
|
+
closeOutline: false,
|
|
36
|
+
sweepDegrees: 270,
|
|
37
|
+
})
|
|
38
|
+
const closedPartialArc = createCircularKeepout({
|
|
39
|
+
closeOutline: true,
|
|
40
|
+
sweepDegrees: 270,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(getFilledCircleFromKeepoutOutline(fullCircle)).toEqual({
|
|
44
|
+
center: { x: 0, y: 0 },
|
|
45
|
+
radius: 2,
|
|
46
|
+
})
|
|
47
|
+
expect(getFilledCircleFromKeepoutOutline(openPartialArc)).toBeNull()
|
|
48
|
+
expect(getFilledCircleFromKeepoutOutline(closedPartialArc)).toBeNull()
|
|
49
|
+
})
|
|
@@ -14,8 +14,8 @@ test("test17 support pcb keepouts", async () => {
|
|
|
14
14
|
export const Test17Component = (props: ChipProps) => (
|
|
15
15
|
<chip
|
|
16
16
|
footprint={<footprint>
|
|
17
|
-
<keepout shape="rect" pcbX="-1.5mm" pcbY="2mm" width="3.2mm" height="1.4mm" />
|
|
18
|
-
<keepout shape="circle" pcbX="2.5mm" pcbY="-1mm" radius="0.9mm" />
|
|
17
|
+
<keepout shape="rect" pcbX="-1.5mm" pcbY="2mm" width="3.2mm" height="1.4mm" layers={["top"]} />
|
|
18
|
+
<keepout shape="circle" pcbX="2.5mm" pcbY="-1mm" radius="0.9mm" layers={["top"]} />
|
|
19
19
|
</footprint>}
|
|
20
20
|
{...props}
|
|
21
21
|
/>
|