From 63d7f9ef43c495dc63131d7d622630f5962f2f93 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 12 Oct 2024 15:39:23 -0400 Subject: [PATCH 1/4] hole fixes --- bun.lockb | Bin 81735 -> 83511 bytes cli/main.ts | 14 +- package.json | 2 + tests/assets/C5184526.raweasy.json | 214 +++++++++++++++++++ tests/convert-to-soup-tests/c5184526.test.ts | 10 + 5 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 tests/assets/C5184526.raweasy.json create mode 100644 tests/convert-to-soup-tests/c5184526.test.ts diff --git a/bun.lockb b/bun.lockb index 20e71cc170046e21457b6e275c271bca8bba9cf8..e545035578a0f7cabe9494a268b782b25b10c63b 100755 GIT binary patch delta 15006 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 duRl?uCea4x5U||UMW|h#@TYUze*;7&kO2Sy delta 14126 zcmeHOd0bWHy54Ka1~$qdgKTsQD2RfAu$e_RsD#6I#B_@@G6*Cp;Di&XrJ&;UI!~FV zCOIBT!=W7Vq?smKre+pdCn2@8G&P6&ylZgV@7THB+r9UX`#bB`v!3ty=J$KwZ>_!X zYwtYmhmUO*h6GmV-nsO4m#$A%4R9*0x^`px?%8+VZZbP^`W+9yW%;FFPS|S_G<*wW+${CYZSU3cl z$9b&PXc~d^X4q*o&dB&2>;%3p7nF>#*Jxaz)4@EzAk^j#+Ti8@w!@A2A<7$o zS3r1z%fUR+L#W3*9XD=&2F&$;-%s&BccgY;k5)V(wWG_J7*8k=ncT6r!V|J{(z9W4 zPGRQ6OzSKYc!7(seq1mgn%z?f z*YJ$f;XCF%V0Po!f`};er5Q7BR8jiqjLfab;^uI%DfUqX?ZlTUT|?0X3P38I%PxcFc|CM1!?IBLCrL1&i??+ z9%+Qha`_(AXHVo521{Xg3i(`bGxD*9C0S{OWAm|41)1XtIc8Iu$>Y1hD4x$yFq~9e zvP@}^ky9Y`)Ed_F06Q=-9srI_PeVtIuv%<@4$OunD;p2%0Yd#Jqo(?%8LrHxI`PESk6a%nVe;c@m@pD>!E^)5~|%Imrb%mOd8gS0m*5HDLBnM=%^%GAvT|$38F*7l8f2{uu{m zt9BwE;}w^LN6Q`hDeMmB4jQ0hWAKI;xq}5@ZrCAKo@fa)*Bb}siTk&e2XF>s)0Er< z^V9D$FmJJqaq>DJgJuJam}oAY*Cm=<}%c43azfl%dnnT zildMtOI^tzXr5?6W=7h?oY95YS<(swwU^sFgLzB-nyAs}TOd(`1UvLNm}j~h%n`Ga zD}ZMvQdc|g%7u0pY=sS!rov@w4oqZli1saazU$Yr~*`NLv}-cPL#B@M(O{7s1$mgn z@9ZcS6lhNsAWX2%!=yW6FW0oEe2-||EUY)Tv!mVa(YljRSo0EF3PV{#tT8JsE3vx+ z1$mluJCWHBnf0UwTEqdIg|4}IM@8B2%(U~NI#94joOr1|D6Da`E&_4S zHEpCm3ls(0oTGIYp|HbBM9TM%u}8Scg6AC%l`IvpS_sua>XOx2D8xGt;1;cQKpeNF zV7E9i$%QHmCh>C@ssjZzq@boI-9m(aSGh8pUV`Fo1)iJe>q>Q~lIQxY*~g)}p^A;< z40i-EM-m!f`IDe{WRZ5eM(d_P@vLAPynf1!a(ztNJJ@yuDbFWP+Y8~GDy6m}CFj{7 zr29yD7NiDCskf2Jky1?&=xI`F8d7rU7f6ke@;YE!WJ;+mNDY-zw%Dt3DibNW#gV6Z z&9KAd8B9XT!Y#z=CRE{P((14w<0#KLPM3s~>{wpgSD<=4wf+kz4iBufd$j1$lt0oTerrZFNc+#6H0!%s= zygGRVX+^bNp(1EIW>tz*JLDr2-QaPkBq%BDbw0jw8+n7ILUDk}fw>$i3d%+bu+O2m z7g357w`Lkm0^M+q6EmApP#^-f8RddbH=~L`lg_8PybuE2@`~1tf>I*IGg|jHRD#qI z_OkA0s2C_L3xXm7FH<%FLt+6(LGg4jB=*oYC|(at4rT=6^~{S4V^MPgREMWEtDq90 z5KQRvzM|xsv3NjopIFrZw1Z-!qO@kH9$Jk9zFDY<^@V0CW!NOhHZ+mDnyqk17~y`D%(6A?lv zC?vrilGhr3FhuJd+h{cCk^O+Rj)jsvmXD2F{yTLHDhV~^$EOavot5mrAyEAE5TytE zo1s(@X%fEhL; z_DH0t{c4{;AsCR2SM7kO+3!Ggqu}^5f4UL^Tb+e|0JGcs@d5 zz3|4`8BcYnau=CsE-$Ir)(5UrHdKALNs_5NY}Gm4>{P} z8g7s6D={{zbUnkxPcv+hLRf1=0C6Z?&tvWo;V5;8(337^ZilduE@tiz>#yO7Bm&&7 zJ-`<;=O^K}sdOQro-$W_5nxW1a%JWQU6g!mTIpiu{O+=RF7w2CD&@>vA7Ll;-{&Qz zppTNF>gO>xMC3>pGk2US%ipm;dC8u|JpCa`>9foZ9HZp_FEVz}e{&!kv>Pk6em>h$ zUMHVoEo!jycoC*6wVB!M84Ax-_!Tf;%v}De!n449F>^nq0GF2md@*zWT!8cE0epew zxZw;gDCb;U&$14BnUwo`=K9N({C~w@bxTnKi8yOnOXlJX{*+4+!7b_UwjWq{qTE< zSME=|hKi-7i$G;I{;$7>&DQ3I(%|3k zAuI%(A?=Xq%Azo{cV;L&Q{h*@d@*zRs|wEo^To_N?ceXAKm8u#rRVEUH$Yup`hUNN z{(roO_=)+K-$RwLDfm0DM_)6&*~gLc`kDnTRrR${fBfaKX+N`IM>+j0WZ&13PC@C& z%VME-p{82QLVY>`mEF&gg8Q2VM=I)XA-%bn1PP;2&xHn8Em0z zP}>Kag{E{LYQ-Q&8a%`-_|VoN7V13MksODbg=UmG)I#^54np~nZkUDM90L1>nS~Zq z1=W8j>`OBXEh#4r_6>u5P=Vw%9QHv?9c~s{(+Q~TG}xDJ7J{iL9rg`}eNb&EFa!2M zm1mfRFuDjeF&*}0nuTyG%Y=Oyun#JdVzXc$)Y>ew5KXmErJ1mAgjtBCl_OwZ7VLwH zqb}L74{Ce1S%|0mP%B2jz8tfVKwEQQUpDN^H4E)2HP<3^pxwB4B;CsvA&Js(f03$i zPo{<=EkY;C!M!sb#=Q%9q1t>{G6t3un1xhYSpZA&VF}bg>QV?xptcv9 zg~4r zYGM&QHQ6i_QrTp9Y7#sJHI8DZz*A6br@i!g`sai2>kaGyubUa<(}RD}C{s>Xc*1-@z#7SeRw7tuxB7gN|Qi$GL{ z`x2_beJRDxwg}5;5$?;Wb~bF71sh7u!b)0M3L9p_2B_84r3^lT+FoWB*3x~b6{Yae z9JBBmZJh%jmBB}I&BE)HIu|~IItaCqbo1b&Iq=auv#^P(p!&~+kIK!$7Ro7ykLJNg zP+Q4sK70f|1UYj!_j<|7Eamg<1HJa#p~; z<**Ox1bMB5eNa_)ES#dERTkkiRpb5%1+KOTXJ|U^)pQZ}vlO<* zBAlZ#+&`t7HI;O4?F-^mkpkA87FTO4d%T`3RJy!oBhWA>8;AFu(|LHnK? zzEmoEzur+Gm)A15N@cG%E{NKJ0r;m%DIaa8NNwq-t@hfh_)+gjyS5 z7ybq*T|95@nE&X|5#Ubv!<6ee049Lze-3b7eINngybA#5W%Cy;u8+AAE(fZ)>_h!8 zhW6$ctS6ucynx0)6Tkp81^CO5m*pdXm*p662;gsXM*yzlf9>Ue66Sv{*0|EKi+*1B zkh%}B;k-zPfg`{HD!=5};vJ;+0DA%cakm0k39JHE18ab_^x~yXgUgYc4=ex{0*ipf z00B#YrNCHV955c3089jmfH6QmFa#I|qyfW$bRYxBq}ofZi&K&E0h$5L0YAVWXaPI~ z9sy4P_6&Q1-Fp<+4@?KD0RFAN9Ow!30(t}d=c7r$WMCRF02l}i0$u|80{u7+I7VIo zf`JgA4G;<-)-^4GRset2Uj$zQz5v*pp8+#~K0pdE3z!WI1}s1%5Cwz-VL%|jf2X(t zdY{>&A=PLYrtB- z7T~t^01-f+(jPchgI57d01lK%00%;UfLDc=o>wdpU;}xR*wOSa-HLg=*vy823s4_$ z0Ca#8;0!bX)Szddc>#?8H^2jM1>6uqHUh5$8x-CIUJHl-*Lj~^SAoP9CF4!- zR^Tl_?Pw>`+ki@7yTY8e1Na-T8`!02&U+Ww3%moo4RHA$h4(331%3}Wzge0e)ukUMo$oEmTD2x#4>u-Gx>T%GZRXY~GF=^yvQ5X^y zVhl0jktvelJH5C-q^R$@i3#=S6;Qu=bQUzC9{K)5AEllN-E?K!@fpO z;USnJ9-CVF=^sXedcd@z!THBeI(`;morJqBnZDPHLu{%4_dyOLY~>SJGwf*I_j-eR z{M6F>)9!>ATN=@Obo z%2V>u$;ZF$J(!$*r2mplqF|34PYs`9r5&|;Lk~zN$c)sZN7gy7*NQ?cay*bTNJoRN z=?z7Yj*wvvw|)ppcDE6QOyuCPiDRj>@R~mAIfF-Fa5!E)DBHAojo%?%^D0a$3TuuM zI|sUgrs_f0>lp+3e%bC&5-P;V71Xn|+eU}IJ@RJyI+TRVC7AaMn74YUc6h#jd}4j) z7*sII{ybBkhSln$4q=w{G0Prpj%@Jy=%~9ixHPTjOgK_*Dz8bDda!qZu;z>8Utb#| zHI-sTJ&~LG_o$S*uJ^B_Box63i^?77VXa=2dl0>yD6~#520BsKI-|JDiKf+c6K6Wp z$8~yxdbqac@u?lHA{LhB-gNn2G}Llx@W^lqhl6$d|UE zhf}`v^^bZnr5Qc=v0KzX56`1V;wh`19-eI%xUT!gzimNB7$77Tug~T*u1751*6LB>_fm3>ijU7N zL>=COC{Yg^pR}(H=-T6>kFEVa@}q9I^ak~G@}_0$u4{dVwU;VLk=w|hN^W%%aftWy zEq#=FruVpAujR?t@{ZUDSl%$U@o0d2NHoQCrnRAI!!6d5iveV~tv9I0gx56*-FL1w zeSo!s*phnP)<;PPixD-Bo=LUqc8vefT0uQ}d_;4zQ$VelXe~)-N$b#5J*)i1%&g?F zWtR?HE2yWLH*B4jd(*zfS!>C}mUJD{Qjb4ZzwXlXgI2XYtra%4B;TL)QR;c=k;67T zIlrh14{_CG^+fgXMh|t)KJIs|C0AS0C^S`%V3*o#%O2XW?L=z@^^i8jUH8i^UsY-? zF|?usKkE(ZsqW)!HPRKRHAd?8SZ`2IWiM(opgiE6_*e{Y#BscS7w$f<0{^t;Yj-#e zyKBT>r*=LHH^_&bH6^1TZK)i8W_nK-L9Ny?g1))i4S#f?&|mci^%VEDH|IE?KW~c% zeRYKVz8n@w8NYTjD95%-QXD+8O5fouX+E&jSZ|Lgh$gF)KjX< z6s4XI?^Ts^`)DKBcG16C2N&JWDFMctj{RL~uf*2a}Kp&+ZJ^y}xUfV?vCe5%` z_~SWrh^6-){HsYix25&>^-+J^gMVB3_r-+O+m_v0Jh+o})MIVwCYq`z+8Z=@VRxr# zjclzI)HCl*<`yNz$KN$tOE$()y9eqLin4u%KORY5YVkic=6T(c>9@uRyRW`!?Vtu( zSv#5!Qv@A|nMxctV6k-v+=Bfl99>XG}&`nU%Cc%rCe@+C(lgmTB)U8(& z^`!m{i|773rnos=4UhXUqlSm|pn``nqHjEHlN#)N`Ctn|Ns?_y|=Ays@81X`pH>SABNLi1KQ8N?er}DN-jUo@9#7lqsLBrSpT0fBTCzKlPCwR19Y+N(R5-Y;bArJVgTE!JO>2}+zq=p)^0$8V zL~F^fiDY=9H>m#y!M;_Q`c6x7k6J4PwWnTB^!4xE#|&`2`#?j5PukPFPeR3I0@Xe7 zZhi$HeQp@`qo0?@Pdi-n<1J|XFf92sdg124&0gUTgrz}(ciS3#1o4l>FZxb?!}6A| z*LCOy(9=EpIz4fZxN;BL1^QU*&^`&{+kSrSfjr_))6yA2", "JLCPCB part number") .option("-o, --output ", "Output filename") .option( - "-t, --type ", - "Output type: soup.json, kicad_mod, raweasy.json, bettereasy.json, tsx", + "-t, --format-type ", + "Output format (can be inferred from filename)", ) .action(async (options) => { await convertEasyEdaJsonToVariousFormats({ jlcpcbPartNumberOrFilepath: options.input, outputFilename: options.output, - formatType: options.type, + formatType: options.format, }) }) @@ -56,4 +57,5 @@ program } }) -program.parse(process.argv) +// program.parse(process.argv) +perfectCli(program, process.argv) diff --git a/package.json b/package.json index 1033388..15dac24 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "scripts": { "test": "bun test", + "cli": "bun cli/main.ts", "build": "tsup lib/index.ts cli/main.ts --format esm --dts --sourcemap", "aider": "aider", "format:check": "biome format .", @@ -36,6 +37,7 @@ "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/assets/C5184526.raweasy.json b/tests/assets/C5184526.raweasy.json new file mode 100644 index 0000000..0e48e01 --- /dev/null +++ b/tests/assets/C5184526.raweasy.json @@ -0,0 +1,214 @@ +{ + "uuid": "26ac871aee7a4533ae5e5f32470bc4cf", + "title": "CPG151101S11-1", + "description": "", + "docType": 2, + "type": 3, + "thumb": "//image.lceda.cn/components/26ac871aee7a4533ae5e5f32470bc4cf.png", + "lcsc": { + "id": 5849986, + "number": "C5184526" + }, + "szlcsc": { + "id": 5849986, + "number": "C5184526" + }, + "owner": { + "uuid": "0819f05c4eef4c71ace90d822a990e87", + "username": "lcsc", + "nickname": "LCSC", + "avatar": "//image.lceda.cn/avatars/2018/6/kFlrasi7W06gTdBLAqW3fkrqbDhbowynuSzkjqso.png" + }, + "tags": ["Mechanical Keyboard Shaft"], + "updateTime": 1724985601, + "updated_at": "2024-10-10 21:18:53", + "dataStr": { + "head": { + "docType": "2", + "editorVersion": "6.5.46", + "x": 400, + "y": 300, + "c_para": { + "pre": "U?", + "name": "CPG151101S11-1", + "package": "TH_L10.9-W5.9-CPG151101S11", + "Supplier": "LCSC", + "Supplier Part": "C5184526", + "Manufacturer": "Kailh(凯华)", + "Manufacturer Part": "CPG151101S11-1", + "JLCPCB Part Class": "Extended Part", + "Contributor": "lcsc" + }, + "puuid": "a76c392969e247e3a74f5644711fdc03", + "utime": 1724124169, + "uuid": "26ac871aee7a4533ae5e5f32470bc4cf", + "importFlag": 0, + "c_spiceCmd": null, + "pre": "U?", + "name": "CPG151101S11-1", + "hasIdFlag": true + }, + "canvas": "CA~1000~1000~#FFFFFF~yes~#CCCCCC~5~1000~1000~line~5~pixel~5~400~300", + "shape": [ + "R~390~285~2~2~20~30~#880000~1~0~none~gge1~0~", + "E~395~290~1.5~1.5~#880000~1~0~#880000~gge2~0", + "P~show~0~1~380~295~180~gge5~0^^380~295^^M380,295h10~#880000^^1~393.7~299~0~1~start~~~#0000FF^^1~389.5~294~0~1~end~~~#0000FF^^0~387~295^^0~M 390 298 L 393 295 L 390 292", + "P~show~0~2~380~305~180~gge6~0^^380~305^^M380,305h10~#880000^^1~393.7~309~0~2~start~~~#0000FF^^1~389.5~304~0~2~end~~~#0000FF^^0~387~305^^0~M 390 308 L 393 305 L 390 302" + ], + "BBox": { + "x": 379, + "y": 284.6, + "width": 31, + "height": 30.4 + }, + "colors": [] + }, + "jlcOnSale": 1, + "datastrid": "bbbf73e6790646c6ba4967101b261535", + "verify": true, + "SMT": true, + "writable": false, + "isFavorite": false, + "packageDetail": { + "uuid": "a76c392969e247e3a74f5644711fdc03", + "title": "TH_L10.9-W5.9-CPG151101S11", + "docType": 4, + "updateTime": 1654592158, + "owner": { + "uuid": "0819f05c4eef4c71ace90d822a990e87", + "username": "lcsc", + "nickname": "LCSC", + "avatar": "//image.lceda.cn/avatars/2018/6/kFlrasi7W06gTdBLAqW3fkrqbDhbowynuSzkjqso.png" + }, + "datastrid": "7b6da15f28a549fa9d7511f036981073", + "writable": false, + "dataStr": { + "head": { + "docType": "4", + "editorVersion": "6.5.5", + "newgId": true, + "c_para": { + "package": "TH_L10.9-W5.9-CPG151101S11", + "pre": "U?", + "Contributor": "lcsc", + "link": "https://www.kailhswitch.com/uploads/201815927/PG151101S11.pdf?rnd=890", + "3DModel": "TH_L10.9-W5.9-CPG151101S11" + }, + "hasIdFlag": true, + "x": 4000, + "y": 3000, + "utime": 1654591488, + "uuid": "a76c392969e247e3a74f5644711fdc03", + "importFlag": 0, + "transformList": "", + "uuid_3d": "f3be2e21060d495bbc39b32091bd6ad1" + }, + "canvas": "CA~1000~1000~#000000~yes~#FFFFFF~0.393701~1000~1000~line~0.393701~mm~1~ARC~visible~0.5~4000~3000~0~none", + "shape": [ + "CIRCLE~3971.4567~2988.4055~0.1181~0.2362~101~gge175~0~~", + "SOLIDREGION~100~~M 3971.4567 2998.3071 L 3971.4567 2991.6929 L 3978.7402 2991.6929 L 3978.7402 2998.3071 Z ~solid~gge85~~~~0", + "SOLIDREGION~100~~M 4021.2598 3008.3071 L 4021.2598 3001.6929 L 4028.5433 3001.6929 L 4028.5433 3008.3071 Z ~solid~gge87~~~~0", + "SOLIDREGION~99~~M 3978.5432 2988.4055 L 3978.5432 3011.5945 L 4021.4568 3011.5945 L 4021.4568 2995.8464 L 4000.7205 2995.8464 L 4000.7204 2995.8465 A 7.4409 7.4409 0 0 1 3993.2795 2988.4056 L 3993.2795 2988.4055 L 3978.5432 2988.4055 Z ~solid~gge94~~~~0", + "SOLIDREGION~5~~M 4030.4133 2998.8976 L 4021.7519 2998.8976 L 4020.9645 2999.685 L 4020.9645 3010.3149 L 4021.7519 3011.1023 L 4031.2007 3011.1023 L 4031.9881 3010.3149 L 4031.9881 2999.685 L 4031.2007 2998.8976 Z ~solid~gge160~~~~0", + "SOLIDREGION~5~~M 3977.4613 2988.8976 L 3968.7999 2988.8976 L 3968.0125 2989.685 L 3968.0125 3000.3149 L 3968.7999 3001.1023 L 3978.2487 3001.1023 L 3979.0361 3000.3149 L 3979.0361 2989.685 L 3978.2487 2988.8976 Z ~solid~gge164~~~~0", + "TRACK~1~3~~3978.5 3001.8155 3978.5 3011.5 3978.5 3011.5~gge153~0", + "TRACK~1~3~~4021.4567 3011.6142 3978.5 3011.6142~gge159~0", + "TRACK~1~3~~4000.7204 2995.8465 4021.4568 2995.8465~gge93~0", + "TRACK~1~3~~4021.4568 2995.8465 4021.4568 2998.1845~gge157~0", + "TRACK~1~3~~3988.409 2988.4055 3978.5 2988.4055~gge151~0", + "TRACK~1~3~~3993.2795 2988.4055 3988.409 2988.4055~gge91~0", + "ARC~1~3~~M3993.2795,2988.4055 A7.4409,7.4409 0 0 0 4000.7204,2995.8465~~gge89~0", + "PAD~RECT~4026.476~3005~11.4173~9.8425~1~~2~0~4020.7674 3000.0787 4032.1847 3000.0787 4032.1847 3009.9213 4020.7674 3009.9213~0~gge25~0~~Y~0~-393.7008~0.1969~4026.4764,3005", + "PAD~RECT~3973.524~2995~11.4173~9.8425~1~~1~0~3967.8154 2990.0787 3979.2327 2990.0787 3979.2327 2999.9213 3967.8154 2999.9213~0~gge37~0~~Y~0~-393.7008~0.1969~3973.5236,2995", + "CIRCLE~4012.5~3005~2.854~5.7087~12~gge108~0~~", + "CIRCLE~3987.5~2995~2.854~5.7087~12~gge112~0~~", + "SOLIDREGION~12~~M 3978.7401 3000.3937 L 3978.7401 3004.3307 A 7.0866 7.0866 0 0 0 3985.8267 3011.4173 L 3986.2205 3011.4173 L 3987.4016 3011.4173 L 4021.2599 3011.4173 L 4021.2599 3010.6299 L 3986.2205 3010.6299 L 3985.8267 3010.6299 A 6.2992 6.2992 0 0 1 3979.5275 3004.3307L3979.5275 3000.3937 Z~solid~gge169~~~~0", + "SOLIDREGION~12~~M 4021.2599 2998.4252 L 4021.2599 2998.0316 A 1.9686 1.9686 0 0 0 4019.2913 2996.063 L 4000.7874 2996.063 L 4000 2996.063 A 7.0866 7.0866 0 0 1 3992.9134 2988.9764 L 3992.9134 2988.189 L 3978.7401 2988.189 L 3978.7401 2988.9764 L 3992.126 2988.9764 L 3992.126 2989.7638 A 7.0866 7.0866 0 0 0 3999.2126 2996.8504L4018.8977 2996.8504A1.5748 1.5748 0 0 1 4020.4725 2998.4252 Z~solid~gge172~~~~0", + "HOLE~4012.5~3005~5.9055~gge9~0", + "HOLE~3987.5~2995~5.9055~gge14~0", + "SVGNODE~{\"gId\":\"g1_outline\",\"nodeName\":\"g\",\"nodeType\":1,\"layerid\":\"19\",\"attrs\":{\"c_width\":\"57.0865\",\"c_height\":\"23.18893\",\"c_rotation\":\"0,0,0\",\"z\":\"-4.7244\",\"c_origin\":\"4000,3000\",\"uuid\":\"c886ec2b42464573a88fc1f647577a49\",\"c_etype\":\"outline3D\",\"id\":\"g1_outline\",\"title\":\"TH_L10.9-W5.9-CPG151101S11\",\"layerid\":\"19\",\"transform\":\"scale(1) translate(0, 0)\"},\"childNodes\":[{\"gId\":\"g1_outline_line0\",\"nodeName\":\"polyline\",\"nodeType\":1,\"attrs\":{\"fill\":\"none\",\"id\":\"g1_outline_line0\",\"c_shapetype\":\"line\",\"points\":\"3971.4568 2991.7126 3971.4568 2991.752 3971.4568 2991.8308 3971.4568 2991.9489 3971.4568 2992.1063 3971.4568 2992.1457 3971.4568 2992.1851 3971.4568 2992.2638 3971.4568 2992.3032 3971.4568 2992.3426 3971.4568 2992.5 3971.4568 2992.5394 3971.4568 2997.5 3971.4568 2997.5394 3971.4568 2997.5788 3971.4568 2997.6969 3971.4568 2997.7756 3971.4568 2997.8544 3971.4568 2997.8937 3971.4568 2997.9331 3971.4568 2998.0906 3971.4568 2998.2087 3971.4568 2998.2874 3971.4962 2998.3268 3971.5355 2998.3268 3971.6143 2998.3268 3971.693 2998.3268 3971.7718 2998.3268 3971.8505 2998.3268 3978.1891 2998.3268 3978.2678 2998.3268 3978.3466 2998.3268 3978.4253 2998.3268 3978.4647 2998.3268 3978.5434 2998.3268 3978.5434 2998.4449 3978.5434 2998.5237 3978.5434 2998.563 3978.5434 2998.6418 3978.5434 2998.7205 3978.5434 2998.7599 3978.5434 2999.9803 3978.5434 3003.7205 3978.5828 3004.7047 3978.6615 3005.0984 3978.7796 3005.6103 3979.0158 3006.437 3979.0552 3006.5158 3979.4489 3007.3819 3979.6064 3007.6575 3979.9214 3008.2087 3980.3938 3008.7992 3980.5119 3008.9567 3981.1812 3009.626 3981.378 3009.7835 3981.9292 3010.2165 3982.4804 3010.5315 3982.756 3010.7284 3983.1891 3010.9252 3983.6221 3011.1221 3983.7402 3011.1221 3984.5276 3011.3976 3985.0395 3011.4764 3985.4725 3011.5551 3986.4174 3011.5945 4021.063 3011.5945 4021.1811 3011.5945 4021.2599 3011.5551 4021.3386 3011.5158 4021.378 3011.4764 4021.4173 3011.3976 4021.4567 3011.3189 4021.4567 3011.2008 4021.4567 3008.7599 4021.4567 3008.7205 4021.4567 3008.6417 4021.4567 3008.563 4021.4567 3008.3268 4021.5354 3008.3268 4021.6142 3008.3268 4021.6929 3008.3268 4021.7323 3008.3268 4021.8504 3008.3268 4028.1496 3008.3268 4028.2677 3008.3268 4028.3465 3008.3268 4028.4252 3008.3268 4028.5433 3008.3268 4028.5433 3008.2874 4028.5433 3008.2087 4028.5433 3008.0906 4028.5433 3007.9331 4028.5433 3007.8937 4028.5433 3007.8543 4028.5433 3007.7756 4028.5433 3007.7362 4028.5433 3007.6969 4028.5433 3007.5394 4028.5433 3007.5 4028.5433 3002.5394 4028.5433 3002.5 4028.5433 3002.4607 4028.5433 3002.3425 4028.5433 3002.2638 4028.5433 3002.1851 4028.5433 3002.1457 4028.5433 3002.1063 4028.5433 3001.9488 4028.5433 3001.8307 4028.5433 3001.752 4028.5039 3001.7126 4028.4646 3001.7126 4028.3858 3001.7126 4028.3071 3001.7126 4028.2283 3001.7126 4028.1496 3001.7126 4021.811 3001.7126 4021.7323 3001.7126 4021.6536 3001.7126 4021.5748 3001.7126 4021.5354 3001.7126 4021.4567 3001.7126 4021.4567 3001.4764 4021.4567 3001.3977 4021.4567 3001.2796 4021.4567 2997.815 4021.4173 2997.3819 4021.2599 2996.9882 4021.0236 2996.5945 4020.8662 2996.437 4020.7087 2996.2796 4020.5512 2996.1615 4020.3543 2996.0433 4019.9213 2995.9252 4019.4882 2995.8465 4001.1418 2995.8465 4000.1575 2995.8071 3999.8032 2995.7284 3999.2127 2995.6496 3998.5434 2995.4134 3998.3071 2995.3741 3997.4016 2994.9804 3997.3229 2994.9016 3996.5749 2994.4685 3996.1812 2994.193 3995.8268 2993.878 3995.1969 2993.2874 3995.1182 2993.2087 3994.4883 2992.4607 3994.3701 2992.2638 3993.9764 2991.6733 3993.5434 2990.7678 3993.2284 2989.8623 3993.0709 2989.4686 3993.0316 2989.3898 3992.8347 2989.1142 3992.7166 2988.9961 3992.5591 2988.8386 3992.3229 2988.6812 3992.1654 2988.6024 3991.8505 2988.4843 3991.7717 2988.4843 3991.3387 2988.4056 3978.9371 2988.4056 3978.819 2988.4449 3978.7403 2988.4843 3978.6615 2988.5237 3978.5828 2988.6024 3978.5434 2988.7205 3978.5434 2988.7993 3978.5434 2991.2796 3978.5434 2991.3583 3978.5434 2991.3977 3978.5434 2991.5158 3978.5434 2991.5945 3978.5434 2991.7126 3978.4647 2991.7126 3978.3859 2991.7126 3978.3072 2991.7126 3978.2678 2991.7126 3978.1497 2991.7126 3971.8505 2991.7126 3971.7324 2991.7126 3971.6537 2991.7126 3971.5749 2991.7126 3971.4962 2991.7126 3971.4568 2991.7126 3971.4568 2991.7126\"}}]}" + ], + "layers": [ + "1~TopLayer~#FF0000~true~false~true~", + "2~BottomLayer~#0000FF~true~false~true~", + "3~TopSilkLayer~#FFCC00~true~false~true~", + "4~BottomSilkLayer~#66CC33~true~false~true~", + "5~TopPasteMaskLayer~#808080~true~false~true~", + "6~BottomPasteMaskLayer~#800000~true~false~true~", + "7~TopSolderMaskLayer~#800080~true~false~true~0.3", + "8~BottomSolderMaskLayer~#AA00FF~true~false~true~0.3", + "9~Ratlines~#6464FF~true~false~true~", + "10~BoardOutLine~#FF00FF~true~false~true~", + "11~Multi-Layer~#C0C0C0~true~false~true~", + "12~Document~#FFFFFF~true~true~true~", + "13~TopAssembly~#33CC99~true~false~true~", + "14~BottomAssembly~#5555FF~true~false~true~", + "15~Mechanical~#F022F0~true~false~true~", + "19~3DModel~#66CCFF~true~false~true~", + "21~Inner1~#999966~false~false~false~~", + "22~Inner2~#008000~false~false~false~~", + "23~Inner3~#00FF00~false~false~false~~", + "24~Inner4~#BC8E00~false~false~false~~", + "25~Inner5~#70DBFA~false~false~false~~", + "26~Inner6~#00CC66~false~false~false~~", + "27~Inner7~#9966FF~false~false~false~~", + "28~Inner8~#800080~false~false~false~~", + "29~Inner9~#008080~false~false~false~~", + "30~Inner10~#15935F~false~false~false~~", + "31~Inner11~#000080~false~false~false~~", + "32~Inner12~#00B400~false~false~false~~", + "33~Inner13~#2E4756~false~false~false~~", + "34~Inner14~#99842F~false~false~false~~", + "35~Inner15~#FFFFAA~false~false~false~~", + "36~Inner16~#99842F~false~false~false~~", + "37~Inner17~#2E4756~false~false~false~~", + "38~Inner18~#3535FF~false~false~false~~", + "39~Inner19~#8000BC~false~false~false~~", + "40~Inner20~#43AE5F~false~false~false~~", + "41~Inner21~#C3ECCE~false~false~false~~", + "42~Inner22~#728978~false~false~false~~", + "43~Inner23~#39503F~false~false~false~~", + "44~Inner24~#0C715D~false~false~false~~", + "45~Inner25~#5A8A80~false~false~false~~", + "46~Inner26~#2B937E~false~false~false~~", + "47~Inner27~#23999D~false~false~false~~", + "48~Inner28~#45B4E3~false~false~false~~", + "49~Inner29~#215DA1~false~false~false~~", + "50~Inner30~#4564D7~false~false~false~~", + "51~Inner31~#6969E9~false~false~false~~", + "52~Inner32~#9069E9~false~false~false~~", + "99~ComponentShapeLayer~#00CCCC~true~false~true~0.4", + "100~LeadShapeLayer~#CC9999~true~false~true~", + "101~ComponentMarkingLayer~#66FFCC~true~false~true~", + "Hole~Hole~#222222~false~false~true~", + "DRCError~DRCError~#FAD609~false~false~true~" + ], + "objects": [ + "All~true~false", + "Component~true~true", + "Prefix~true~true", + "Name~true~false", + "Track~true~true", + "Pad~true~true", + "Via~true~true", + "Hole~true~true", + "Copper_Area~true~true", + "Circle~true~true", + "Arc~true~true", + "Solid_Region~true~true", + "Text~true~true", + "Image~true~true", + "Rect~true~true", + "Dimension~true~true", + "Protractor~true~true" + ], + "BBox": { + "x": 3967.8, + "y": 2988.1, + "width": 64.4, + "height": 23.8 + }, + "netColors": [] + } + } +} diff --git a/tests/convert-to-soup-tests/c5184526.test.ts b/tests/convert-to-soup-tests/c5184526.test.ts new file mode 100644 index 0000000..a7bf09b --- /dev/null +++ b/tests/convert-to-soup-tests/c5184526.test.ts @@ -0,0 +1,10 @@ +import c5184526 from "tests/assets/C5184526.raweasy.json" +import { expect, test } from "bun:test" +import { convertEasyEdaJsonToCircuitJson, EasyEdaJsonSchema } from "lib/index" + +test("C5184526 should have two holes", () => { + const bettereasy = EasyEdaJsonSchema.parse(c5184526) + const circuitJson = convertEasyEdaJsonToCircuitJson(bettereasy) + + expect(circuitJson.filter((e) => e.type === "pcb_hole").length).toBe(2) +}) From 6848a0e873b02ea7ddef64a5e169d93739539eb9 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 12 Oct 2024 16:04:13 -0400 Subject: [PATCH 2/4] fix pcb hole --- bun.lockb | Bin 83511 -> 83511 bytes ...ert-easyeda-json-to-tscircuit-soup-json.ts | 22 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/bun.lockb b/bun.lockb index e545035578a0f7cabe9494a268b782b25b10c63b..68153284ceba8ec4362c14dc46c8fede2ce8487a 100755 GIT binary patch delta 23 fcmdnq!@9kPb;H$19E@>>dIow%2Al6bib@6mbz2Fo delta 21 dcmdnq!@9kPb;H$1OiT=u1AiND=6bA~3;=0P2|fS- diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts index 1287f8a..5dbebd6 100644 --- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts @@ -3,6 +3,7 @@ import { TrackSchema, ArcSchema, SVGNodeSchema, + HoleSchema, } from "./schemas/package-detail-shape-schema" import { z } from "zod" import type { BetterEasyEdaJson } from "./schemas/easy-eda-json-schema" @@ -19,6 +20,7 @@ import { pcb_smtpad, pcb_silkscreen_path, pcb_plated_hole, + pcb_hole, } from "circuit-json" import * as Soup from "circuit-json" import { generateArcFromSweep, generateArcPathWithMid } from "./math/arc-utils" @@ -62,6 +64,17 @@ const handleSilkscreenArc = (arc: z.infer, index: number) => { }) } +const handleHole = (hole: z.infer, index: number) => { + return pcb_hole.parse({ + type: "pcb_hole", + x: hole.center.x, + y: hole.center.y, + hole_diameter: hole.radius * 2, + hole_shape: "circle", + pcb_hole_id: `pcb_hole_${index + 1}`, + } as Soup.PcbHole) +} + interface Options { useModelCdn?: boolean shouldRecenter?: boolean @@ -163,6 +176,15 @@ export const convertEasyEdaJsonToCircuitJson = ( } }) + // Add holes + easyEdaJson.packageDetail.dataStr.shape + .filter( + (shape): shape is z.infer => shape.type === "HOLE", + ) + .forEach((h, index) => { + soupElements.push(handleHole(h, index)) + }) + // Add silkscreen paths and arcs easyEdaJson.packageDetail.dataStr.shape.forEach((shape, index) => { if (shape.type === "TRACK") { From c9a0a36149cedadc51b56a4ab5d4f1f72d9bf7f9 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 12 Oct 2024 16:40:16 -0400 Subject: [PATCH 3/4] snapshot testing, huge fix for units --- bun.lockb | Bin 83511 -> 115986 bytes bunfig.toml | 2 + ...ert-easyeda-json-to-tscircuit-soup-json.ts | 84 +++--- package.json | 2 + .../__snapshots__/c5184526.diff.png | Bin 0 -> 3056 bytes .../__snapshots__/c5184526.snap.svg | 13 + tests/convert-to-soup-tests/c5184526.test.ts | 7 + tests/fixtures/preload.ts | 1 + tmp.json | 263 ++++++++++++++++++ 9 files changed, 341 insertions(+), 31 deletions(-) create mode 100644 bunfig.toml create mode 100644 tests/convert-to-soup-tests/__snapshots__/c5184526.diff.png create mode 100644 tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg create mode 100644 tests/fixtures/preload.ts create mode 100644 tmp.json diff --git a/bun.lockb b/bun.lockb index 68153284ceba8ec4362c14dc46c8fede2ce8487a..9a62973038b5e158864fc0349a5ff4282d0e62cb 100755 GIT binary patch delta 32359 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 delta 12629 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 diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..cd844d0 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = "tests/fixtures/preload.ts" \ No newline at end of file diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts index 5dbebd6..d05e79a 100644 --- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts @@ -1,11 +1,11 @@ -import { +import type { PadSchema, TrackSchema, ArcSchema, SVGNodeSchema, HoleSchema, } from "./schemas/package-detail-shape-schema" -import { z } from "zod" +import type { z } from "zod" import type { BetterEasyEdaJson } from "./schemas/easy-eda-json-schema" import type { AnySoupElement, @@ -29,6 +29,22 @@ import { scale, translate } from "transformation-matrix" import { computeCenterOffset } from "./compute-center-offset" import { mm } from "@tscircuit/mm" +const mil2mm = (mil: number | string) => { + if (typeof mil === "number") return mm(`${mil}mil`) + if (mil.match(/^\d+$/)) return mm(`${mil}mil`) + return mm(mil) +} +/** + * Some components, like paths and "HOLE", seem to use mil*10 as + * their unlabeled unit + */ +const milx10 = (mil10: number | string) => { + if (typeof mil10 === "number") return mil2mm(mil10) * 10 + if (mil10.match(/^\d+$/)) return mil2mm(mil10) * 10 + // If it has a unit, return the specified unit ignoring the multiplier + return mil2mm(mil10) +} + const handleSilkscreenPath = ( track: z.infer, index: number, @@ -38,7 +54,10 @@ const handleSilkscreenPath = ( pcb_silkscreen_path_id: `pcb_silkscreen_path_${index + 1}`, pcb_component_id: "pcb_component_1", layer: "top", // Assuming all silkscreen is on top layer - route: track.points.map((point) => ({ x: point.x, y: point.y })), + route: track.points.map((point) => ({ + x: milx10(point.x), + y: milx10(point.y), + })), stroke_width: track.width, }) } @@ -59,17 +78,20 @@ const handleSilkscreenArc = (arc: z.infer, index: number) => { pcb_silkscreen_path_id: `pcb_silkscreen_arc_${index + 1}`, pcb_component_id: "pcb_component_1", layer: "top", // Assuming all silkscreen is on top layer - route: arcPath, - stroke_width: arc.width, - }) + route: arcPath.map((p) => ({ + x: milx10(p.x), + y: milx10(p.y), + })), + stroke_width: mm(arc.width), + } as Soup.PcbSilkscreenPathInput) } const handleHole = (hole: z.infer, index: number) => { return pcb_hole.parse({ type: "pcb_hole", - x: hole.center.x, - y: hole.center.y, - hole_diameter: hole.radius * 2, + x: milx10(hole.center.x), + y: milx10(hole.center.y), + hole_diameter: milx10(hole.radius) * 2, hole_shape: "circle", pcb_hole_id: `pcb_hole_${index + 1}`, } as Soup.PcbHole) @@ -124,18 +146,18 @@ export const convertEasyEdaJsonToCircuitJson = ( name: portNumber, }) - if (pad.holeRadius !== undefined && mm(pad.holeRadius) !== 0) { + if (pad.holeRadius !== undefined && mil2mm(pad.holeRadius) !== 0) { // Add pcb_plated_hole soupElements.push( pcb_plated_hole.parse({ type: "pcb_plated_hole", pcb_plated_hole_id: `pcb_plated_hole_${index + 1}`, shape: "circle", - x: mm(pad.center.x), - y: mm(pad.center.y), - hole_diameter: mm(pad.holeRadius) * 2, - outer_diameter: mm(pad.width), - radius: mm(pad.holeRadius), + x: mil2mm(pad.center.x), + y: mil2mm(pad.center.y), + hole_diameter: mil2mm(pad.holeRadius) * 2, + outer_diameter: mil2mm(pad.width), + radius: mil2mm(pad.holeRadius), port_hints: [portNumber], pcb_component_id: "pcb_component_1", pcb_port_id: `pcb_port_${index + 1}`, @@ -157,22 +179,22 @@ export const convertEasyEdaJsonToCircuitJson = ( if (!soupShape) { throw new Error(`unknown pad.shape: "${pad.shape}"`) } - soupElements.push( - pcb_smtpad.parse({ - type: "pcb_smtpad", - pcb_smtpad_id: `pcb_smtpad_${index + 1}`, - shape: soupShape, - x: mm(pad.center.x), - y: mm(pad.center.y), - ...(soupShape === "rect" - ? { width: mm(pad.width), height: mm(pad.height) } - : { radius: Math.min(mm(pad.width), mm(pad.height)) / 2 }), - layer: "top", - port_hints: [portNumber], - pcb_component_id: "pcb_component_1", - pcb_port_id: `pcb_port_${index + 1}`, - } as PCBSMTPad), - ) + + const parsedPcbSmtpad = pcb_smtpad.parse({ + type: "pcb_smtpad", + pcb_smtpad_id: `pcb_smtpad_${index + 1}`, + shape: soupShape, + x: mil2mm(pad.center.x), + y: mil2mm(pad.center.y), + ...(soupShape === "rect" + ? { width: mil2mm(pad.width), height: mil2mm(pad.height) } + : { radius: Math.min(mil2mm(pad.width), mil2mm(pad.height)) / 2 }), + layer: "top", + port_hints: [portNumber], + pcb_component_id: "pcb_component_1", + pcb_port_id: `pcb_port_${index + 1}`, + } as PCBSMTPad) + soupElements.push(parsedPcbSmtpad) } }) diff --git a/package.json b/package.json index 15dac24..7ef520a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "@tscircuit/props": "^0.0.68", "@tscircuit/soup-util": "^0.0.38", "@types/bun": "latest", + "bun-match-svg": "^0.0.6", "circuit-json": "^0.0.83", + "circuit-to-svg": "^0.0.40", "tsup": "^8.1.0" }, "peerDependencies": { diff --git a/tests/convert-to-soup-tests/__snapshots__/c5184526.diff.png b/tests/convert-to-soup-tests/__snapshots__/c5184526.diff.png new file mode 100644 index 0000000000000000000000000000000000000000..2fdf71fa3f2b10d11c4334cd3196fa31b926797f GIT binary patch literal 3056 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i0*Z)=h^hlA&H|6fVxYte5N6z39hA+$ zz`e-R#WAE}&fA+Cy{0BI9DnG)^x%S{9v;paClqf-U2+J#&K%k7Y!d3+UA)D#gi)|W z(NSEmBryI(4l~iXxKKuIcm7%*k3o&% zgqV&+nWnYBe&ki9egAxax_-pA&A_HJw7o2GnSGgkagE)jpH<#Tz$QF{r>mdKI;Vst E02eZ@^8f$< literal 0 HcmV?d00001 diff --git a/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg b/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg new file mode 100644 index 0000000..5459d84 --- /dev/null +++ b/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/tests/convert-to-soup-tests/c5184526.test.ts b/tests/convert-to-soup-tests/c5184526.test.ts index a7bf09b..f921257 100644 --- a/tests/convert-to-soup-tests/c5184526.test.ts +++ b/tests/convert-to-soup-tests/c5184526.test.ts @@ -1,10 +1,17 @@ import c5184526 from "tests/assets/C5184526.raweasy.json" import { expect, test } from "bun:test" import { convertEasyEdaJsonToCircuitJson, EasyEdaJsonSchema } from "lib/index" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" test("C5184526 should have two holes", () => { const bettereasy = EasyEdaJsonSchema.parse(c5184526) + Bun.write("./tmp.bettereasy.json", JSON.stringify(bettereasy, null, 2)) const circuitJson = convertEasyEdaJsonToCircuitJson(bettereasy) expect(circuitJson.filter((e) => e.type === "pcb_hole").length).toBe(2) + Bun.write("./tmp.json", JSON.stringify(circuitJson, null, 2)) + + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + ) }) diff --git a/tests/fixtures/preload.ts b/tests/fixtures/preload.ts new file mode 100644 index 0000000..ee67a60 --- /dev/null +++ b/tests/fixtures/preload.ts @@ -0,0 +1 @@ +import "bun-match-svg" diff --git a/tmp.json b/tmp.json new file mode 100644 index 0000000..e9de5d3 --- /dev/null +++ b/tmp.json @@ -0,0 +1,263 @@ +[ + { + "type": "source_component", + "ftype": "simple_bug", + "source_component_id": "source_component_1", + "name": "U1" + }, + { + "type": "pcb_component", + "pcb_component_id": "pcb_component_1", + "source_component_id": "source_component_1", + "center": { + "x": 0, + "y": 0 + }, + "layer": "top", + "rotation": 0, + "width": 0, + "height": 0 + }, + { + "type": "source_port", + "source_port_id": "source_port_1", + "source_component_id": "source_component_1", + "name": "2" + }, + { + "type": "pcb_smtpad", + "shape": "rect", + "pcb_smtpad_id": "pcb_smtpad_1", + "x": 6.724904000000038, + "y": -1.2699999999999818, + "width": 2.8999941999999996, + "height": 2.4999949999999997, + "layer": "top", + "port_hints": [ + "2" + ], + "pcb_component_id": "pcb_component_1", + "pcb_port_id": "pcb_port_1" + }, + { + "type": "source_port", + "source_port_id": "source_port_2", + "source_component_id": "source_component_1", + "name": "1" + }, + { + "type": "pcb_smtpad", + "shape": "rect", + "pcb_smtpad_id": "pcb_smtpad_2", + "x": -6.724904000000038, + "y": 1.2699999999999818, + "width": 2.8999941999999996, + "height": 2.4999949999999997, + "layer": "top", + "port_hints": [ + "1" + ], + "pcb_component_id": "pcb_component_1", + "pcb_port_id": "pcb_port_2" + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_1", + "hole_shape": "circle", + "hole_diameter": 2.9999939999999996, + "x": 3.1749999999999545, + "y": -1.2699999999999818 + }, + { + "type": "pcb_hole", + "pcb_hole_id": "pcb_hole_2", + "hole_shape": "circle", + "hole_diameter": 2.9999939999999996, + "x": -3.1749999999999545, + "y": 1.2700000000000955 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_7", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": -5.461000000000013, + "y": -0.4611370000000079 + }, + { + "x": -5.461000000000013, + "y": -2.9209999999999354 + }, + { + "x": -5.461000000000013, + "y": -2.9209999999999354 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_8", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": 5.4500017999999955, + "y": -2.950006799999869 + }, + { + "x": -5.461000000000013, + "y": -2.950006799999869 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_9", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": 0.18298159999994823, + "y": 1.0549889999999778 + }, + { + "x": 5.450027199999909, + "y": 1.0549889999999778 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_10", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": 5.450027199999909, + "y": 1.0549889999999778 + }, + { + "x": 5.450027199999909, + "y": 0.4611370000000079 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_11", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": -2.944114000000127, + "y": 2.9450030000000424 + }, + { + "x": -5.461000000000013, + "y": 2.9450030000000424 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_path_12", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": -1.707006999999976, + "y": 2.9450030000000424 + }, + { + "x": -2.944114000000127, + "y": 2.9450030000000424 + } + ], + "stroke_width": 1 + }, + { + "type": "pcb_silkscreen_path", + "pcb_silkscreen_path_id": "pcb_silkscreen_arc_13", + "pcb_component_id": "pcb_component_1", + "layer": "top", + "route": [ + { + "x": -1.707006999999976, + "y": 2.9450030000000424 + }, + { + "x": -1.460311886129034, + "y": 2.9288336092640748 + }, + { + "x": -1.2178378661753868, + "y": 2.8806021044970294 + }, + { + "x": -0.9837338087377248, + "y": 2.801133754218995 + }, + { + "x": -0.762005367588813, + "y": 2.6917883071702136 + }, + { + "x": -0.5564464426572613, + "y": 2.5544367262343712 + }, + { + "x": -0.37057426423848483, + "y": 2.3914291751993915 + }, + { + "x": -0.20756921118118044, + "y": 2.2055548061152876 + }, + { + "x": -0.07022039279377168, + "y": 1.9999940353145576 + }, + { + "x": 0.03912207440475868, + "y": 1.7782641246765252 + }, + { + "x": 0.11858727851722506, + "y": 1.5441589992735771 + }, + { + "x": 0.1668155246366041, + "y": 1.301684331151364 + }, + { + "x": 0.18298159999994823, + "y": 1.0549889999999778 + } + ], + "stroke_width": 1 + }, + { + "type": "cad_component", + "cad_component_id": "cad_component_1", + "pcb_component_id": "pcb_component_1", + "source_component_id": "source_component_1", + "position": { + "x": -1016, + "y": 762, + "z": 0 + }, + "rotation": { + "x": 0, + "y": 0, + "z": 0 + }, + "model_obj_url": "https://modules.easyeda.com/3dmodel/c886ec2b42464573a88fc1f647577a49" + } +] \ No newline at end of file From 1877d93b5c5e1b116e4ad159f31406884e35b954 Mon Sep 17 00:00:00 2001 From: seveibar Date: Sat, 12 Oct 2024 16:51:08 -0400 Subject: [PATCH 4/4] fix flipped axis issue --- .gitignore | 2 + ...ert-easyeda-json-to-tscircuit-soup-json.ts | 4 +- .../__snapshots__/c5184526.snap.svg | 2 +- tmp.json | 263 ------------------ 4 files changed, 5 insertions(+), 266 deletions(-) delete mode 100644 tmp.json diff --git a/.gitignore b/.gitignore index a8f8ada..7bc4940 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ dist .vscode tmp temp.tsx +tmp.json +tmp* diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts index d05e79a..091c83f 100644 --- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts @@ -25,7 +25,7 @@ import { import * as Soup from "circuit-json" import { generateArcFromSweep, generateArcPathWithMid } from "./math/arc-utils" import { transformPCBElements } from "@tscircuit/soup-util" -import { scale, translate } from "transformation-matrix" +import { compose, scale, translate } from "transformation-matrix" import { computeCenterOffset } from "./compute-center-offset" import { mm } from "@tscircuit/mm" @@ -257,7 +257,7 @@ export const convertEasyEdaJsonToCircuitJson = ( if (shouldRecenter) { transformPCBElements( soupElements, - translate(-centerOffset.x, centerOffset.y), + compose(translate(-centerOffset.x, centerOffset.y), scale(1, -1)), ) } diff --git a/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg b/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg index 5459d84..31b7815 100644 --- a/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg +++ b/tests/convert-to-soup-tests/__snapshots__/c5184526.snap.svg @@ -10,4 +10,4 @@ .pcb-silkscreen-top { stroke: #f2eda1; } .pcb-silkscreen-bottom { stroke: #f2eda1; } .pcb-silkscreen-text { fill: #f2eda1; } - \ No newline at end of file + \ No newline at end of file diff --git a/tmp.json b/tmp.json deleted file mode 100644 index e9de5d3..0000000 --- a/tmp.json +++ /dev/null @@ -1,263 +0,0 @@ -[ - { - "type": "source_component", - "ftype": "simple_bug", - "source_component_id": "source_component_1", - "name": "U1" - }, - { - "type": "pcb_component", - "pcb_component_id": "pcb_component_1", - "source_component_id": "source_component_1", - "center": { - "x": 0, - "y": 0 - }, - "layer": "top", - "rotation": 0, - "width": 0, - "height": 0 - }, - { - "type": "source_port", - "source_port_id": "source_port_1", - "source_component_id": "source_component_1", - "name": "2" - }, - { - "type": "pcb_smtpad", - "shape": "rect", - "pcb_smtpad_id": "pcb_smtpad_1", - "x": 6.724904000000038, - "y": -1.2699999999999818, - "width": 2.8999941999999996, - "height": 2.4999949999999997, - "layer": "top", - "port_hints": [ - "2" - ], - "pcb_component_id": "pcb_component_1", - "pcb_port_id": "pcb_port_1" - }, - { - "type": "source_port", - "source_port_id": "source_port_2", - "source_component_id": "source_component_1", - "name": "1" - }, - { - "type": "pcb_smtpad", - "shape": "rect", - "pcb_smtpad_id": "pcb_smtpad_2", - "x": -6.724904000000038, - "y": 1.2699999999999818, - "width": 2.8999941999999996, - "height": 2.4999949999999997, - "layer": "top", - "port_hints": [ - "1" - ], - "pcb_component_id": "pcb_component_1", - "pcb_port_id": "pcb_port_2" - }, - { - "type": "pcb_hole", - "pcb_hole_id": "pcb_hole_1", - "hole_shape": "circle", - "hole_diameter": 2.9999939999999996, - "x": 3.1749999999999545, - "y": -1.2699999999999818 - }, - { - "type": "pcb_hole", - "pcb_hole_id": "pcb_hole_2", - "hole_shape": "circle", - "hole_diameter": 2.9999939999999996, - "x": -3.1749999999999545, - "y": 1.2700000000000955 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_7", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": -5.461000000000013, - "y": -0.4611370000000079 - }, - { - "x": -5.461000000000013, - "y": -2.9209999999999354 - }, - { - "x": -5.461000000000013, - "y": -2.9209999999999354 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_8", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": 5.4500017999999955, - "y": -2.950006799999869 - }, - { - "x": -5.461000000000013, - "y": -2.950006799999869 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_9", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": 0.18298159999994823, - "y": 1.0549889999999778 - }, - { - "x": 5.450027199999909, - "y": 1.0549889999999778 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_10", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": 5.450027199999909, - "y": 1.0549889999999778 - }, - { - "x": 5.450027199999909, - "y": 0.4611370000000079 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_11", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": -2.944114000000127, - "y": 2.9450030000000424 - }, - { - "x": -5.461000000000013, - "y": 2.9450030000000424 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_path_12", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": -1.707006999999976, - "y": 2.9450030000000424 - }, - { - "x": -2.944114000000127, - "y": 2.9450030000000424 - } - ], - "stroke_width": 1 - }, - { - "type": "pcb_silkscreen_path", - "pcb_silkscreen_path_id": "pcb_silkscreen_arc_13", - "pcb_component_id": "pcb_component_1", - "layer": "top", - "route": [ - { - "x": -1.707006999999976, - "y": 2.9450030000000424 - }, - { - "x": -1.460311886129034, - "y": 2.9288336092640748 - }, - { - "x": -1.2178378661753868, - "y": 2.8806021044970294 - }, - { - "x": -0.9837338087377248, - "y": 2.801133754218995 - }, - { - "x": -0.762005367588813, - "y": 2.6917883071702136 - }, - { - "x": -0.5564464426572613, - "y": 2.5544367262343712 - }, - { - "x": -0.37057426423848483, - "y": 2.3914291751993915 - }, - { - "x": -0.20756921118118044, - "y": 2.2055548061152876 - }, - { - "x": -0.07022039279377168, - "y": 1.9999940353145576 - }, - { - "x": 0.03912207440475868, - "y": 1.7782641246765252 - }, - { - "x": 0.11858727851722506, - "y": 1.5441589992735771 - }, - { - "x": 0.1668155246366041, - "y": 1.301684331151364 - }, - { - "x": 0.18298159999994823, - "y": 1.0549889999999778 - } - ], - "stroke_width": 1 - }, - { - "type": "cad_component", - "cad_component_id": "cad_component_1", - "pcb_component_id": "pcb_component_1", - "source_component_id": "source_component_1", - "position": { - "x": -1016, - "y": 762, - "z": 0 - }, - "rotation": { - "x": 0, - "y": 0, - "z": 0 - }, - "model_obj_url": "https://modules.easyeda.com/3dmodel/c886ec2b42464573a88fc1f647577a49" - } -] \ No newline at end of file