From 83f2460d88be1886e82cfbe1db7dbe8a7f3f088a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:21:37 +0000 Subject: [PATCH 1/3] Initial plan From 7db0456ccbbc5d7e771048a088cc08c00a66b30d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:37:44 +0000 Subject: [PATCH 2/3] Add double bed jacquard support with doublebed, birdseye, and tuck modes Agent-Logs-Url: https://github.com/kevinlearnscoding/Knitting-Machine-Punchcard-Generator/sessions/d0154761-56f2-434e-a65f-11d92cfd6676 Co-authored-by: kevinlearnscoding <42563934+kevinlearnscoding@users.noreply.github.com> --- QUICKSTART.md | 18 +++++ README.md | 19 ++++++ .../punchcard-generator.cpython-312.pyc | Bin 0 -> 23223 bytes punchcard-generator.py | 63 +++++++++++++++++- 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 __pycache__/punchcard-generator.cpython-312.pyc diff --git a/QUICKSTART.md b/QUICKSTART.md index ed288ca..525d5e7 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -78,6 +78,24 @@ find . -name "*.pcx" | python3 punchcard-generator.py --stitches 24 --layout mot - `-d ./output` preserve subfolder structure in output folder - `--threshold 220` change punch sensitivity - `--invert` invert pixel-to-punch behavior +- `--jacquard {none,doublebed,birdseye,tuck}` double bed jacquard mode + +## 6) Double bed jacquard + +For double bed jacquard knitting the punchcard rows are doubled โ€” each design row becomes two punchcard rows (pattern row + backing row). Three backing styles are available: + +```bash +# Solid/full-jacquard backing (all stitches punched on backing row) +python3 punchcard-generator.py design.png 24 motif 6 --jacquard doublebed + +# Bird's eye backing (alternating checkerboard on backing row) +python3 punchcard-generator.py design.png 24 motif 6 --jacquard birdseye + +# Tuck-stitch backing (inverse of pattern on backing row) +python3 punchcard-generator.py design.png 24 motif 6 --jacquard tuck +``` + +> A 6-row design with `--jacquard doublebed` produces a 12-row card. ## Troubleshooting ๐Ÿ”ง diff --git a/README.md b/README.md index c8f8302..c8e09b8 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This uttility generates SVG files, usable in most diecutting machines, for punch - ๐Ÿงต Supports 12-stitch and 24-stitch card widths - ๐Ÿ” Layout modes: `auto`, `motif`, and `repeat` - ๐Ÿ“ Vertical repeats for longer pattern runs +- ๐Ÿงถ Double bed jacquard modes: `doublebed`, `birdseye`, and `tuck` - ๐Ÿงช Batch processing from shell globs and stdin - โš™๏ธ Threshold and inversion controls for image-to-punch mapping @@ -87,6 +88,7 @@ find . -name "*.pcx" | python3 punchcard-generator.py --stitches 24 --layout mot - `--stitches {12,24}` card width (default `24`) - `--layout {auto,motif,repeat}` layout mode (default `auto`) - `--repeat-height N` vertical repeats (default `1`) +- `--jacquard {none,doublebed,birdseye,tuck}` double bed jacquard mode (default `none`) - `--threshold 0-255` image threshold (default `255`) - `--invert` invert punched and non-punched spaces on the card - `--hole-ratio 0-1` hole size ratio (default `0.55`) @@ -97,6 +99,23 @@ find . -name "*.pcx" | python3 punchcard-generator.py --stitches 24 --layout mot - `repeat`: tiles design across width (design width must divide by card width) - `auto`: picks `motif` or `repeat` based on width compatibility +## ๐Ÿงถ Double Bed Jacquard + +Double bed jacquard uses both the main bed and the ribber to create a two-layer fabric. Each design row is split into two punchcard rows: the pattern row (colour selection) and a backing row. Use `--jacquard` to enable this mode: + +- `none` (default): standard 1:1 punchcard +- `doublebed`: solid/full-jacquard backing โ€” every stitch punched on the backing row +- `birdseye`: bird's eye backing โ€” alternating checkerboard pattern on the backing row +- `tuck`: tuck-stitch backing โ€” inverse of the pattern row on the backing row + +```bash +python3 punchcard-generator.py design.png 24 motif 6 --jacquard doublebed +python3 punchcard-generator.py design.png 24 motif 6 --jacquard birdseye +python3 punchcard-generator.py design.png 24 motif 6 --jacquard tuck +``` + +> **Note:** Double bed jacquard doubles the number of rows on the card. A 6-row motif with `--jacquard doublebed` produces a 12-row card. + ## ๐Ÿค Contributing Ideas, bug reports, and PRs are welcome. diff --git a/__pycache__/punchcard-generator.cpython-312.pyc b/__pycache__/punchcard-generator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98cfb7236eaa29e155fda030e8d3c170dd584455 GIT binary patch literal 23223 zcmdsf32vR$8gd%^jMkj&2;9e=@Pxybm}yj_Clm07*Sij#GTYL?F?+W@rycb`u*qP z0-yvw>1En!Cs*RR|2@lp_J2G7!5`c0#T=f$D7x<-c@=N^&>mL?c>MdwbCVM}kssoE z)i>YEvu|Cm4sYF%e%R1!;5k1h>P5qcyl51Q5Sm0YLW@|8&??#x+C>LKCyQ~3C4k*5 zXQ@~QxLmA2SSeN^tQKn!*0NG{ET&#;Kun`3e8@jjPZLtKdYS=0ubvj>%FiynMg#B1VOd=-oB__B%}2yJ3S>_nX1!iiJj zIzSE!_v}CMZE*wAoX@7HG2%Po%Sd-gCE}(ltsFONkRv)y(p_GmO;GMFZWgzQTg7d+ zw|{0(V|z=b(#xJ)4h<4_h&#oe+q*u~t1&#cOupUX9&zsrgKY!hF5ft;_S zKWoI726W;};v2VnKQlgORL^0w-o$8iiRWKnv@QU9x7har?YIcMUf^9XUU~t%K2a2< z7Z}@qaX=ir?O!qno!Uyuy+6x+S zyooj+l$ylRY7`XTy2{DB@FlaaH}SQ_!l6t#;(`>XmF!(5t$I!?$F#h>hJn?g#r8I9 z_4Kw#t9n=9ix)?0xZb9bQ%|+BhP9G%sO_Rw*5iw{NTgO)WZKBmm(;-Wpa#M&GwjVz zVee+xE3^0ryQ^TIsl~h)BrH<%yE>_P#EP*$sf~E<-P?@f-e_d19F?hT|TSTntNY^ZO%2%bPz1;b? zIIeds#>yqFMGJa0T)8)5vBI8y5PRY2IBpqAfxH(B<}E-6khj3@Mr?r{E44}wM!ej8<)i?0$jA*Nq=O%ge zUkfL#Uct%b%W{oSZoR(2&FVgQW1O4Nzrl_3p6Rcsg5w@OWjr+O9gtGRKCdkHjr+yW zpj!7`Z%?XN4vhB=O8$YtP^Lj;Jtcj$Hx!cOk-ovekklvoy~7ed zGsSE@sj?;M6M_Ezpp?Pdv;OJi$^2x_)01-hhWw*TQKw3BaZAu~wLST18C-zdbYBMVvk6)= z?Y@9KBFTN~2>fMc9rUD12fai6YTLb`zDt2nC@{P~)B5Hf73kTN0l2k29^F?}DEyTT zfiEN2GGs*v>HBdUBE?NEqLar$EP;Z{)as|6%NYSKKRS|SgH!tFls4s;Q{jj%qW>&Y z)082U5p7Vr`PsbB!ESxFM8u#idy&7b)9T^5h~W$u;pMZxxV{L#wA6FzFVs8!Ib|}0 zHNWM#@zmOjwsL1UkN)^sY*Mo{;SYxT0#`hylr9iV8L*iKQzls&Vu0atz(0~Q4|}gj zqF)ZCObAD?0aNBn-k?N~$C$Fnfk3E_=XgNA(s^XW9|~dJw;%KR2K^&a`^m8p-yji`_5%`f zd9mwWEYoPV;DA=JV{|fAE)p#n2I{`NM#)eBxi0(#-$O9XePJqlSXzGbG+<9YX zeDgwSd(`}}qUz?rje(m(H-_Td=UW#lwnnWF&BfQPGuB_*ee=ML12<3HI1xXxP`2*Q zhWqB8q_ajb*Zj;>lF2iBc&_Zex#OX;lzv1SPZ@&# zu#_^6dV?Xnf}=zJkVl^?_K(p1VVghlw)mA4{!C;o!(BbeqMS{&DFW@=^+FIXDWoY~kMU;oJJxV~v-)9l*`>&m3N{ASyY zwpjmydu4Rb!;*@d)iN|@6hJq(SH=6V7yH@QM^gk_f zQ79O9drWNL3iFQ*D*xav=dX4kp-1GkS)9^G^o6*}n^^r*hAHC!H&qnMa+ruwD+e)A zL=F?u&3Wlr+KzN6)l(K=FR%|HmWZi8yB8w-6{@WmyiOk}wh?5$Y*Aa9A4ZC0ov0h( z#q1h{@VmjlMT!dL77dpRaUZkR1MLkQ{e&Z8h#33zpy|frDgJ1RpLoKzr#MfMydG_k zNo!9TW$y@tG4p^F8ud>|L#ZN26w-(&Hz3txl{Zr+OK4D*f)KOBRFPj@s)hmCJDD;v z@hw%PN^B_ui6JQ-@xDQ?9F%uZVY5Hj$Ea+O+K%O_J?gS`T^jQbiE0ie#>mHz?FjyY zzl~s;OJZ$qo!L4YT(H(o?@g9f-8^*T(7VT`_eEbyTJ6(&zqGq&U;35v-*6`5iYT9S zxuZrb+S#!W>%QR(mi5tH$;#TBQ#YpK-no+as|%HD=Oz=Co1zE5aF!<qlpfeqz2=I_JA@>rPuxE?vx7>_oI@8PSgI`|$XEb4%KZTGDRL=9(>!6)DyR#n2#= zfzOvuTC)WBHd1bKIkFhRG8J{V^%}tg$t99U`}nB`Q=MI+xui&Tk>{p*x$?OjPZPpP zZdsy63uXL?aV*r|zUfnv0!DlR5sdZ;U+mG##QHGjvLC#Wtq;9ihbZ|F#TN~FN5mm% zDN%B<%$eB@NfRL%_HB-|)8IY?M2zfFaduC9^`d1>n$sC;=lYXo$93C`ZT9TVH*UNU z3w_wPXkM9(TQt{vXGFH|DDT%4JYnnVdiSnk{ZERGyGl(mNuhuF8@K5B*-l^nUZoXC zgZ(&;>B^rNk;h&s3S-DMelcZ=m_%I|C8o>~qo}7eNRbA5hk*igK#SGq#TJ8lSR>ZL zQyQ`SkQ0rOVuVEuHZj=zktH`-%apa`l`Z}#OS8b~3fER z&7*kPMFxuZ3TBHanQh7*F-B~;o<|VEST_}(dU`la#Rfr;(S>x6Z} z6J7}GnskmpyD``NNi6jh$M7 zk-&)5ECfSx;EJ@p`O=WrcSZYb&tUHYKE^1XIDpjWmwiK$;6pQ>6g3MzG~@~J%k;U8 zy0t?-K_fx-Klu~|XApRbQu<-|;_`(Xdw@fTB~(Obo#352Wl%%nd1PCQ0;o-Lzy_8ZxB%vGWHwn@(468nfF#1rZb|bC~8?{Odoq=t|n2xexYo`^s%J7G2W7Jube)Tbe6~XnQ-jLoF`GYKH*$HeNc-! zI=4Adw;|!&Fn#cm%`v?%X|TL^^xdOM>F&Fm?ymX#u<}};^6Ew9Vn7+4Py&<6WH=3a z$nVwRb&cOguYI}$^l~56eamsiL%J-Aj=71s%Xf+ik#h7+rR4mg;X)>Hc5CcvY%ng( z?VsB;zxmF&&o?KwpH;TKqP)_lTpUsY6N!rxO5Y^VhIv23ewksvqWiL@CBFBEo!@Xp z#%uhHBWjslpR||G`s1ENMO(t&Hs`<7@@XJp-yYRHtX~xm{_p~c1IT(TdT3ToI!k8H zyg!+ARV7O*W1hG_QPY_y>74i6@%;$EE)sE>P!QWO*F0A?_v-xa`3>`9ch228rW~43 zsweN;!e2v7xW=cA)Bs4j#$p<<*{VfD&6m$(YGVuKD;LeHej!&QGs+)Yy7w9M|Jq>O zXE!YoiAmaEjBE=4GO36>oleT-5ljk$_(>gukyExr_y`w1O zWX8bIRjH4aQgg_Ys33yBAW5GP?o5@jm+qOGlGch?`#tOGWN8h7_4ll+=Cr9aqS_B&IaQjqxl#NE@;h(Tsghqdwhs;u?MtYmw&V} zWgvdW1}DF4Rw_qX@cm3#1BO$lag8Y><7%_lb6-9W2rX1(~ z9v{(*X3`Sm{2H^80Ma3ogrP4_6-A1~;xKYTCMk-TSe!Y+?d7gpe7XUMp66X8kBC;J zk(6Q~o{TTECac`gec(1g2br>vbdtvbv;70F2GN*9BtK_5X$bOIa!FJ%&O|@Ac_mL>oTb+-RQ(uq%((w(`;*J z1Oj1rEEp0lNrE>d3`sCJ2whCZkjYvglT9&PmMIVnjLAMJzc#SCkw8e0CVY}4=GPW( zLR%PS>8dm`G%1Mwt7z7eEY+`!1jj~412Tw*nxN7LVf7P!HB5)auX=~Zq00 zX@t=Yc^6ugDuNaP1=ZtZYKJ^dan?K%L-s`?UzuZsl$B97_3$87q*jx%jC#esP$1+T zN|^~9lKMkxq)cZI@)d>^DI!@?NUOE5Dj3N3rQCx7*&hy!070KxT~MnR1p*^JDMn&sN?uRcVCup}Q{WZiror z4J&O2KDXa}M|t&i#r4Kxy{^;&mCNo(n>kx`tm~e&4q27@oeSHZcdc7BI?kwKdm@^w`DdMd;P}SbNWPO+njIy(5Hcg%I!+U@u(HS zqq2&qg;epWg_4!uaJ*#=ba7kx16y6fRu?;W-{wi{5&6hj6EG|OFjrxcpBxmG83{{?KUK z=P)d>BV-xdKg6c$gzJBN&WWS%!utWS*Qc z45|z)6^yu-7tRZFu8xSRh962;7s{xta0}R}kx2OaX0fP{xh!h#hiBUN&7my20ZSzk zg=cTpu&c#@1r}~^k(L9skD_)nYw?=Gb&8hngJl)KZdwL=zJmF2h>Qp95sMl7vE~zb zFhw%7*~Z{^!JUOsE!ZEBhgH%>TU@}WAO^-RLA$vGns?djduxm+^p(s}Mn`cmk%)ab z&=6qdh4wN++<=I>T1*uDBcXs08V?BBLkq?)wy=VN&@P-t{w|?4Ac{Z{40^T*Uz~p_M4PhSMutf*uE>5NqG~AV?Wh zyh(HnZS~RTh;V6AxOg#>?cznXYr8Y0$mShn6}0C+ET(^KXo!YE*dm}xc@l`}=vVDA zhV9R%KNKKT`bD8rpuFwbam-Z4uw3%XVo;itfW+1fJ=%-~YXt75c)8kn(xYAsUPYY&#u zk`o)+uIuslcpHKr@JBeF$+1i(gB&=cV-lSz!mHwa#%lcQ{7oK9A2Qjb;dk{oW;d!e zU=ebxYR1Zs%VYS4G_UJem*Ov{iqxatjJQ6{*wPcrHaY>D7?pQ~TUDE^y6*b0Tp0`O z*ftdKd53~KI;H7k9X{dNeCO0X&yG)8?(SJ= zK9Fd-@YnF_;JCk6U5B#}8oRJ<`zY8$0a+-RfFr*R$YW8Bs-{d~`2ZqGF$<0j zg;KmfBcNnbX?jv-8d8GfUqjv?(N(sF5%j2nM|r_%l|Mqxe?~>9yO_nVYhd0^I!mM0 zFKy0Q!|bUWre8SiM1i5qXV-M6hu+RI|a_w3E-BCet`R&wK&=>B9)-N&nMtzKxluz2cK<+V2# zPMu$Dy0B1lA$mkBt(7dgXKzSWR^J@IF^&wR0xmylc<_uU=1 z`^M*9rT*lC`;=lkm2RYFrkgpNd-~Y3dLxXEdh*i(>+W*Hf2mD7$q?Ep&m54n%B>Ch^P#j_`8+J{DI5cR~=->(N`U{EtL zR0g#GDk2Cvmx=7t#Dw1$5Nl@XO@J%}foU4VVg{K2Ss7#oWMhy8kexxrfE*060&+44 zmR`}tAZT1-349h8EvL3XE36 z60x#hH&Y)ep*$Jb#o!7SufkOXi`5j))QQ!Uo=Zhc)iW`yPL_vZo~i5CMM_xcLjFu@ zgy2kBu`-8QtU(J=C$+O*H{hCbi?xL`VX+P(gu8$UQ98iA-#F7)$WAQQ&(uTrZMw|PMV$1iB@;fjGl@Z5ORbF~_JwilWP3bd&xRRZGR4>!2Rgo&RYIURnVQZwCwJ=Xu zid43o!wTEROkKoDE8_pVephP!US=dcpJjwzo(p9?93a%M5!-IB)kXuy zgtd{{%S5l9dc^iZt8}JO!>YY8Udxov<@hB|COVFXD~<>9q+1B4GL&*j4sXckg1hAa z1W>v2j30JDS%#$9c_uU_`U9F=$Lxw*1epW_8PtN4p)+tYQAPPR3doN{CSE3Ec3~=` zfOICAmbdJnfQC(m1q^043dndamr+nb0S%fQp@4|?SCsG-C3ti!LXHEL-$1ZL8lmof9*3-SohI?EG{E#H~{?Z|qE*k2l3je{)NsW@knwDXZYa zCtvii2-6ev2S&CxcXf0%LoyCZC*=T6eQ<0;Vr=eg$2L5icb-&2UrSDu!u8FtsNMtCrrQi#AqE_*tuhg zIh+;<&HLlK2!3EBMR`g;0~;x$39vnqm3GF&E-aiNV>zR4MKi?(JxSmzh{@G6OCC;z z0UJNcqi)NVB0KN0rBJ_>Q9-pOZ(=>@XDmT}68tjLGX8_(lkhL{6dhNO`+CtC`5Xnv z&B?!w;8UJSiym9br1^E4G(S$W<^l^V7tC3G6L8lfV^-e;TX~Qkz3h-mHI}JIUh2kU z8^up`&=zDiiHzttO^+%)I$#XLshB7+m(XCi^SR4*X@O>1yElRV=P>)1R7H!D?z-4` z!o4bLO4>@{x#4imZoTJdNS4>f^*1g+LR3#+l5Qbh5jW2j&AkN^Q~CVW`9WpJsN#C- zTRqvXia5KQyf-LYW!y4XG0)Eh=U3h-nx9a1zNNV2Y&OSi&CChdY@DvyBlnz*iPo)2 zS9!Xe!}r$vjsk8RL~+=v8UpYG!%c3+gpCjL#QC+L>hLnk9J&ZIMHx>4!;G=uih`*G zYXj?$){=T2kiViLx*0QD8@dlVKtwo?>A0%Vl%ry~z%r2k5fVHW_)g+bk-1e6ZBfgR z&Le+D0h>`qN*NU;%9>LPaue&MdCDlZq(6k$z0l0&mFT9aB^pXoYZEY+?iw7-SsHMR zmN{sv?wBu4RBxCq!kFK*-LNGqR>V)m_r$NxdFRf|8|VAyUstvVl(JDwSBaZu)iDb@ zVg1amukAp@g!h8G;ym@V(3!A>DN?EO-KS5SJ-F|5-=1Bk_x7DUePaKiqx;UJ^l*4m zRWKr_stUG?pe_o|BJixp9Tzhzvn(PuRRm|fe*b_xL-~mA{PK(`<;-|oXE&@ZIOrXf z`c!(Dva0VsSb=2ucd3eb3fL6CPY~lKY>KbY=PMMzw#Plan{#uYWv#&#$fxOrh2A}x zN8R+5%Pa-?$3XMv_zSjxRsIc>MjMnyW|Qt(aC9r??l0}l5A3TF_Eqtz1^dS7{YjI3 z`fbWko3Ywf_boUsD&~t{x=LsFerTCKL?^MfM9IqO!+B6?b*v*%>X|;8tZPI7_8oPNlWQ$AYoYrxkN6ec)Y1?{o3)MEObuC&*g%bMxJal^byukQdYSNuOfgND&PEH5?@* zQD++~R}r(uy|iBGc5Fqxjt!jCM^{)ono$~HxKK0x~ZhBX!>Y+ z6<1nuv-L(R#Etp2i#0ei*onU7c@89tdzEvN(%Y}}%S!NVMSdsEaXq?Ibclai_a?ng z>CV&ZyzT|qh=|K>TMIZ?3`FgUrK6WK`URm>|Vm{B3zB4S9 z_3@V=RF%%{n_o3wrEI>UxQ6n0h)R^wR$xfmI7@M~cg8uxr{UXL^>rC%ELBQZD63vp zHXm6u9R0$8Myysm+m)SX7Y(nV6PEgEFrdfgC_81xIj=Kfo`$6q9(;CqpgE>WL~aV+WAB#bo#xw_ zHd+j)OcMI>FcBXXfQT8^3Y>JaGiZCBrN$Mp-ZpBQDDc7_1ONC&m?=%n#!;Ggs8Jem zX*r?ck@~_O(M)=M&JqOu3|1Rp=>e8vh6P&kyRJMfy)5EFOHH?}T041eq&81yr1X+d zR!2m=7(064Z3nM`+qTb$9pHJY9c53o!~O2sY_E-?Jz}MFm|Y!H<$3LQM%<8P&?1Bd zYyBZKc9hJMo0)9uhS$8j7p;)@QBa8hj-}%Sze~aQD7Zthyd0w!eC$cUNf~efX)s*e z!OlK9LKC5|We>YWB5o0oNl%_oWW~&8K?JH|m_mf5X@;urJ8rqaOWY*5K&DybtyqT~bqTnJ0UIZ!a9t~tU__k&4 zuc#h8R<&<^V?jxT#dsofqri;8QtuYnHO%l!>kkA;Gbkh_XlL2(%&i&7F4{#J`QM<7 zh_x3yj$oSmwvMy5#BuuBF}>&Es+Z=2cm2xi7Zz9bB@8Xod!uc$kw>O7#Z;H9s)^bk zxhrtQUD^PCPY_EH9=2>q!XjO>I$70{tXcc5#kit4VHTp+*>fPlRV$;mM<(Yi+=!|l zRxm&eZaX4yyWGYvxJqeQs=;Cz=b~VRb6|0JGT{L$-G}~t_vDR3P;#dLqV&siA zCQPlltT=*JtScVcTF6Dr1%BU_tX=t67Ngzqt%I}IWnx`*47rUsm3{qBw@`n}^dz&fKVq!T+3Qns$zlj3C-Wwh%@$?_t9!5Sc{SQ|Q)Dp<~+|{Uy!yIps+@e(K4UXH*t6I^rl~GtIWs8FAwF5ihsoD4_@) z3{*$Ye1Q!g8AH@__FQ&pQ0|?h)O2?zMEV?iBK9R-TrLY2B5g}e<3LMscOs%E%Bk`P zvQOfMQHDzqt`;st+k-v06x%jc@=O~|EmC2yBZX5Qm`_5!ULlrwDT z81+q1I5H5nF!vY%c9d}K79rp4Ms^!I&mD$ePv*P{M{X4BDbA4~`Cp>x%hDbB4=D96 zg0K!0)`qJ{A=y7F*HUyH0&>K`6+t+?vO7vdqm4s>ahUo31*PhPlfhI`Fa**jvwL%l zE~G5`;4d@^gV2b_t2}I6-irVvZ66r3aw{V2B>UG)-*$@Nj9#9-Q=&T?Uv%7gW%fA z;cr95h{~a=_~|)59#kA{|D@%|YksumfqDBq^Y;G%e--17CqcR{@za{h-QC3_PlX-|{>Uo{y4r_6nQ!(RVLUmv`qGzuTymbqjpWxW@KVf7+r&NTrJ1#)0mIC=a4 z{11eadtO%^-GUwA)%zyA!=pnu3(aB_n2#2%ZJ~RE`XRh@)?>i6Q#mk@vch4&hs8(B z2Fq^LKZHm#nJor!A2qCN4}`5JUO9X6m9xUhU1tx5SD#?FP2qS(KJ5Xx5s>$V#;Qc} zW|vxno({+jCR`f#UiHc;Lk5)ym$!X;PVd`wcAv2K5Wur1P9NHLCcOO_oOHWY7$1bU zL*_y&`4}*FSyVTKbx&r@F<)p*mO8@b_I7ncLJs+T47kjQI1^a5WiYGh9l{CL&)^n; z9hnT_%IyHW z6+_5}%*}+pWv$dF}oGW5buQjo^Dj*JJ%~KFJ`;cq>uij2SY2D%tR8ax8jF>O{(-dSt+v zMV_UO{W=2gTG08u1)Q?fKK=qo7K-b}0(uFrV1KBCE{np$88<@LgfHZ~Gi&r$ zIHURi2L=2RhJT2t3T~OoG>r%R|?v$>3!&mP>!fhu5 zK|fKy?1p&8p?d_A8pHP*m{TTZ5v?@{Vfan7SVPNsqmr0mC)da?+yi=FIP7{-C?h`{SLdKIhLnEo&S zw;`tK37mm=}#Uy%C7RuKcZB) zr*V0Pfmi8sl!8eLa(03r(Pqo}6pvfv zs;r~R_jfZ*8^Lb^F#`&d>X^%-s_(8wh9~B2Aj)Gy(hk`KqK*GtP&DCcq@L&bzu=1h zjI;b1=ll;`dD`r@6eZ1Nvr<|Q=n)`a+6aiOuQ0c!O$3`^B#xJ+Ed;~Z+88&ctpvlO z+ZZ>b?F2hut&RE8PJ&&Wr7m_hT|zK)`?}bfbSc4QDxPwJVR)vpl>}E2tWQ@HT*Fyv zW4qF|1lMtv=J>{RJ;4nGuTM7;EO0Q)m!_KtZssfvv9WXu!K)DbUFpo%p4HVlg)4u2 znz!=h-_%+7=EqeIzVVx8Jzx9SR>{|Xv%$pIeN)!WZ+m>1U%|W6ukxI$?6Kt?p07x$VxRwIt~- zrQEe^`5oyudCp*qh8OhJN&AZE>ksTJ6805IdsVXRbkeygSyB#LlcQ*QKh}lO`oK_` zFjU42xOuR}hZCMfgP1fFYq83zZD|M-{2m>WzQqN|y|@6GNllvFDc5S8d?xKj(iYBG z^(|+_nMMw2(u^b*w8fPgsm@(JDJDfL2IF@uju+}B4bqm&pY5Zi4 zZPswpal^4_T^Vmdkhy}Xly6%!Z+~R6zW4UKZ^ugRnQF2Ln-|SnDB;?>*Je+B@11P? zhDGy6po@mCzccgBB21i32(qaM7tK)ltq)Aq2~%}!*F6&s)7Z}%DCLCkXYPvG(9LT% zt}VD*$lzHCgJ(%OPCl#Za7y{LnKN6XTNiLSvg@9y4tIM_f6_RAG||*^_ndO-%uo81 z*Mo_D!L&|qImmxe*BH0X?f%5MP`5663{)<2mxsJ#;TNG)?_abX_*rF5Y-?P;wR54e zEow`a3vu_2zG!i>&ZC&ClNC+zmUy>Pv1-Qpg~b*0>Yt&%Q-BowT8SdC|P-XSgV|;p45pv2||! z?`{5c^Mf|rBHg;!wmqBa@S^$1*F~JUG+BddNluF+diV<)jJ!42KsSqSz(YZO)d?RT zxpgF2)9`V}tq%GT5lq)pU$u=N@4U4$S=~goUzmui>p!l)RsUDD4mhUhfjPZ`vpKJC zp4t4sTAQ%eE?Vo8WtH^%C2GBUS-oXih5zU3{TlUF#@2tl`PSwKwQY&ow#C|Z^#6gm zI$^H9Z>~-2xw?+%*0j&a8ESr>zM$t^aM<9nnsTe=f$acYXgbKNKh*Qb8?Ep&xX)F* LQlr0L%_IEZsFGij literal 0 HcmV?d00001 diff --git a/punchcard-generator.py b/punchcard-generator.py index 3b60159..46804a6 100644 --- a/punchcard-generator.py +++ b/punchcard-generator.py @@ -184,6 +184,45 @@ def compose_card_rows(rows, card_stitches, card_layout, repeat_height): raise ValueError(f"Unsupported card layout '{layout}'") +def build_double_bed_rows(rows, backing="doublebed"): + """Transform card rows for double bed jacquard knitting. + + Each pattern row is doubled into two punchcard rows: + - Row 1 (odd pass): the original pattern row (selects needles for colour A) + - Row 2 (even pass): a backing row whose style is chosen by ``backing`` + + Backing modes + ------------- + doublebed : every stitch punched โ€“ solid / full-jacquard backing + birdseye : alternating x-/checkerboard backing (bird's eye) + tuck : inverse of the pattern row (tuck-stitch backing) + """ + if not rows: + return rows + + width = len(rows[0]) + result = [] + + for i, row in enumerate(rows): + result.append(row) + + if backing == "doublebed": + result.append("x" * width) + elif backing == "birdseye": + # Checkerboard: phase shifts by 1 each pair so the backing forms a + # diagonal bird's eye grid across the fabric. + offset = i % 2 + backing_row = "".join("x" if (j + offset) % 2 == 0 else "-" for j in range(width)) + result.append(backing_row) + elif backing == "tuck": + tuck_row = "".join("-" if ch == "x" else "x" for ch in row) + result.append(tuck_row) + else: + raise ValueError(f"Unsupported double bed backing mode: '{backing}'") + + return result + + def write_brother_style_svg( path, rows, @@ -326,6 +365,7 @@ def generate_punchcard( card_stitches=None, card_layout="auto", card_repeat_height=1, + jacquard="none", ): img = open_flattened_rgb_image(input_path) rows, width_cells, height_cells = build_punch_rows(img, threshold=threshold, invert=invert) @@ -346,6 +386,9 @@ def generate_punchcard( ) output_width = card_stitches + if jacquard != "none": + output_rows = build_double_bed_rows(output_rows, backing=jacquard) + if punchcard_mode in ("text", "both"): txt_path = output_base + ".punch.txt" write_punch_text(txt_path, output_rows) @@ -373,7 +416,8 @@ def generate_punchcard( print(f"Created: {svg_path}") if layout_used is not None: - print(f"Card layout: {layout_used}, width: {card_stitches}, rows: {len(output_rows)}") + jacquard_info = f", jacquard: {jacquard}" if jacquard != "none" else "" + print(f"Card layout: {layout_used}, width: {card_stitches}, rows: {len(output_rows)}{jacquard_info}") def process_file( @@ -389,6 +433,7 @@ def process_file( stitches=24, layout="auto", repeat_height=1, + jacquard="none", ): input_path = input_path.strip() if not input_path or not os.path.exists(input_path): @@ -416,6 +461,7 @@ def process_file( card_stitches=stitches, card_layout=layout, card_repeat_height=repeat_height, + jacquard=jacquard, ) except Exception as e: print(f"Error on {input_path}: {e}", file=sys.stderr) @@ -483,6 +529,19 @@ def process_file( default=1, help="Number of vertical repeats for card pattern. Default: 1.", ) + parser.add_argument( + "--jacquard", + choices=["none", "doublebed", "birdseye", "tuck"], + default="none", + help=( + "Double bed jacquard mode. Each pattern row is doubled: the original row" + " followed by a backing row. " + "none: standard 1:1 card (default). " + "doublebed: full/solid backing (all stitches punched). " + "birdseye: alternating checkerboard backing (bird's eye). " + "tuck: inverse of the pattern row (tuck-stitch backing)." + ), + ) parser.add_argument("-help", action="help", help="Show this help message and exit.") parser.add_argument( @@ -561,6 +620,7 @@ def process_file( stitches=stitches, layout=layout, repeat_height=repeat_height, + jacquard=args.jacquard, ) # 2. Process files passed via pipe (e.g., find . -name '*.pcx' | punchcard) @@ -579,6 +639,7 @@ def process_file( stitches=stitches, layout=layout, repeat_height=repeat_height, + jacquard=args.jacquard, ) # 3. If no input was provided via either method, show the help From 37f1a9b3d3194bbc54882b0b92247a0be0ea31e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:38:29 +0000 Subject: [PATCH 3/3] Add .gitignore to exclude __pycache__ Agent-Logs-Url: https://github.com/kevinlearnscoding/Knitting-Machine-Punchcard-Generator/sessions/d0154761-56f2-434e-a65f-11d92cfd6676 Co-authored-by: kevinlearnscoding <42563934+kevinlearnscoding@users.noreply.github.com> --- .gitignore | 1 + __pycache__/punchcard-generator.cpython-312.pyc | Bin 23223 -> 0 bytes 2 files changed, 1 insertion(+) create mode 100644 .gitignore delete mode 100644 __pycache__/punchcard-generator.cpython-312.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/__pycache__/punchcard-generator.cpython-312.pyc b/__pycache__/punchcard-generator.cpython-312.pyc deleted file mode 100644 index 98cfb7236eaa29e155fda030e8d3c170dd584455..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23223 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 LQlr0L%_IEZsFGij