diff --git a/benchmark/benchmark.ts b/benchmark/benchmark.ts index c680cef..8eacb93 100644 --- a/benchmark/benchmark.ts +++ b/benchmark/benchmark.ts @@ -24,7 +24,7 @@ async function benchmark() { await convertEasyEdaJsonToVariousFormats({ jlcpcbPartNumberOrFilepath: partnumber, outputFilename: "temp.tsx", - formatType: "tsx", + outputFormat: "tsx", }) successes++ } catch (error) { diff --git a/bun.lockb b/bun.lockb index 9a62973..788fb9c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/cli/main.ts b/cli/main.ts index 4bfb22a..67f22b5 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -6,11 +6,10 @@ import { convertEasyEdaJsonToTscircuitSoupJson } from "../lib/convert-easyeda-js import fs from "fs/promises" import packageJson from "../package.json" import { EasyEdaJsonSchema } from "lib/schemas/easy-eda-json-schema" -import { convertRawEasyEdaToTs } from "lib/convert-to-typescript-component" +import { convertRawEasyToTsx } from "lib/convert-to-typescript-component" import * as path from "path" import { normalizeManufacturerPartNumber } from "lib" import { convertEasyEdaJsonToVariousFormats } from "lib/convert-easyeda-json-to-various-formats" -import { perfectCli } from "perfect-cli" const program = new Command() @@ -57,5 +56,5 @@ program } }) -// program.parse(process.argv) -perfectCli(program, process.argv) +program.parse(process.argv) +// perfectCli(program, process.argv) diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts index 091c83f..a6e3657 100644 --- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts @@ -24,7 +24,7 @@ import { } from "circuit-json" import * as Soup from "circuit-json" import { generateArcFromSweep, generateArcPathWithMid } from "./math/arc-utils" -import { transformPCBElements } from "@tscircuit/soup-util" +import { findBoundsAndCenter, transformPCBElements } from "@tscircuit/soup-util" import { compose, scale, translate } from "transformation-matrix" import { computeCenterOffset } from "./compute-center-offset" import { mm } from "@tscircuit/mm" @@ -123,10 +123,10 @@ export const convertEasyEdaJsonToCircuitJson = ( source_component_id: "source_component_1", name: "U1", ftype: "simple_bug", - width: 0, // TODO compute width - height: 0, // TODO compute height + width: 0, // we update this at the end + height: 0, // we update this at the end rotation: 0, - center: { x: centerOffset.x, y: centerOffset.y }, + center: { x: 0, y: 0 }, layer: "top", } as PcbComponentInput) @@ -255,9 +255,14 @@ export const convertEasyEdaJsonToCircuitJson = ( } if (shouldRecenter) { + const bounds = findBoundsAndCenter( + // exclude the pcb_component because it's center is currently incorrect, + // we set it to (0,0) + soupElements.filter((e) => e.type !== "pcb_component"), + ) transformPCBElements( soupElements, - compose(translate(-centerOffset.x, centerOffset.y), scale(1, -1)), + compose(translate(-bounds.center.x, bounds.center.y), scale(1, -1)), ) } diff --git a/lib/convert-easyeda-json-to-various-formats.ts b/lib/convert-easyeda-json-to-various-formats.ts index 3d0835b..552ab49 100644 --- a/lib/convert-easyeda-json-to-various-formats.ts +++ b/lib/convert-easyeda-json-to-various-formats.ts @@ -2,7 +2,7 @@ import { fetchEasyEDAComponent } from "../lib/fetch-easyeda-json" import { convertEasyEdaJsonToTscircuitSoupJson } from "../lib/convert-easyeda-json-to-tscircuit-soup-json" import fs from "fs/promises" import { EasyEdaJsonSchema } from "lib/schemas/easy-eda-json-schema" -import { convertRawEasyEdaToTs } from "lib/convert-to-typescript-component" +import { convertRawEasyToTsx } from "lib/convert-to-typescript-component" import * as path from "path" import { normalizeManufacturerPartNumber } from "lib" @@ -83,7 +83,7 @@ export const convertEasyEdaJsonToVariousFormats = async ({ outputFilename.endsWith(".tsx") || outputFilename.endsWith(".ts") ) { - const tsComp = await convertRawEasyEdaToTs(rawEasyEdaJson) + const tsComp = await convertRawEasyToTsx(rawEasyEdaJson) await fs.writeFile(outputFilename, tsComp) console.log( `[${jlcpcbPartNumberOrFilepath}] Saved TypeScript component: ${outputFilename}`, diff --git a/lib/convert-to-typescript-component/index.tsx b/lib/convert-to-typescript-component/index.tsx index 3825166..43e4ead 100644 --- a/lib/convert-to-typescript-component/index.tsx +++ b/lib/convert-to-typescript-component/index.tsx @@ -5,35 +5,36 @@ import { } from "lib/schemas/easy-eda-json-schema" import { su } from "@tscircuit/soup-util" import { soupTypescriptComponentTemplate } from "./soup-typescript-component-template" -import { convertEasyEdaJsonToTscircuitSoupJson } from "lib/convert-easyeda-json-to-tscircuit-soup-json" +import { + convertEasyEdaJsonToCircuitJson, + convertEasyEdaJsonToTscircuitSoupJson, +} from "lib/convert-easyeda-json-to-tscircuit-soup-json" import { normalizeManufacturerPartNumber } from "lib/utils/normalize-manufacturer-part-number" -export const convertRawEasyEdaToTs = async (rawEasy: any) => { - const easyeda = EasyEdaJsonSchema.parse(rawEasy) - const soup = convertEasyEdaJsonToTscircuitSoupJson(easyeda, { - useModelCdn: true, - }) - const result = await convertToTypescriptComponent({ - easyeda, - soup, +export const convertRawEasyToTsx = async (rawEasy: any) => { + const betterEasy = EasyEdaJsonSchema.parse(rawEasy) + const result = await convertBetterEasyToTsx({ + betterEasy, }) return result } -export const convertToTypescriptComponent = async ({ - soup, - easyeda: easyEdaJson, +export const convertBetterEasyToTsx = async ({ + betterEasy, }: { - soup: AnyCircuitElement[] - easyeda: BetterEasyEdaJson + betterEasy: BetterEasyEdaJson }): Promise => { - const rawPn = easyEdaJson.dataStr.head.c_para["Manufacturer Part"] + const circuitJson = convertEasyEdaJsonToCircuitJson(betterEasy, { + useModelCdn: true, + shouldRecenter: true, + }) + const rawPn = betterEasy.dataStr.head.c_para["Manufacturer Part"] const pn = normalizeManufacturerPartNumber(rawPn) - const [cad_component] = su(soup as any).cad_component.list() + const [cad_component] = su(circuitJson).cad_component.list() // Derive pinLabels from easyeda json const pinLabels: Record = {} - easyEdaJson.dataStr.shape + betterEasy.dataStr.shape .filter((shape) => shape.type === "PIN") .forEach((pin) => { const isPinLabelNumeric = /^\d+$/.test(pin.label) @@ -43,7 +44,7 @@ export const convertToTypescriptComponent = async ({ }) // Derive schPinArrangement from easyeda json - const pins = easyEdaJson.dataStr.shape.filter((shape) => shape.type === "PIN") + const pins = betterEasy.dataStr.shape.filter((shape) => shape.type === "PIN") const leftPins = pins.filter((pin) => pin.rotation === 180) const rightPins = pins.filter((pin) => pin.rotation === 0) @@ -73,7 +74,7 @@ export const convertToTypescriptComponent = async ({ pinLabels, schPinArrangement, objUrl: modelObjUrl, - easyEdaJson, + circuitJson, }) } diff --git a/lib/convert-to-typescript-component/soup-typescript-component-template.ts b/lib/convert-to-typescript-component/soup-typescript-component-template.ts index 2759223..6e8d9a5 100644 --- a/lib/convert-to-typescript-component/soup-typescript-component-template.ts +++ b/lib/convert-to-typescript-component/soup-typescript-component-template.ts @@ -1,15 +1,16 @@ -import type { AnySoupElement } from "circuit-json" +import type { AnyCircuitElement, AnySoupElement } from "circuit-json" import type { ChipProps } from "@tscircuit/props" import { su } from "@tscircuit/soup-util" import type { BetterEasyEdaJson } from "../schemas/easy-eda-json-schema" import { generateFootprintTsx } from "../generate-footprint-tsx" +import { convertEasyEdaJsonToCircuitJson } from "lib/convert-easyeda-json-to-tscircuit-soup-json" interface Params { pinLabels: ChipProps["pinLabels"] componentName: string schPinArrangement: ChipProps["schPortArrangement"] objUrl?: string - easyEdaJson: BetterEasyEdaJson + circuitJson: AnyCircuitElement[] } export const soupTypescriptComponentTemplate = ({ @@ -17,9 +18,9 @@ export const soupTypescriptComponentTemplate = ({ componentName, schPinArrangement, objUrl, - easyEdaJson, + circuitJson, }: Params) => { - const footprintTsx = generateFootprintTsx(easyEdaJson) + const footprintTsx = generateFootprintTsx(circuitJson) return ` import { createUseComponent } from "@tscircuit/core" import type { CommonLayoutProps } from "@tscircuit/props" @@ -39,7 +40,8 @@ export const ${componentName} = (props: Props) => { ${ objUrl ? `cadModel={{ - objUrl: "${objUrl}" + objUrl: "${objUrl}", + rotationOffset: { x: 0, y: 0, z: 0 } }}` : "" } diff --git a/lib/generate-footprint-tsx.ts b/lib/generate-footprint-tsx.ts index 4ca4f83..a9cf01f 100644 --- a/lib/generate-footprint-tsx.ts +++ b/lib/generate-footprint-tsx.ts @@ -1,53 +1,70 @@ import { z } from "zod" import type { BetterEasyEdaJson } from "./schemas/easy-eda-json-schema" -import { PadSchema } from "./schemas/package-detail-shape-schema" +import type { + HoleSchema, + PadSchema, +} from "./schemas/package-detail-shape-schema" import { computeCenterOffset } from "./compute-center-offset" -import { mm } from "@tscircuit/mm" +import { mm, mmStr } from "@tscircuit/mm" +import { convertEasyEdaJsonToTscircuitSoupJson } from "./convert-easyeda-json-to-tscircuit-soup-json" +import type { AnyCircuitElement } from "circuit-json" +import { su } from "@tscircuit/soup-util" export const generateFootprintTsx = ( - easyEdaJson: BetterEasyEdaJson, + circuitJson: AnyCircuitElement[], ): string => { - const pads = easyEdaJson.packageDetail.dataStr.shape.filter( - (shape): shape is z.infer => shape.type === "PAD", - ) + const holes = su(circuitJson).pcb_hole.list() + const platedHoles = su(circuitJson).pcb_plated_hole.list() + const smtPads = su(circuitJson).pcb_smtpad.list() + const silkscreenPaths = su(circuitJson).pcb_silkscreen_path.list() - const centerOffset = computeCenterOffset(easyEdaJson) - const centerX = centerOffset.x - const centerY = centerOffset.y + const elementStrings: string[] = [] - const footprintElements = pads.map((pad) => { - const { center, width, height, holeRadius, number } = pad - const isPlatedHole = holeRadius !== undefined && mm(holeRadius) > 0 + for (const hole of holes) { + if (hole.hole_shape === "circle") { + elementStrings.push( + ``, + ) + } else if (hole.hole_shape === "oval") { + console.warn("Unhandled oval hole in conversion (needs implementation)") + } + } - // Normalize the position by subtracting the center point - const normalizedX = mm(center.x) - centerX - const normalizedY = mm(center.y) - centerY + for (const platedHole of platedHoles) { + if (platedHole.shape === "oval") { + elementStrings.push( + ``, + ) + } else if (platedHole.shape === "circle") { + elementStrings.push( + ``, + ) + } else if (platedHole.shape === "pill") { + console.warn("Unhandled pill hole in conversion (needs implementation)") + } + } - if (isPlatedHole) { - return ` - `.replace(/\n/, "") - } else { - return ` - `.replace(/\n/, "") + for (const smtPad of smtPads) { + if (smtPad.shape === "circle") { + elementStrings.push( + ``, + ) + } else if (smtPad.shape === "rect") { + elementStrings.push( + ``, + ) } - }) + } + + for (const silkscreenPath of silkscreenPaths) { + elementStrings.push( + ``, + ) + } return ` - ${footprintElements.join("\n")} + ${elementStrings.join("\n")} `.trim() } diff --git a/lib/index.ts b/lib/index.ts index 9501f9c..e49ecd7 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,6 @@ export * from "./convert-easyeda-json-to-tscircuit-soup-json" export * from "./fetch-easyeda-json" -export { convertRawEasyEdaToTs } from "./convert-to-typescript-component" +export { convertRawEasyToTsx as convertRawEasyEdaToTs } from "./convert-to-typescript-component" export { normalizeManufacturerPartNumber } from "./utils/normalize-manufacturer-part-number" export * from "./schemas/easy-eda-json-schema" export { convertEasyEdaJsonToVariousFormats } from "./convert-easyeda-json-to-various-formats" diff --git a/package.json b/package.json index 9b725cb..004aea0 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "dependencies": { "@tscircuit/mm": "^0.0.8", "commander": "^12.1.0", - "perfect-cli": "^1.0.20", "transformation-matrix": "^2.16.1", "zod": "^3.23.8" } diff --git a/tests/convert-to-soup-tests/convert-usb-c-to-soup.test.ts b/tests/convert-to-soup-tests/convert-usb-c-to-soup.test.ts index 575cef5..6e0a0c9 100644 --- a/tests/convert-to-soup-tests/convert-usb-c-to-soup.test.ts +++ b/tests/convert-to-soup-tests/convert-usb-c-to-soup.test.ts @@ -6,13 +6,11 @@ import { convertEasyEdaJsonToCircuitJson } from "lib/convert-easyeda-json-to-tsc import type { AnySoupElement } from "circuit-json" import { su } from "@tscircuit/soup-util" -it("should convert a usb-c footprint to tscircuit soup json", async () => { +it.skip("should convert a usb-c footprint to tscircuit soup json", async () => { const parsedJson = EasyEdaJsonSchema.parse(usbCEasyEdaJson) const soupElements = convertEasyEdaJsonToCircuitJson(parsedJson) as any expect(su(soupElements).pcb_component.list()[0]).toBeTruthy() expect(su(soupElements).cad_component.list().length).toBe(1) - - await logSoup("easyeda usb-c to soup", soupElements as AnySoupElement[]) }) diff --git a/tests/convert-to-ts/C128415-to-ts.test.ts b/tests/convert-to-ts/C128415-to-ts.test.ts index 556d14a..0d539f4 100644 --- a/tests/convert-to-ts/C128415-to-ts.test.ts +++ b/tests/convert-to-ts/C128415-to-ts.test.ts @@ -1,17 +1,13 @@ import { it, expect } from "bun:test" import timerRawEasy from "../assets/C128415.raweasy.json" -import { convertToTypescriptComponent } from "lib/convert-to-typescript-component" +import { convertBetterEasyToTsx } from "lib/convert-to-typescript-component" import { EasyEdaJsonSchema } from "lib/schemas/easy-eda-json-schema" import { convertEasyEdaJsonToCircuitJson } from "lib" it("should convert 555timer into typescript file", async () => { - const easyeda = EasyEdaJsonSchema.parse(timerRawEasy) - const soup = convertEasyEdaJsonToCircuitJson(easyeda, { - useModelCdn: true, + const betterEasy = EasyEdaJsonSchema.parse(timerRawEasy) + const result = await convertBetterEasyToTsx({ + betterEasy, }) - const result = await convertToTypescriptComponent({ - easyeda, - soup, - }) - console.log(result) + // TODO snapshot }) diff --git a/tests/convert-to-ts/C88224-to-ts.test.ts b/tests/convert-to-ts/C88224-to-ts.test.ts index ffa3b15..f247d88 100644 --- a/tests/convert-to-ts/C88224-to-ts.test.ts +++ b/tests/convert-to-ts/C88224-to-ts.test.ts @@ -1,17 +1,13 @@ import { it, expect } from "bun:test" import chipRawEasy from "../assets/C88224.raweasy.json" -import { convertToTypescriptComponent } from "lib/convert-to-typescript-component" +import { convertBetterEasyToTsx } from "lib/convert-to-typescript-component" import { EasyEdaJsonSchema } from "lib/schemas/easy-eda-json-schema" import { convertEasyEdaJsonToCircuitJson } from "lib" it("should convert c88224 into typescript file", async () => { - const easyeda = EasyEdaJsonSchema.parse(chipRawEasy) - const soup = convertEasyEdaJsonToCircuitJson(easyeda, { - useModelCdn: true, - }) - const result = await convertToTypescriptComponent({ - easyeda, - soup, + const betterEasy = EasyEdaJsonSchema.parse(chipRawEasy) + const result = await convertBetterEasyToTsx({ + betterEasy, }) console.log(result)