Add GDSII and OASIS export
This commit is contained in:
parent
a47466016f
commit
d7dd0e363b
3 changed files with 107 additions and 3 deletions
|
|
@ -50,6 +50,7 @@ dev = [
|
|||
"ipykernel>=7.1.0",
|
||||
]
|
||||
gui = ["cairosvg", "pillow"]
|
||||
gds = ["gdstk"]
|
||||
|
||||
[build-system]
|
||||
requires = ["uv-build"]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,27 @@ def print_valid_twists(ctx, param, value):
|
|||
ctx.exit()
|
||||
|
||||
|
||||
def circle_center_to_tangents(center, a, b):
|
||||
""" Given two points on a circle and the center of the circle, calculate the intersection of two tangents at the two points """
|
||||
cx, cy = center
|
||||
ax, ay = a
|
||||
bx, by = b
|
||||
|
||||
dax = ax - cx
|
||||
day = ay - cy
|
||||
dbx = bx - cx
|
||||
dby = by - cy
|
||||
|
||||
v = dax*ax + day*ay
|
||||
w = dbx*bx + dby*by
|
||||
det = dax*dby - day*dbx
|
||||
|
||||
ix = (v*dby - day*w) / det
|
||||
iy = (dax*w - v*dbx) / det
|
||||
|
||||
return ix, iy
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option('--turns', type=int, default=5, help='Number of turns')
|
||||
@click.option('--twists', type=int, default=1, help='Number of twists per revolution. Note that this number must be co-prime to the number of turns. Run with --show-twists to list valid values. (default: 1)')
|
||||
|
|
@ -64,19 +85,20 @@ def print_valid_twists(ctx, param, value):
|
|||
@click.option('--circle-segments', type=int, default=64, help='When not using arcs, the number of points to use for arc interpolation per 360 degrees.')
|
||||
@click.option('--arc-tolerance', type=float, default=0.02)
|
||||
@click.option('--approximate-arcs/--no-approximate-arcs', default=True, help='Use circular arcs to smoothen output shape (default: on)')
|
||||
@click.option('--format', type=click.Choice(['svg', 'gerber', 'kicad-footprint', 'kicad-pcb', 'json', 'show']), default='kicad-footprint')
|
||||
@click.option('--format', type=click.Choice(['svg', 'gerber', 'kicad-footprint', 'kicad-pcb', 'json', 'gdsii', 'oasis', 'show']), default='kicad-footprint')
|
||||
@click.option('--clipboard/--no-clipboard', help='Use clipboard integration (requires wl-clipboard)')
|
||||
@click.option('--footprint-name', help="Name for the generated footprint. Default: Output file name sans extension.")
|
||||
@click.option('--cell-name', help="Name for the generated cell when exporting GDSII. Default: Output file name sans extension.")
|
||||
@click.option('--layer-pair', default='F.Cu,B.Cu', help="Target KiCad layer pair for the generated footprint, comma-separated. Default: F.Cu/B.Cu.")
|
||||
@click.version_option()
|
||||
@click.pass_context
|
||||
def cli(ctx, footprint_name, clipboard, single_layer, arc_tolerance, circle_segments, format, **kwargs):
|
||||
def cli(ctx, footprint_name, cell_name, clipboard, single_layer, arc_tolerance, circle_segments, format, **kwargs):
|
||||
ctx.ensure_object(dict)
|
||||
logger = logging.getLogger('kicoil')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
def write(shape, outfile):
|
||||
nonlocal footprint_name, clipboard, single_layer, arc_tolerance, circle_segments, format
|
||||
nonlocal footprint_name, clipboard, single_layer, arc_tolerance, circle_segments, format, cell_name
|
||||
logger = logging.getLogger('kicoil')
|
||||
|
||||
if single_layer:
|
||||
|
|
@ -160,6 +182,51 @@ def cli(ctx, footprint_name, clipboard, single_layer, arc_tolerance, circle_segm
|
|||
|
||||
data = json.dumps(d, indent=4)
|
||||
|
||||
elif format in ('gdsii', 'oasis'):
|
||||
import gdstk
|
||||
|
||||
DRILL_LAYER = 2
|
||||
lib = gdstk.Library()
|
||||
|
||||
if cell_name is None:
|
||||
if outfile:
|
||||
cell_name = outfile.stem
|
||||
else:
|
||||
cell_name = f'planar_coil'
|
||||
cell = lib.new_cell(cell_name)
|
||||
|
||||
for line in footprint.lines:
|
||||
layer = model.layer_pair.index(line.layer)
|
||||
path = gdstk.FlexPath([(line.start.x, line.start.y), (line.end.x, line.end.y)], line.stroke.width, ends=['round'], layer=layer)
|
||||
cell.add(path)
|
||||
|
||||
for arc in footprint.arcs:
|
||||
layer = model.layer_pair.index(arc.layer)
|
||||
center, r, _direction = kicad_mid_to_center_arc(arc.mid, arc.start, arc.end)
|
||||
proj_x, proj_y = circle_center_to_tangents(center, tuple(arc.start), tuple(arc.end))
|
||||
# multiply r with 0.99 to make sure gdstk's interpolation routine catches on since the arc endpoints are calculated exactly
|
||||
path = gdstk.FlexPath([(arc.start.x, arc.start.y), (proj_x, proj_y), (arc.end.x, arc.end.y)], arc.stroke.width, bend_radius=r*0.99, ends=['round'], layer=layer)
|
||||
cell.add(path)
|
||||
|
||||
for pad in footprint.pads:
|
||||
for layer in pad.layers:
|
||||
layer = model.layer_pair.index(layer)
|
||||
layer_obj = gdstk.ellipse(tuple(pad.at), pad.size.x/2, layer=layer)
|
||||
cell.add(layer_obj)
|
||||
|
||||
if pad.drill:
|
||||
drill = gdstk.ellipse(tuple(pad.at), pad.drill.diameter/2, layer=DRILL_LAYER)
|
||||
cell.add(drill)
|
||||
|
||||
if clipboard or not outfile:
|
||||
raise click.ClickException('outfile is required for GDSII or OASIS export')
|
||||
|
||||
if format == 'gdsii':
|
||||
lib.write_gds(outfile)
|
||||
else:
|
||||
lib.write_oas(outfile)
|
||||
return
|
||||
|
||||
elif format in ('svg', 'show'):
|
||||
data = str(make_transparent_svg(footprint))
|
||||
|
||||
|
|
|
|||
36
uv.lock
generated
36
uv.lock
generated
|
|
@ -325,6 +325,38 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485, upload-time = "2025-11-28T17:05:47.573Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gdstk"
|
||||
version = "0.9.61"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/b5/a12ef182943856ffd55a36e825d01f12414ac2f3610950378a69291edf3e/gdstk-0.9.61.tar.gz", hash = "sha256:2967935fdf455c56ca77ad5c703c87cb88644ab75e752dcac866a36499879c6f", size = 317766, upload-time = "2025-08-28T10:17:15.192Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/07/9a/7ea7b7a295e029542d4aeb252d01c1bcc8e724df26155f9b5f432b02d02a/gdstk-0.9.61-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3beeae846fc523c7e3a01c47edcd3b7dd83c29650e56b82a371e528f9cb0ec3e", size = 923024, upload-time = "2025-08-28T10:16:53.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/3d/5aa9b1a4665259702e9f17e03a9a114a873df25c9ba2c9e782ff25fb11e9/gdstk-0.9.61-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:575a21639b31e2fab4d9e918468b8b40a58183028db563e5963be594bff1403d", size = 475687, upload-time = "2025-08-28T10:16:54.886Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/13/ec783d8de5d9b4e51763102cac6da124f16747e4c73f166c36a105065008/gdstk-0.9.61-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:90d54b48223dcbb8257769faaa87542d12a749d8486e8d1187a45d06e9422859", size = 536872, upload-time = "2025-10-20T11:25:52.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/4a/365ca49b76ee3d70a0d044fcc44272c85754fd763e0b3f3e9f498b8bf4a1/gdstk-0.9.61-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35405bed95542a0b10f343b165ce0ad80740bf8127a4507565ec74222e6ec8d3", size = 600630, upload-time = "2025-08-28T10:16:56.326Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/3e/815fd2977ff1d885ad87d0a54deb19926fd025933325c7a27625c1c0a0c3/gdstk-0.9.61-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b311ddf8982995b52ac3bf3b32a6cf6d918afc4e66dea527d531e8af73896231", size = 536873, upload-time = "2025-08-28T10:16:57.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/8b/1ba0abc4fb3c60015d23894aa9d093f473fdc337584f9c1d7afe96d6f9f5/gdstk-0.9.61-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dcbfc60fba92d10f1c7d612b5409c343fcaf2a380640e9fb01c504ca948b412", size = 540029, upload-time = "2025-10-20T11:26:09.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/72/cc46f132741e541995ede7fccf9820f105fb2296ab70192bd27de56190f2/gdstk-0.9.61-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:fab67ccdd8029ef7eb873f8c98f875dc2665a5e45af7cf3d2a7a0f401826a1d3", size = 535763, upload-time = "2025-08-28T10:16:58.621Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/db/72196721fedfc38cf21158e5a436d73b41ea244b78c1053462a35d1e42cb/gdstk-0.9.61-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5852749e203d6978e06d02f8ef9e29ce4512cb1aedeb62c37b8e8b2c10c4f529", size = 1711669, upload-time = "2025-08-28T10:16:59.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/ec/eeebcc95c2741e1f39da08c95fdcc56b3d0f5305ad732d8a66213b6fa0b8/gdstk-0.9.61-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ee38a54c799e77dbe219266f765bbd3b2906b62bc7b6fb64b1387e6db3dd187", size = 1535165, upload-time = "2025-08-28T10:17:01.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/a8/653335b1ec13306b023a9aa1e2072e8bab5c0a5376c138066006504198c3/gdstk-0.9.61-cp313-cp313-win_amd64.whl", hash = "sha256:6abb396873b2660dd7863d664b3822f00547bf7f216af27be9f1f812bc5e8027", size = 500117, upload-time = "2025-08-28T10:17:02.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/52/28cd8720357d6b892ac19684e4af57f7264ba8eea0ea8078e17f0476408c/gdstk-0.9.61-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a674af8be5cf1f8ea9f6c5b5f165f797d7e2ed74cbca68b4a22adb92b515fb35", size = 916091, upload-time = "2025-10-20T11:25:35.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/f1/c55c7b7b0158a8540716a4fea3aaf0288726122afc0e9af1f0564b79605b/gdstk-0.9.61-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:38ec0b7285d6c9bf8cbc279731dc0d314633cda2ce9e6f9053554b3e5f004fcd", size = 474548, upload-time = "2025-10-20T11:25:38.086Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/83/23907af9b349d79af54c4a79ac7972b9a52f8cee48b366ee9635cf9a32d9/gdstk-0.9.61-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3b63a77b57fb441c8017217aaf1e8b13d93cbee822031a8e2826adb716e01dd4", size = 537072, upload-time = "2025-10-20T11:25:55.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/e7/e930928f9a03b896f51b6e975dbb652cffa81c61338609d6289c3f48313c/gdstk-0.9.61-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7fae6eee627e837d1405b47d381ccd33dbba85473b1bb3822bdc8ae41dbc0dc", size = 540132, upload-time = "2025-10-20T11:26:11.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/9d/56afbb84cccb07751f8532591bfc3a1608833943446b51978b52daaaa2b1/gdstk-0.9.61-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9e396694cac24bd87d0e38c37e6740d9ba0c13f6c9f2211a871d62288430f069", size = 1578033, upload-time = "2025-10-20T11:26:18.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/14/c854d3ef2b0ece30c0cb4c034d3b7f58425086383a229c960d813a163861/gdstk-0.9.61-cp314-cp314-win_amd64.whl", hash = "sha256:7ea0c1200dc53b794e9c0cc6fe3ea51e49113dfdd9c3109e1961cda3cc2197c7", size = 513072, upload-time = "2025-10-20T11:25:21.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/0a/5e9c3be1327d36556a5e8d16c6696614fdc1df0d28c0629b829785245d76/gdstk-0.9.61-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:616dd1c3e7aea4a98aeb03db7cf76a853d134c54690790eaa25c63eede7b869a", size = 925091, upload-time = "2025-10-20T11:25:40.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/4a/a72de67ea1c217537ae537d7e96b6a0b3c7427177bd1439c89e7617ad3f0/gdstk-0.9.61-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b0e898202fbb7fd4c39f8404831415a0aa0445656342102c4e77d4a7c2c15a1d", size = 477723, upload-time = "2025-10-20T11:25:44.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/94/5034a646ee3bda58210cd377b241582161c8683489210cba762d4f7f06d1/gdstk-0.9.61-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:29bb862a1a814f5bbd6f8bbc2f99e1163df9e6307071cb6e11251dbe7542feb5", size = 541773, upload-time = "2025-10-20T11:25:57.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/90/3e5883b528dcc7c4daa74925276a09ff274004518deea48c859b9215120b/gdstk-0.9.61-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6c2a08d82a683aff50dc63f2943ed805d32d46bd984cbd4ac9cf876146d0ef9", size = 544525, upload-time = "2025-10-20T11:26:13.877Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/0b/549a0e72982b87011013705cdb552e2acfdb882ceaeb41a3fe020e981ec8/gdstk-0.9.61-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3ba52f95763052a6968583942e6531ceca20c14c762d44fe2bd887445e2f73b6", size = 1582069, upload-time = "2025-10-20T11:26:21.189Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gerbonara"
|
||||
version = "1.6.0"
|
||||
|
|
@ -554,6 +586,9 @@ dependencies = [
|
|||
dev = [
|
||||
{ name = "ipykernel" },
|
||||
]
|
||||
gds = [
|
||||
{ name = "gdstk" },
|
||||
]
|
||||
gui = [
|
||||
{ name = "cairosvg" },
|
||||
{ name = "pillow" },
|
||||
|
|
@ -573,6 +608,7 @@ requires-dist = [
|
|||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [{ name = "ipykernel", specifier = ">=7.1.0" }]
|
||||
gds = [{ name = "gdstk" }]
|
||||
gui = [
|
||||
{ name = "cairosvg" },
|
||||
{ name = "pillow" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue