From 8a4be0aceca73fde740cfa8d5b59d364f03ee451 Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Thu, 5 Mar 2026 20:04:39 -0800 Subject: [PATCH] Add courtyard outline import from EasyEDA layer 12 --- ...ert-easyeda-json-to-tscircuit-soup-json.ts | 55 ++++++++++++++++++- .../__snapshots__/c14877-courtyard.snap.svg | 1 + .../c14877-courtyard.test.ts | 19 +++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/convert-to-soup-tests/__snapshots__/c14877-courtyard.snap.svg create mode 100644 tests/convert-to-soup-tests/c14877-courtyard.test.ts diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts index 7bf17953..da2c9254 100644 --- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts @@ -19,6 +19,7 @@ import { any_source_component, pcb_smtpad, pcb_silkscreen_path, + pcb_courtyard_outline, pcb_plated_hole, pcb_hole, pcb_via, @@ -116,6 +117,23 @@ const handleSilkscreenPath = ( }) } +const handleCourtyardPath = ( + track: z.infer, + index: number, +) => { + return pcb_courtyard_outline.parse({ + type: "pcb_courtyard_outline", + pcb_courtyard_outline_id: `pcb_courtyard_outline_${index + 1}`, + pcb_component_id: "pcb_component_1", + layer: "top", + outline: track.points.map((point) => ({ + x: milx10(point.x), + y: milx10(point.y), + })), + stroke_width: mil10ToMm(track.width), + }) +} + const handleSilkscreenArc = (arc: z.infer, index: number) => { const arcPath = generateArcFromSweep( arc.start.x, @@ -140,6 +158,30 @@ const handleSilkscreenArc = (arc: z.infer, index: number) => { } as Soup.PcbSilkscreenPathInput) } +const handleCourtyardArc = (arc: z.infer, index: number) => { + const arcPath = generateArcFromSweep( + arc.start.x, + arc.start.y, + arc.end.x, + arc.end.y, + arc.radiusX, + arc.largeArc, + arc.sweepDirection === "CW", + ) + + return pcb_courtyard_outline.parse({ + type: "pcb_courtyard_outline", + pcb_courtyard_outline_id: `pcb_courtyard_arc_${index + 1}`, + pcb_component_id: "pcb_component_1", + layer: "top", + outline: arcPath.map((p) => ({ + x: milx10(p.x), + y: milx10(p.y), + })), + stroke_width: mil10ToMm(arc.width), + }) +} + const handleHole = (hole: z.infer, index: number) => { return pcb_hole.parse({ type: "pcb_hole", @@ -461,9 +503,17 @@ export const convertEasyEdaJsonToCircuitJson = ( // Add silkscreen paths, arcs and text easyEdaJson.packageDetail.dataStr.shape.forEach((shape, index) => { if (shape.type === "TRACK") { - circuitElements.push(handleSilkscreenPath(shape, index)) + if (shape.layer === 12) { + circuitElements.push(handleCourtyardPath(shape, index)) + } else { + circuitElements.push(handleSilkscreenPath(shape, index)) + } } else if (shape.type === "ARC") { - circuitElements.push(handleSilkscreenArc(shape, index)) + if (shape.layer === 12) { + circuitElements.push(handleCourtyardArc(shape, index)) + } else { + circuitElements.push(handleSilkscreenArc(shape, index)) + } } else if (shape.type === "TEXT") { circuitElements.push( Soup.pcb_silkscreen_text.parse({ @@ -495,6 +545,7 @@ export const convertEasyEdaJsonToCircuitJson = ( e.type === "pcb_hole" || e.type === "pcb_via" || e.type === "pcb_silkscreen_path" || + e.type === "pcb_courtyard_outline" || e.type === "pcb_silkscreen_text", ) diff --git a/tests/convert-to-soup-tests/__snapshots__/c14877-courtyard.snap.svg b/tests/convert-to-soup-tests/__snapshots__/c14877-courtyard.snap.svg new file mode 100644 index 00000000..d76739cc --- /dev/null +++ b/tests/convert-to-soup-tests/__snapshots__/c14877-courtyard.snap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/convert-to-soup-tests/c14877-courtyard.test.ts b/tests/convert-to-soup-tests/c14877-courtyard.test.ts new file mode 100644 index 00000000..76178612 --- /dev/null +++ b/tests/convert-to-soup-tests/c14877-courtyard.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from "bun:test" +import c14877 from "tests/assets/C14877.raweasy.json" +import { convertEasyEdaJsonToCircuitJson, EasyEdaJsonSchema } from "lib/index" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" + +test("C14877 imports courtyard geometry", () => { + const circuitJson = convertEasyEdaJsonToCircuitJson( + EasyEdaJsonSchema.parse(c14877), + ) + + const courtyardOutlines = circuitJson.filter( + (element) => element.type === "pcb_courtyard_outline", + ) + + expect(courtyardOutlines.length).toBeGreaterThan(0) + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + ) +})