chore: update deployed documentation
This commit is contained in:
1 parent
a5e5a3b96d
commit
651541da82
1694 files changed
+4099896
-497061
No files matched your search
@@ -0,0 +1,537 @@
|
||||
0
|
||||
BRDOUT: 13 8631 4287
|
||||
8630 2829
|
||||
8631 0
|
||||
5240 0
|
||||
5239 1988
|
||||
5499 1988
|
||||
5499 2011
|
||||
5482 2011
|
||||
5482 2328
|
||||
5500 2328
|
||||
5500 2352
|
||||
5239 2352
|
||||
5239 2830
|
||||
8630 2829
|
||||
|
||||
NETS: 86
|
||||
1 GND
|
||||
2 V_USB
|
||||
3 15V
|
||||
4 5V
|
||||
5 9VAC2
|
||||
6 9VAC1
|
||||
7 V_REF = 2.048 | 2.5 | 2.9
|
||||
8 9VAC_OUT2
|
||||
9 9VAC_OUT1
|
||||
10 3.3V
|
||||
11 VDDA
|
||||
12 Net-(IC1-V5V)
|
||||
13 Net-(D1-A)
|
||||
14 Net-(IC1-VOUT)
|
||||
15 Net-(IC1-VSEL)
|
||||
16 unconnected-(IC1-FLIP-Pad6)
|
||||
17 DN
|
||||
18 Net-(IC1-PWR_EN)
|
||||
19 Net-(IC1-LED)
|
||||
20 Net-(IC1-VCC)
|
||||
21 Net-(IC1-ISEL)
|
||||
22 DP
|
||||
23 CC1
|
||||
24 CC2
|
||||
25 unconnected-(J1-SBU1-PadA8)
|
||||
26 unconnected-(J1-SBU2-PadB8)
|
||||
27 NRST
|
||||
28 JTDI
|
||||
29 VCP_TX
|
||||
30 unconnected-(J3-NC-Pad2)
|
||||
31 JTMS
|
||||
32 unconnected-(J3-NC-Pad1)
|
||||
33 unconnected-(J3-JRCLK{slash}NC-Pad9)
|
||||
34 VCP_RX
|
||||
35 JTDO
|
||||
36 Net-(Q1-Pad4)
|
||||
37 Net-(Q1-Pad5_8)
|
||||
38 G3
|
||||
39 G4
|
||||
40 G1
|
||||
41 G2
|
||||
42 Net-(U3-+)
|
||||
43 Net-(U3--)
|
||||
44 1.225V
|
||||
45 OSC_OUT
|
||||
46 PWM_G1
|
||||
47 PWM_G2
|
||||
48 PWM_G4
|
||||
49 PWM_G3
|
||||
50 unconnected-(U2-PA1-Pad9)
|
||||
51 unconnected-(U2-PB9-Pad46)
|
||||
52 unconnected-(U2-PC15-Pad4)
|
||||
53 OSC_IN
|
||||
54 unconnected-(U2-PA3-Pad11)
|
||||
55 unconnected-(U2-PB4-Pad41)
|
||||
56 unconnected-(U2-PB15-Pad29)
|
||||
57 unconnected-(U2-PB0-Pad16)
|
||||
58 unconnected-(U2-PB2-Pad18)
|
||||
59 unconnected-(U2-PB5-Pad42)
|
||||
60 9VAC_COMP_ADC
|
||||
61 unconnected-(U2-PA2-Pad10)
|
||||
62 unconnected-(U2-PA7-Pad15)
|
||||
63 JTCLK
|
||||
64 unconnected-(U2-PG10-Pad7)
|
||||
65 unconnected-(U2-PA0-Pad8)
|
||||
66 unconnected-(U2-PA6-Pad14)
|
||||
67 unconnected-(U2-PC13-Pad2)
|
||||
68 unconnected-(U2-PA12-Pad34)
|
||||
69 unconnected-(U2-PB8-Pad45)
|
||||
70 unconnected-(U2-PA5-Pad13)
|
||||
71 unconnected-(U2-PB1-Pad17)
|
||||
72 unconnected-(U2-PB10-Pad22)
|
||||
73 unconnected-(U2-PC14-Pad3)
|
||||
74 unconnected-(U2-PA4-Pad12)
|
||||
75 ENABLE_DRVA
|
||||
76 ENABLE_DRVB
|
||||
77 CURRENT_COMP_ADC
|
||||
78 unconnected-(U7-NC-Pad3)
|
||||
79 unconnected-(U8-NC-Pad4)
|
||||
80 Net-(L2-Pad1)
|
||||
81 Net-(L3-Pad1)
|
||||
82 Net-(IC2-SW)
|
||||
83 Net-(IC2-VBST)
|
||||
84 Net-(IC2-EN)
|
||||
85 Net-(IC2-VFB)
|
||||
86 Net-(U1-+)
|
||||
|
||||
PARTS: 90
|
||||
C1 5619 2158 5747 1975 0 1
|
||||
C10 6278 2323 6406 2140 2 1
|
||||
C104 5703 1760 5777 1722 4 1
|
||||
C11 6621 2033 6695 1995 6 1
|
||||
C142 5968 1141 6041 1103 8 1
|
||||
C19 7295 3231 7388 3414 10 2
|
||||
C2 6800 831 6885 758 12 1
|
||||
C20 5756 447 5829 409 14 1
|
||||
C21 7434 3192 7473 3265 16 2
|
||||
C22 7474 3183 7513 3256 18 2
|
||||
C23 7820 1824 7913 2007 20 2
|
||||
C24 6886 832 6971 758 22 1
|
||||
C25 7059 3230 7151 3413 24 2
|
||||
C27 6282 1186 6355 1147 26 1
|
||||
C28 6652 1009 6690 936 28 1
|
||||
C29 6281 1147 6355 1109 30 1
|
||||
C3 6280 2119 6408 1936 32 1
|
||||
C30 6751 1095 6825 1057 34 1
|
||||
C31 6188 1315 6371 1187 36 1
|
||||
C33 6186 1444 6369 1316 38 1
|
||||
C35 5969 1376 6007 1302 40 1
|
||||
C36 5755 485 5829 447 42 1
|
||||
C37 6884 2357 7020 2278 44 1
|
||||
C38 6868 2273 7004 2194 46 1
|
||||
C39 6732 2273 6868 2194 48 1
|
||||
C4 6868 2193 7004 2114 50 1
|
||||
C46 5896 1141 5969 1103 52 1
|
||||
C47 5620 931 5694 893 54 1
|
||||
C48 5830 447 5904 409 56 1
|
||||
C49 5830 485 5904 447 58 1
|
||||
C5 6883 2605 7019 2526 60 1
|
||||
C59 5912 537 5986 499 62 1
|
||||
C6 6884 2521 7020 2442 64 1
|
||||
C60 5952 888 5990 815 66 1
|
||||
C61 5544 854 5582 781 68 1
|
||||
C62 5889 1376 5927 1303 70 1
|
||||
C63 5968 1103 6042 1065 72 1
|
||||
C7 6884 2439 7020 2360 74 1
|
||||
C8 6048 2053 6231 1925 76 1
|
||||
C9 6597 2267 6671 2228 78 1
|
||||
C94 5468 649 5546 570 80 1
|
||||
C97 5408 809 5486 730 82 1
|
||||
D1 5682 1975 5802 1913 84 1
|
||||
D2 7672 1781 7873 2061 86 2
|
||||
D27 5793 370 5923 225 88 1
|
||||
D3 5589 2480 5868 2278 90 1
|
||||
H1 7359 2754 7601 2512 92 1
|
||||
H2 5390 2754 5632 2512 95 1
|
||||
H3 5390 274 5632 32 98 1
|
||||
IC1 5695 1907 5864 1756 101 1
|
||||
IC2 6554 2221 6721 2079 116 1
|
||||
J1 5215 2423 5562 1616 122 1
|
||||
J2 7862 2811 8612 1975 138 1
|
||||
J3 5229 1384 5861 1136 149 1
|
||||
L1 6427 2706 6880 2282 163 1
|
||||
L2 6579 1691 6998 1134 165 1
|
||||
L3 6436 564 6993 145 167 1
|
||||
Q1 6101 2253 6287 2094 169 1
|
||||
Q2 6438 1110 6652 797 174 1
|
||||
Q5 5915 2254 6101 2094 182 1
|
||||
R1 5723 2218 5841 2158 187 1
|
||||
R2 7747 1571 7872 1696 189 2
|
||||
R21 6693 670 6827 593 191 1
|
||||
R22 7604 3208 7663 3327 193 2
|
||||
R23 5830 409 5905 370 195 1
|
||||
R24 7604 3328 7663 3447 197 2
|
||||
R25 7664 3268 7783 3327 199 2
|
||||
R26 7724 3148 7783 3266 201 2
|
||||
R27 7462 3396 7581 3455 203 2
|
||||
R28 7759 2081 7884 2206 205 2
|
||||
R29 6734 2187 6773 2112 207 1
|
||||
R3 5948 2051 5987 1976 209 1
|
||||
R30 6734 2111 6773 2036 211 1
|
||||
R37 5948 2091 6023 2052 213 1
|
||||
R4 5806 2036 5866 1918 215 1
|
||||
R5 5427 1914 5545 1854 217 1
|
||||
R6 5426 1854 5545 1795 219 1
|
||||
R7 5882 1787 6001 1728 221 1
|
||||
R71 5756 409 5831 370 223 1
|
||||
R8 6414 2089 6474 1971 225 1
|
||||
R9 6620 2075 6695 2036 227 1
|
||||
T1 6999 1944 8620 8 229 1
|
||||
U1 6685 815 6848 672 239 1
|
||||
U2 5584 893 5991 486 244 1
|
||||
U3 7434 3253 7597 3395 292 2
|
||||
U4 6689 1055 6982 835 297 1
|
||||
U5 6202 1109 6422 816 305 1
|
||||
U7 7787 3200 7940 3342 313 2
|
||||
U8 5879 1303 6022 1140 316 1
|
||||
Y2 5396 791 5579 608 321 1
|
||||
|
||||
PINS: 325
|
||||
5683 2125 2 1
|
||||
5683 2009 1 1
|
||||
6342 2290 1 1
|
||||
6342 2174 3 1
|
||||
5759 1741 1 1
|
||||
5721 1741 12 1
|
||||
6677 2014 1 1
|
||||
6639 2014 84 1
|
||||
5985 1122 10 1
|
||||
6023 1122 1 1
|
||||
7342 3264 6 2
|
||||
7342 3380 5 2
|
||||
6866 813 1 1
|
||||
6866 776 10 1
|
||||
5811 428 7 1
|
||||
5774 428 1 1
|
||||
7454 3210 1 2
|
||||
7454 3248 4 2
|
||||
7494 3238 4 2
|
||||
7494 3201 1 2
|
||||
7866 1858 9 2
|
||||
7866 1974 8 2
|
||||
6905 776 10 1
|
||||
6905 814 1 1
|
||||
7105 3263 6 2
|
||||
7105 3380 5 2
|
||||
6300 1166 1 1
|
||||
6337 1166 3 1
|
||||
6671 954 1 1
|
||||
6671 991 3 1
|
||||
6337 1128 3 1
|
||||
6299 1128 1 1
|
||||
6344 2085 3 1
|
||||
6344 1969 1 1
|
||||
6807 1076 3 1
|
||||
6769 1076 1 1
|
||||
6221 1251 1 1
|
||||
6337 1251 3 1
|
||||
6336 1380 3 1
|
||||
6220 1380 1 1
|
||||
5988 1320 4 1
|
||||
5988 1358 1 1
|
||||
5811 466 7 1
|
||||
5773 466 1 1
|
||||
6914 2318 4 1
|
||||
6989 2318 1 1
|
||||
6898 2234 4 1
|
||||
6973 2234 1 1
|
||||
6837 2234 4 1
|
||||
6762 2234 1 1
|
||||
6898 2154 4 1
|
||||
6973 2154 1 1
|
||||
5951 1122 10 1
|
||||
5913 1122 1 1
|
||||
5638 912 1 1
|
||||
5676 912 10 1
|
||||
5848 428 11 1
|
||||
5886 428 1 1
|
||||
5886 466 1 1
|
||||
5848 466 11 1
|
||||
6914 2566 4 1
|
||||
6989 2566 1 1
|
||||
5930 518 10 1
|
||||
5968 518 1 1
|
||||
6914 2482 4 1
|
||||
6989 2482 1 1
|
||||
5971 833 10 1
|
||||
5971 870 1 1
|
||||
5563 799 10 1
|
||||
5563 836 1 1
|
||||
5908 1321 4 1
|
||||
5908 1359 1 1
|
||||
5986 1084 10 1
|
||||
6024 1084 1 1
|
||||
6914 2400 4 1
|
||||
6989 2400 1 1
|
||||
6081 1989 1 1
|
||||
6197 1989 3 1
|
||||
6615 2248 82 1
|
||||
6653 2248 83 1
|
||||
5494 596 1 1
|
||||
5521 623 45 1
|
||||
5434 756 1 1
|
||||
5461 783 53 1
|
||||
5712 1944 1 1
|
||||
5774 1944 13 1
|
||||
7742 1999 8 2
|
||||
7742 1842 9 2
|
||||
5899 332 10 1
|
||||
5816 332 7 1
|
||||
5650 2348 2 1
|
||||
5808 2348 1 1
|
||||
7480 2633 1 1
|
||||
7480 2633 1 1
|
||||
7480 2633 1 1
|
||||
5511 2633 1 1
|
||||
5511 2633 1 1
|
||||
5511 2633 1 1
|
||||
5511 153 1 1
|
||||
5511 153 1 1
|
||||
5511 153 1 1
|
||||
5839 1789 14 1
|
||||
5839 1805 18 1
|
||||
5839 1821 20 1
|
||||
5839 1837 2 1
|
||||
5839 1852 1 1
|
||||
5839 1868 16 1
|
||||
5839 1884 19 1
|
||||
5721 1884 15 1
|
||||
5721 1868 21 1
|
||||
5721 1852 24 1
|
||||
5721 1837 23 1
|
||||
5721 1821 17 1
|
||||
5721 1805 22 1
|
||||
5721 1789 12 1
|
||||
5780 1837 1 1
|
||||
6589 2187 1 1
|
||||
6589 2150 82 1
|
||||
6589 2112 3 1
|
||||
6687 2112 85 1
|
||||
6687 2150 84 1
|
||||
6687 2187 83 1
|
||||
5511 2296 1 1
|
||||
5511 2264 2 1
|
||||
5511 2219 23 1
|
||||
5511 2180 22 1
|
||||
5511 2160 17 1
|
||||
5511 2121 25 1
|
||||
5511 2044 1 1
|
||||
5511 2075 2 1
|
||||
5511 2101 24 1
|
||||
5511 2140 22 1
|
||||
5511 2199 17 1
|
||||
5511 2239 26 1
|
||||
5466 2391 1 1
|
||||
5466 1949 1 1
|
||||
5309 2391 1 1
|
||||
5309 1949 1 1
|
||||
8503 2281 1 1
|
||||
8503 2478 1 1
|
||||
8109 2577 1 1
|
||||
8109 2380 1 1
|
||||
8109 2183 1 1
|
||||
8011 2478 4 1
|
||||
8011 2281 4 1
|
||||
8011 2675 9 1
|
||||
8011 2085 8 1
|
||||
8306 2085 0 1
|
||||
8306 2675 0 1
|
||||
5695 1336 32 1
|
||||
5695 1183 30 1
|
||||
5645 1336 10 1
|
||||
5645 1183 31 1
|
||||
5595 1336 1 1
|
||||
5595 1183 63 1
|
||||
5545 1336 1 1
|
||||
5545 1183 35 1
|
||||
5495 1336 33 1
|
||||
5495 1183 28 1
|
||||
5445 1336 1 1
|
||||
5445 1183 27 1
|
||||
5395 1336 34 1
|
||||
5395 1183 29 1
|
||||
6503 2494 82 1
|
||||
6804 2494 4 1
|
||||
6788 1226 80 1
|
||||
6788 1600 6 1
|
||||
6527 354 81 1
|
||||
6901 354 5 1
|
||||
6244 2135 3 1
|
||||
6244 2161 3 1
|
||||
6244 2186 3 1
|
||||
6244 2212 36 1
|
||||
6185 2173 37 1
|
||||
6470 865 39 1
|
||||
6520 865 81 1
|
||||
6570 865 86 1
|
||||
6620 865 41 1
|
||||
6620 1060 40 1
|
||||
6570 1060 80 1
|
||||
6520 1060 3 1
|
||||
6470 1060 38 1
|
||||
5959 2212 20 1
|
||||
5959 2187 20 1
|
||||
5959 2161 20 1
|
||||
5959 2135 36 1
|
||||
6018 2174 37 1
|
||||
5749 2188 2 1
|
||||
5814 2188 20 1
|
||||
7787 1611 1 2
|
||||
7832 1656 9 2
|
||||
6724 632 86 1
|
||||
6796 632 1 1
|
||||
7634 3300 42 2
|
||||
7634 3235 6 2
|
||||
5887 390 10 1
|
||||
5847 390 11 1
|
||||
7634 3355 43 2
|
||||
7634 3420 5 2
|
||||
7691 3298 42 2
|
||||
7756 3298 44 2
|
||||
7754 3240 44 2
|
||||
7754 3175 10 2
|
||||
7554 3426 43 2
|
||||
7489 3426 60 2
|
||||
7844 2121 8 2
|
||||
7799 2166 1 2
|
||||
6754 2170 4 1
|
||||
6754 2130 85 1
|
||||
5968 2034 36 1
|
||||
5968 1994 18 1
|
||||
6754 2094 85 1
|
||||
6754 2054 1 1
|
||||
5966 2072 36 1
|
||||
6006 2072 37 1
|
||||
5836 1945 19 1
|
||||
5836 2010 13 1
|
||||
5518 1884 15 1
|
||||
5453 1884 1 1
|
||||
5518 1824 21 1
|
||||
5453 1824 1 1
|
||||
5974 1757 3 1
|
||||
5909 1757 14 1
|
||||
5813 390 7 1
|
||||
5773 390 1 1
|
||||
6444 2062 3 1
|
||||
6444 1997 84 1
|
||||
6638 2056 84 1
|
||||
6678 2056 1 1
|
||||
7219 1566 6 1
|
||||
7219 1271 0 1
|
||||
7219 976 0 1
|
||||
7219 680 0 1
|
||||
7219 385 5 1
|
||||
8400 1566 8 1
|
||||
8400 1271 0 1
|
||||
8400 976 0 1
|
||||
8400 680 0 1
|
||||
8400 385 9 1
|
||||
6722 777 77 1
|
||||
6722 740 1 1
|
||||
6722 703 86 1
|
||||
6811 703 1 1
|
||||
6811 777 10 1
|
||||
5623 798 10 1
|
||||
5623 778 67 1
|
||||
5623 758 73 1
|
||||
5623 739 52 1
|
||||
5623 719 53 1
|
||||
5623 699 45 1
|
||||
5623 680 64 1
|
||||
5623 660 65 1
|
||||
5623 640 50 1
|
||||
5623 621 61 1
|
||||
5623 601 54 1
|
||||
5623 581 74 1
|
||||
5679 526 70 1
|
||||
5699 526 66 1
|
||||
5718 526 62 1
|
||||
5738 526 57 1
|
||||
5758 526 71 1
|
||||
5777 526 58 1
|
||||
5797 526 1 1
|
||||
5817 526 7 1
|
||||
5837 526 11 1
|
||||
5856 526 72 1
|
||||
5876 526 1 1
|
||||
5896 526 10 1
|
||||
5951 581 75 1
|
||||
5951 601 76 1
|
||||
5951 621 60 1
|
||||
5951 640 77 1
|
||||
5951 660 56 1
|
||||
5951 680 49 1
|
||||
5951 699 48 1
|
||||
5951 719 46 1
|
||||
5951 739 47 1
|
||||
5951 758 68 1
|
||||
5951 778 1 1
|
||||
5951 798 10 1
|
||||
5896 853 31 1
|
||||
5876 853 63 1
|
||||
5856 853 28 1
|
||||
5837 853 35 1
|
||||
5817 853 55 1
|
||||
5797 853 59 1
|
||||
5777 853 29 1
|
||||
5758 853 34 1
|
||||
5738 853 69 1
|
||||
5718 853 51 1
|
||||
5699 853 1 1
|
||||
5679 853 10 1
|
||||
7560 3290 42 2
|
||||
7560 3328 1 2
|
||||
7560 3365 43 2
|
||||
7471 3365 60 2
|
||||
7471 3290 4 2
|
||||
6933 872 75 1
|
||||
6933 922 46 1
|
||||
6933 972 1 1
|
||||
6933 1022 47 1
|
||||
6738 1022 40 1
|
||||
6738 972 3 1
|
||||
6738 922 41 1
|
||||
6738 872 76 1
|
||||
6239 865 75 1
|
||||
6289 865 49 1
|
||||
6339 865 1 1
|
||||
6389 865 48 1
|
||||
6389 1060 38 1
|
||||
6339 1060 3 1
|
||||
6289 1060 39 1
|
||||
6239 1060 76 1
|
||||
7827 3305 44 2
|
||||
7827 3230 1 2
|
||||
7900 3268 78 2
|
||||
5984 1266 4 1
|
||||
5947 1266 1 1
|
||||
5910 1266 4 1
|
||||
5910 1177 79 1
|
||||
5984 1177 10 1
|
||||
5482 656 45 1
|
||||
5531 704 1 1
|
||||
5492 743 53 1
|
||||
5443 695 1 1
|
||||
|
||||
NAILS: 12
|
||||
1 6067 1014 47 1
|
||||
1 6067 914 1 1
|
||||
2 6161 979 46 1
|
||||
2 6161 1079 1 1
|
||||
3 6294 627 49 1
|
||||
3 6294 527 1 1
|
||||
4 6200 723 48 1
|
||||
4 6300 723 1 1
|
||||
6 7622 2312 8 1
|
||||
6 7622 2412 9 1
|
||||
7 6857 2068 4 1
|
||||
7 6957 2068 1 1
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,531 @@
|
||||
// Generated by SolidPython <Unknown> on 2025-09-08 06:43:20
|
||||
$fa = 0.4; $fs = 0.4;
|
||||
|
||||
|
||||
rotate(a = 0, v = [1, 0, 0]) {
|
||||
difference() {
|
||||
scale(v = [1, 1, 1]) {
|
||||
difference() {
|
||||
linear_extrude(height = 1.4500000000) {
|
||||
offset(r = 1.0000000000) {
|
||||
import(file = "c64psu-stencil_3d_edge.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
translate(v = [0, 0, 0.1500000000]) {
|
||||
linear_extrude(height = 1.4500000000) {
|
||||
translate(v = [0, 0, 0]) {
|
||||
import(file = "c64psu-stencil_3d_edge.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
linear_extrude(center = true, height = 0.6000000000) {
|
||||
translate(v = [0, 0, 0]) {
|
||||
import(file = "c64psu-stencil_3d_bottom.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************
|
||||
********* SolidPython code: **********
|
||||
************************************************
|
||||
|
||||
from pcbnewTransition import pcbnew
|
||||
import numpy as np
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from kikit.common import *
|
||||
from kikit.defs import *
|
||||
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
|
||||
from kikit.export import gerberImpl, pasteDxfExport
|
||||
from kikit.export import exportSettingsJlcpcb
|
||||
# Allow importing solid from this dir
|
||||
import os
|
||||
import sys
|
||||
cur_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if cur_path not in sys.path:
|
||||
sys.path.insert(0, cur_path)
|
||||
import solid
|
||||
import solid.utils
|
||||
import subprocess
|
||||
import shutil
|
||||
from kikit.common import removeComponents, parseReferences
|
||||
|
||||
from shapely.geometry import Point
|
||||
|
||||
|
||||
OUTER_BORDER = fromMm(7.5)
|
||||
INNER_BORDER = fromMm(5)
|
||||
MOUNTING_HOLES_COUNT = 3
|
||||
MOUNTING_HOLE_R = fromMm(1)
|
||||
HOLE_SPACING = fromMm(20)
|
||||
|
||||
def addBottomCounterpart(board, item):
|
||||
item = item.Duplicate()
|
||||
item.SetLayer(Layer.B_Paste)
|
||||
board.Add(item)
|
||||
|
||||
def addRoundedCorner(board, center, start, end, thickness):
|
||||
corner = pcbnew.PCB_SHAPE()
|
||||
corner.SetShape(STROKE_T.S_ARC)
|
||||
corner.SetCenter(toKiCADPoint((center[0], center[1])))
|
||||
corner.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
|
||||
if np.cross(start - center, end - center) > 0:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(90), True)
|
||||
else:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(-90), True)
|
||||
corner.SetWidth(thickness)
|
||||
corner.SetLayer(Layer.F_Paste)
|
||||
board.Add(corner)
|
||||
addBottomCounterpart(board, corner)
|
||||
|
||||
def addLine(board, start, end, thickness):
|
||||
line = pcbnew.PCB_SHAPE()
|
||||
line.SetShape(STROKE_T.S_SEGMENT)
|
||||
line.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
line.SetEnd(toKiCADPoint((end[0], end[1])))
|
||||
line.SetWidth(thickness)
|
||||
line.SetLayer(Layer.F_Paste)
|
||||
board.Add(line)
|
||||
addBottomCounterpart(board, line)
|
||||
|
||||
def addBite(board, origin, direction, normal, thickness):
|
||||
"""
|
||||
Adds a bite to the stencil, direction points to the bridge, normal points
|
||||
inside the stencil
|
||||
"""
|
||||
direction = normalize(direction) * thickness
|
||||
normal = normalize(normal) * thickness
|
||||
center = toKiCADPoint((origin[0], origin[1])) + toKiCADPoint((normal[0], normal[1]))
|
||||
start = origin
|
||||
end = center + toKiCADPoint((direction[0], direction[1]))
|
||||
# addLine(board, end, end + normal / 2, thickness)
|
||||
addRoundedCorner(board, center, start, end, thickness)
|
||||
|
||||
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
|
||||
"""
|
||||
Return number of bridges which fit inside the length and cut length
|
||||
"""
|
||||
count = round(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
|
||||
cutLength = round((length - (count - 1) * bridgeWidth) / count)
|
||||
return count, cutLength
|
||||
|
||||
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
|
||||
"""
|
||||
Add rectangular frame to the board
|
||||
"""
|
||||
R=fromMm(1)
|
||||
|
||||
corners = [
|
||||
(tl(rect), toKiCADPoint((R, 0)), toKiCADPoint((0, R))), # TL
|
||||
(tr(rect), toKiCADPoint((0, R)), toKiCADPoint((-R, 0))), # TR
|
||||
(br(rect), toKiCADPoint((-R, 0)), toKiCADPoint((0, -R))), # BR
|
||||
(bl(rect), toKiCADPoint((0, -R)), toKiCADPoint((R, 0))) # BL
|
||||
]
|
||||
for c, sOffset, eOffset in corners:
|
||||
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = round(rect.GetX() + R + i * bridgeWidth + i * cutLength)
|
||||
end = start + cutLength
|
||||
|
||||
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
|
||||
addLine(board, toKiCADPoint((start, y1)), toKiCADPoint((end, y1)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y1)), toKiCADPoint((-1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y1)), toKiCADPoint((1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
addLine(board, toKiCADPoint((start, y2)), toKiCADPoint((end, y2)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y2)), toKiCADPoint((-1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y2)), toKiCADPoint((1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
|
||||
end = start + cutLength
|
||||
|
||||
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
|
||||
addLine(board, toKiCADPoint((x1, start)), toKiCADPoint((x1, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x1, start)), toKiCADPoint((0, -1)), toKiCADPoint((1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x1, end)), toKiCADPoint((0, 1)), toKiCADPoint((1, 0)), clearance)
|
||||
addLine(board, toKiCADPoint((x2, start)), toKiCADPoint((x2, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x2, start)), toKiCADPoint((0, -1)), toKiCADPoint((-1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x2, end)), toKiCADPoint((0, 1)), toKiCADPoint((-1, 0)), clearance)
|
||||
|
||||
def addHole(board, position, radius):
|
||||
circle = pcbnew.PCB_SHAPE()
|
||||
circle.SetShape(STROKE_T.S_CIRCLE)
|
||||
circle.SetCenter(toKiCADPoint((position[0], position[1])))
|
||||
# Set 3'oclock point of the circle to set radius
|
||||
circle.SetEnd(toKiCADPoint((position[0], position[1])) + toKiCADPoint((radius/2, 0)))
|
||||
|
||||
circle.SetWidth(radius)
|
||||
circle.SetLayer(Layer.F_Paste)
|
||||
board.Add(circle)
|
||||
addBottomCounterpart(board, circle)
|
||||
|
||||
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
|
||||
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
|
||||
"""
|
||||
Given a Pcbnew board finds the board outline and creates a stencil for
|
||||
KiKit's stencil jig.
|
||||
|
||||
Mainly, adds mounting holes and mouse bites to define the panel outline.
|
||||
|
||||
jigFrameSize is a tuple (width, height).
|
||||
"""
|
||||
bBox = findBoardBoundingBox(board)
|
||||
frameSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
|
||||
cutSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
|
||||
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
|
||||
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((x, OUTER_BORDER / 2 + frameSize.GetY())), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight())), MOUNTING_HOLE_R)
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((OUTER_BORDER / 2 + frameSize.GetX(), y)), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y)), MOUNTING_HOLE_R)
|
||||
|
||||
PIN_TOLERANCE = fromMm(0.05)
|
||||
addHole(board, tl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, tr(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, br(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, bl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
|
||||
def jigMountingHoles(jigFrameSize, origin=toKiCADPoint((0, 0))):
|
||||
""" Get list of all mounting holes in a jig of given size """
|
||||
w, h = jigFrameSize
|
||||
holes = [
|
||||
toKiCADPoint((0, (w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint((0, -(w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint(((h + INNER_BORDER) / 2, 0)),
|
||||
toKiCADPoint((-(h + INNER_BORDER) / 2, 0)),
|
||||
]
|
||||
return [x + origin for x in holes]
|
||||
|
||||
def createOuterPolygon(board, jigFrameSize, outerBorder):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
holes = jigMountingHoles(jigFrameSize, centerpoint)
|
||||
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
|
||||
tabs = []
|
||||
for hole in holes:
|
||||
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, maxHeight=fromMm(1000))
|
||||
tabs.append(tab)
|
||||
outerSubstrate.union(tabs)
|
||||
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
|
||||
outerSubstrate.millFillets(fromMm(3))
|
||||
return outerSubstrate.exterior(), holes
|
||||
|
||||
def createOffsetPolygon(board, offset):
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
|
||||
return outerSubstrate.exterior()
|
||||
|
||||
def m2countersink():
|
||||
HEAD_DIA = fromMm(4.5)
|
||||
HOLE_LEN = fromMm(10)
|
||||
SINK_EXTRA = fromMm(0.3)
|
||||
sinkH = np.sqrt(HEAD_DIA**2 / 4)
|
||||
|
||||
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
|
||||
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
|
||||
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
|
||||
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
|
||||
|
||||
def mirrorX(linestring, origin):
|
||||
return [(2 * origin - x, y) for x, y in linestring]
|
||||
|
||||
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, topSide):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
|
||||
top = jigThickness - fromMm(0.15)
|
||||
pcbBottom = jigThickness - pcbThickness
|
||||
|
||||
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
|
||||
outerRing = outerPolygon.exterior.coords
|
||||
if topSide:
|
||||
outerRing = mirrorX(outerRing, centerpoint[0])
|
||||
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
|
||||
outerRing))
|
||||
|
||||
innerRings = [x.exterior.coords for x in listGeometries(createOffsetPolygon(board, - innerBorder))]
|
||||
if topSide:
|
||||
innerRings = [mirrorX(innerRing, centerpoint[0]) for innerRing in innerRings]
|
||||
|
||||
innerCutout = solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRings[0])))
|
||||
for innerRing in innerRings[1:]:
|
||||
innerCutout = innerCutout + solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
|
||||
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
|
||||
if topSide:
|
||||
registerRing = mirrorX(registerRing, centerpoint[0])
|
||||
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
|
||||
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
|
||||
|
||||
register = body - innerCutout - registerCutout
|
||||
for hole in holes:
|
||||
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
|
||||
return solid.scale(toMm(1))(
|
||||
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
|
||||
|
||||
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, True)
|
||||
|
||||
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, False)
|
||||
|
||||
def renderScad(infile, outfile):
|
||||
infile = os.path.abspath(infile)
|
||||
outfile = os.path.abspath(outfile)
|
||||
try:
|
||||
subprocess.run(["openscad", "-o", outfile, infile],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
message = f"Cannot render {outfile}, OpenSCAD error:\n"
|
||||
message += (e.stdout.decode("utf-8") + "\n") if e.stdout is not None else ""
|
||||
message += (e.stderr.decode("utf-8") + "\n") if e.stderr is not None else ""
|
||||
raise RuntimeError(message)
|
||||
except FileNotFoundError as e:
|
||||
message = f"OpenSCAD is not available.\n"
|
||||
message += f"Did you install it? Program `openscad` has to be in PATH"
|
||||
raise RuntimeError(message)
|
||||
|
||||
def shapelyToSHAPE_POLY_SET(polygon):
|
||||
p = pcbnew.SHAPE_POLY_SET()
|
||||
p.AddOutline(linestringToKicad(polygon.exterior))
|
||||
return p
|
||||
|
||||
def cutoutComponents(board, components):
|
||||
topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd)
|
||||
for polygon in topCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.F_Paste)
|
||||
board.Add(zone)
|
||||
bottomCutout = extractComponentPolygons(components, pcbnew.B_CrtYd)
|
||||
for polygon in bottomCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.B_Paste)
|
||||
board.Add(zone)
|
||||
|
||||
def setStencilLayerVisibility(boardName):
|
||||
prlPath = os.path.splitext(boardName)[0] + ".kicad_prl"
|
||||
try:
|
||||
with open(prlPath, encoding="utf-8") as f:
|
||||
# We use ordered dict, so we preserve the ordering of the keys and
|
||||
# thus, formatting
|
||||
prl = json.load(f, object_pairs_hook=OrderedDict)
|
||||
except FileNotFoundError:
|
||||
# KiCAD didn't generate project local settings, let's create an empty one
|
||||
prl = {
|
||||
"board": {}
|
||||
}
|
||||
prl["board"]["visible_layers"] = "ffc000c_7ffffffe"
|
||||
prl["board"]["visible_items"] = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
13,
|
||||
21,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
34,
|
||||
35
|
||||
]
|
||||
with open(prlPath, "w", encoding="utf-8") as f:
|
||||
json.dump(prl, f, indent=2)
|
||||
pass
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
|
||||
registerborder, tolerance, ignore, cutout):
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
removeComponents(board, refs)
|
||||
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
|
||||
addJigFrame(board, jigsize)
|
||||
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
|
||||
|
||||
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
|
||||
board.Save(stencilFile)
|
||||
|
||||
setStencilLayerVisibility(stencilFile)
|
||||
|
||||
plotPlan = [
|
||||
# name, id, comment
|
||||
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
|
||||
("PasteTop", pcbnew.F_Paste, "Paste top"),
|
||||
]
|
||||
# get a copy of exportSettingsJlcpcb dictionary and
|
||||
# exclude the Edge.Cuts layer for creation of stencil gerber files
|
||||
exportSettings = exportSettingsJlcpcb.copy()
|
||||
exportSettings["ExcludeEdgeLayer"] = True
|
||||
gerberDir = os.path.join(outputdir, "gerber")
|
||||
gerberImpl(stencilFile, gerberDir, plotPlan, False, exportSettings)
|
||||
|
||||
shutil.make_archive(os.path.join(outputdir, "gerbers"), "zip", gerberDir)
|
||||
|
||||
jigthickness = fromMm(jigthickness)
|
||||
pcbthickness = fromMm(pcbthickness)
|
||||
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
|
||||
tolerance = fromMm(tolerance)
|
||||
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
|
||||
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
|
||||
solid.scad_render_to_file(topRegister, topRegisterFile)
|
||||
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
|
||||
|
||||
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
|
||||
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
|
||||
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
|
||||
|
||||
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
|
||||
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
|
||||
body = solid.linear_extrude(height=thickness + frameHeight)(
|
||||
bodyOffset(solid.import_dxf(outlineDxf)))
|
||||
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
|
||||
board = solid.utils.up(thickness)(
|
||||
solid.linear_extrude(height=thickness + frameHeight)(
|
||||
boardOffset(solid.import_dxf(outlineDxf))))
|
||||
return body - board
|
||||
|
||||
def getComponents(board, references):
|
||||
"""
|
||||
Return a list of components based on designator
|
||||
"""
|
||||
return [f for f in board.GetFootprints() if f.GetReference() in references]
|
||||
|
||||
def collectFootprintEdges(footprint, layer):
|
||||
"""
|
||||
Return all edges on given layer in given footprint
|
||||
"""
|
||||
return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer]
|
||||
|
||||
def extractComponentPolygons(footprints, srcLayer):
|
||||
"""
|
||||
Return a list of shapely polygons with holes for already placed components.
|
||||
The source layer defines the geometry on which the cutout is computed.
|
||||
Usually it a font or back courtyard
|
||||
"""
|
||||
polygons = []
|
||||
for f in footprints:
|
||||
edges = collectFootprintEdges(f, srcLayer)
|
||||
for ring in extractRings(edges):
|
||||
polygons.append(toShapely(ring, edges))
|
||||
return polygons
|
||||
|
||||
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
|
||||
frameClearance, enlargeHoles, front):
|
||||
zScale = -1 if front else 1
|
||||
xRotate = 180 if front else 0
|
||||
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
|
||||
thickness, frameHeight, frameWidth, frameClearance))
|
||||
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
|
||||
holes = solid.linear_extrude(height=4*thickness, center=True)(
|
||||
holesOffset(solid.import_dxf(holesDxf)))
|
||||
substrate -= holes
|
||||
for h in extraHoles:
|
||||
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
|
||||
solid.linear_extrude(height=4*thickness, center=True)(
|
||||
solid.polygon(h.exterior.coords)))
|
||||
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
|
||||
|
||||
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
|
||||
ignore, cutout, frameclearance, enlargeholes):
|
||||
"""
|
||||
Create a 3D printed self-registering stencil.
|
||||
"""
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
cutoutComponents = getComponents(board, parseReferences(cutout))
|
||||
removeComponents(board, refs)
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# We create the stencil based on DXF export. Using it avoids the necessity
|
||||
# to interpret KiCAD PAD shapes which constantly change with newer and newer
|
||||
# versions.
|
||||
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
|
||||
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
|
||||
# On Windows, OpenSCAD requires to use forward slashes instead of backslashes,
|
||||
# hence, the replacement:
|
||||
if os.name == "nt":
|
||||
bottomPaste = bottomPaste.replace("\\", "/")
|
||||
topPaste = topPaste.replace("\\", "/")
|
||||
outline = outline.replace("\\", "/")
|
||||
|
||||
topCutout = extractComponentPolygons(cutoutComponents, pcbnew.F_CrtYd)
|
||||
bottomCutout = extractComponentPolygons(cutoutComponents, pcbnew.B_CrtYd)
|
||||
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
|
||||
framewidth, frameclearance, enlargeholes, True)
|
||||
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
|
||||
height, framewidth, frameclearance, enlargeholes, False)
|
||||
|
||||
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
|
||||
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
|
||||
|
||||
topStencilFile = os.path.join(outputdir, "topStencil.scad")
|
||||
solid.scad_render_to_file(topStencil, topStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
************************************************/
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,386 @@
|
||||
0
|
||||
SECTION
|
||||
2
|
||||
HEADER
|
||||
9
|
||||
$ANGBASE
|
||||
50
|
||||
0.0
|
||||
9
|
||||
$ANGDIR
|
||||
70
|
||||
1
|
||||
9
|
||||
$MEASUREMENT
|
||||
70
|
||||
1
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
SECTION
|
||||
2
|
||||
TABLES
|
||||
0
|
||||
TABLE
|
||||
2
|
||||
LTYPE
|
||||
70
|
||||
4
|
||||
0
|
||||
LTYPE
|
||||
5
|
||||
40F
|
||||
2
|
||||
CONTINUOUS
|
||||
70
|
||||
0
|
||||
3
|
||||
Solid line
|
||||
72
|
||||
65
|
||||
73
|
||||
0
|
||||
40
|
||||
0.0
|
||||
0
|
||||
LTYPE
|
||||
5
|
||||
410
|
||||
2
|
||||
DASHDOT
|
||||
70
|
||||
0
|
||||
3
|
||||
Dash Dot ____ _ ____ _
|
||||
72
|
||||
65
|
||||
73
|
||||
4
|
||||
40
|
||||
2.0
|
||||
49
|
||||
1.25
|
||||
49
|
||||
-0.25
|
||||
49
|
||||
0.25
|
||||
49
|
||||
-0.25
|
||||
0
|
||||
LTYPE
|
||||
5
|
||||
411
|
||||
2
|
||||
DASHED
|
||||
70
|
||||
0
|
||||
3
|
||||
Dashed __ __ __ __ __
|
||||
72
|
||||
65
|
||||
73
|
||||
2
|
||||
40
|
||||
0.75
|
||||
49
|
||||
0.5
|
||||
49
|
||||
-0.25
|
||||
0
|
||||
LTYPE
|
||||
5
|
||||
43B
|
||||
2
|
||||
DOTTED
|
||||
70
|
||||
0
|
||||
3
|
||||
Dotted . . . .
|
||||
72
|
||||
65
|
||||
73
|
||||
2
|
||||
40
|
||||
0.2
|
||||
49
|
||||
0.0
|
||||
49
|
||||
-0.2
|
||||
0
|
||||
ENDTAB
|
||||
0
|
||||
TABLE
|
||||
2
|
||||
STYLE
|
||||
70
|
||||
4
|
||||
0
|
||||
STYLE
|
||||
2
|
||||
KICAD
|
||||
70
|
||||
0
|
||||
40
|
||||
0
|
||||
41
|
||||
1
|
||||
42
|
||||
1
|
||||
50
|
||||
0
|
||||
71
|
||||
0
|
||||
3
|
||||
isocp.shx
|
||||
0
|
||||
STYLE
|
||||
2
|
||||
KICADB
|
||||
70
|
||||
0
|
||||
40
|
||||
0
|
||||
41
|
||||
1
|
||||
42
|
||||
1
|
||||
50
|
||||
0
|
||||
71
|
||||
0
|
||||
3
|
||||
isocp.shx
|
||||
0
|
||||
STYLE
|
||||
2
|
||||
KICADI
|
||||
70
|
||||
0
|
||||
40
|
||||
0
|
||||
41
|
||||
1
|
||||
42
|
||||
1
|
||||
50
|
||||
15
|
||||
71
|
||||
0
|
||||
3
|
||||
isocp.shx
|
||||
0
|
||||
STYLE
|
||||
2
|
||||
KICADBI
|
||||
70
|
||||
0
|
||||
40
|
||||
0
|
||||
41
|
||||
1
|
||||
42
|
||||
1
|
||||
50
|
||||
15
|
||||
71
|
||||
0
|
||||
3
|
||||
isocp.shx
|
||||
0
|
||||
ENDTAB
|
||||
0
|
||||
TABLE
|
||||
2
|
||||
LAYER
|
||||
70
|
||||
1
|
||||
0
|
||||
LAYER
|
||||
2
|
||||
BLACK
|
||||
70
|
||||
0
|
||||
62
|
||||
7
|
||||
6
|
||||
CONTINUOUS
|
||||
0
|
||||
ENDTAB
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
SECTION
|
||||
2
|
||||
ENTITIES
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
219.2245509999999911
|
||||
20
|
||||
-37.0334679999999992
|
||||
11
|
||||
219.2500619999999856
|
||||
21
|
||||
-108.9006239999999934
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
219.2500619999999856
|
||||
20
|
||||
-108.9006239999999934
|
||||
11
|
||||
133.100087000000002
|
||||
21
|
||||
-108.875624000000002
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
133.100087000000002
|
||||
20
|
||||
-108.875624000000002
|
||||
11
|
||||
133.0750740000000008
|
||||
21
|
||||
-58.3999999999999986
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
133.0750740000000008
|
||||
20
|
||||
-58.3999999999999986
|
||||
11
|
||||
139.6749999999999829
|
||||
21
|
||||
-58.3999999999999986
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.6749999999999829
|
||||
20
|
||||
-58.3999999999999986
|
||||
11
|
||||
139.6749999999999829
|
||||
21
|
||||
-57.7999999999999972
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.6749999999999829
|
||||
20
|
||||
-57.7999999999999972
|
||||
11
|
||||
139.25
|
||||
21
|
||||
-57.7999999999999972
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.25
|
||||
20
|
||||
-57.7999999999999972
|
||||
11
|
||||
139.25
|
||||
21
|
||||
-49.75
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.25
|
||||
20
|
||||
-49.75
|
||||
11
|
||||
139.6999999999999886
|
||||
21
|
||||
-49.75
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.6999999999999886
|
||||
20
|
||||
-49.75
|
||||
11
|
||||
139.6999999999999886
|
||||
21
|
||||
-49.1499999999999986
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
139.6999999999999886
|
||||
20
|
||||
-49.1499999999999986
|
||||
11
|
||||
133.0749999999999886
|
||||
21
|
||||
-49.1499999999999986
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
133.0749999999999886
|
||||
20
|
||||
-49.1499999999999986
|
||||
11
|
||||
133.0749999999999886
|
||||
21
|
||||
-37.0084680000000006
|
||||
0
|
||||
LINE
|
||||
8
|
||||
BLACK
|
||||
6
|
||||
CONTINUOUS
|
||||
10
|
||||
133.0749999999999886
|
||||
20
|
||||
-37.0084680000000006
|
||||
11
|
||||
219.2245509999999911
|
||||
21
|
||||
-37.0334679999999992
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
EOF
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,531 @@
|
||||
// Generated by SolidPython <Unknown> on 2025-09-08 06:43:20
|
||||
$fa = 0.4; $fs = 0.4;
|
||||
|
||||
|
||||
rotate(a = 180, v = [1, 0, 0]) {
|
||||
difference() {
|
||||
scale(v = [1, 1, -1]) {
|
||||
difference() {
|
||||
linear_extrude(height = 1.4500000000) {
|
||||
offset(r = 1.0000000000) {
|
||||
import(file = "c64psu-stencil_3d_edge.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
translate(v = [0, 0, 0.1500000000]) {
|
||||
linear_extrude(height = 1.4500000000) {
|
||||
translate(v = [0, 0, 0]) {
|
||||
import(file = "c64psu-stencil_3d_edge.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
linear_extrude(center = true, height = 0.6000000000) {
|
||||
translate(v = [0, 0, 0]) {
|
||||
import(file = "c64psu-stencil_3d_top.dxf", origin = [0, 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************
|
||||
********* SolidPython code: **********
|
||||
************************************************
|
||||
|
||||
from pcbnewTransition import pcbnew
|
||||
import numpy as np
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from kikit.common import *
|
||||
from kikit.defs import *
|
||||
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
|
||||
from kikit.export import gerberImpl, pasteDxfExport
|
||||
from kikit.export import exportSettingsJlcpcb
|
||||
# Allow importing solid from this dir
|
||||
import os
|
||||
import sys
|
||||
cur_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if cur_path not in sys.path:
|
||||
sys.path.insert(0, cur_path)
|
||||
import solid
|
||||
import solid.utils
|
||||
import subprocess
|
||||
import shutil
|
||||
from kikit.common import removeComponents, parseReferences
|
||||
|
||||
from shapely.geometry import Point
|
||||
|
||||
|
||||
OUTER_BORDER = fromMm(7.5)
|
||||
INNER_BORDER = fromMm(5)
|
||||
MOUNTING_HOLES_COUNT = 3
|
||||
MOUNTING_HOLE_R = fromMm(1)
|
||||
HOLE_SPACING = fromMm(20)
|
||||
|
||||
def addBottomCounterpart(board, item):
|
||||
item = item.Duplicate()
|
||||
item.SetLayer(Layer.B_Paste)
|
||||
board.Add(item)
|
||||
|
||||
def addRoundedCorner(board, center, start, end, thickness):
|
||||
corner = pcbnew.PCB_SHAPE()
|
||||
corner.SetShape(STROKE_T.S_ARC)
|
||||
corner.SetCenter(toKiCADPoint((center[0], center[1])))
|
||||
corner.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
|
||||
if np.cross(start - center, end - center) > 0:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(90), True)
|
||||
else:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(-90), True)
|
||||
corner.SetWidth(thickness)
|
||||
corner.SetLayer(Layer.F_Paste)
|
||||
board.Add(corner)
|
||||
addBottomCounterpart(board, corner)
|
||||
|
||||
def addLine(board, start, end, thickness):
|
||||
line = pcbnew.PCB_SHAPE()
|
||||
line.SetShape(STROKE_T.S_SEGMENT)
|
||||
line.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
line.SetEnd(toKiCADPoint((end[0], end[1])))
|
||||
line.SetWidth(thickness)
|
||||
line.SetLayer(Layer.F_Paste)
|
||||
board.Add(line)
|
||||
addBottomCounterpart(board, line)
|
||||
|
||||
def addBite(board, origin, direction, normal, thickness):
|
||||
"""
|
||||
Adds a bite to the stencil, direction points to the bridge, normal points
|
||||
inside the stencil
|
||||
"""
|
||||
direction = normalize(direction) * thickness
|
||||
normal = normalize(normal) * thickness
|
||||
center = toKiCADPoint((origin[0], origin[1])) + toKiCADPoint((normal[0], normal[1]))
|
||||
start = origin
|
||||
end = center + toKiCADPoint((direction[0], direction[1]))
|
||||
# addLine(board, end, end + normal / 2, thickness)
|
||||
addRoundedCorner(board, center, start, end, thickness)
|
||||
|
||||
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
|
||||
"""
|
||||
Return number of bridges which fit inside the length and cut length
|
||||
"""
|
||||
count = round(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
|
||||
cutLength = round((length - (count - 1) * bridgeWidth) / count)
|
||||
return count, cutLength
|
||||
|
||||
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
|
||||
"""
|
||||
Add rectangular frame to the board
|
||||
"""
|
||||
R=fromMm(1)
|
||||
|
||||
corners = [
|
||||
(tl(rect), toKiCADPoint((R, 0)), toKiCADPoint((0, R))), # TL
|
||||
(tr(rect), toKiCADPoint((0, R)), toKiCADPoint((-R, 0))), # TR
|
||||
(br(rect), toKiCADPoint((-R, 0)), toKiCADPoint((0, -R))), # BR
|
||||
(bl(rect), toKiCADPoint((0, -R)), toKiCADPoint((R, 0))) # BL
|
||||
]
|
||||
for c, sOffset, eOffset in corners:
|
||||
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = round(rect.GetX() + R + i * bridgeWidth + i * cutLength)
|
||||
end = start + cutLength
|
||||
|
||||
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
|
||||
addLine(board, toKiCADPoint((start, y1)), toKiCADPoint((end, y1)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y1)), toKiCADPoint((-1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y1)), toKiCADPoint((1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
addLine(board, toKiCADPoint((start, y2)), toKiCADPoint((end, y2)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y2)), toKiCADPoint((-1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y2)), toKiCADPoint((1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
|
||||
end = start + cutLength
|
||||
|
||||
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
|
||||
addLine(board, toKiCADPoint((x1, start)), toKiCADPoint((x1, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x1, start)), toKiCADPoint((0, -1)), toKiCADPoint((1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x1, end)), toKiCADPoint((0, 1)), toKiCADPoint((1, 0)), clearance)
|
||||
addLine(board, toKiCADPoint((x2, start)), toKiCADPoint((x2, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x2, start)), toKiCADPoint((0, -1)), toKiCADPoint((-1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x2, end)), toKiCADPoint((0, 1)), toKiCADPoint((-1, 0)), clearance)
|
||||
|
||||
def addHole(board, position, radius):
|
||||
circle = pcbnew.PCB_SHAPE()
|
||||
circle.SetShape(STROKE_T.S_CIRCLE)
|
||||
circle.SetCenter(toKiCADPoint((position[0], position[1])))
|
||||
# Set 3'oclock point of the circle to set radius
|
||||
circle.SetEnd(toKiCADPoint((position[0], position[1])) + toKiCADPoint((radius/2, 0)))
|
||||
|
||||
circle.SetWidth(radius)
|
||||
circle.SetLayer(Layer.F_Paste)
|
||||
board.Add(circle)
|
||||
addBottomCounterpart(board, circle)
|
||||
|
||||
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
|
||||
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
|
||||
"""
|
||||
Given a Pcbnew board finds the board outline and creates a stencil for
|
||||
KiKit's stencil jig.
|
||||
|
||||
Mainly, adds mounting holes and mouse bites to define the panel outline.
|
||||
|
||||
jigFrameSize is a tuple (width, height).
|
||||
"""
|
||||
bBox = findBoardBoundingBox(board)
|
||||
frameSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
|
||||
cutSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
|
||||
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
|
||||
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((x, OUTER_BORDER / 2 + frameSize.GetY())), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight())), MOUNTING_HOLE_R)
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((OUTER_BORDER / 2 + frameSize.GetX(), y)), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y)), MOUNTING_HOLE_R)
|
||||
|
||||
PIN_TOLERANCE = fromMm(0.05)
|
||||
addHole(board, tl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, tr(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, br(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, bl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
|
||||
def jigMountingHoles(jigFrameSize, origin=toKiCADPoint((0, 0))):
|
||||
""" Get list of all mounting holes in a jig of given size """
|
||||
w, h = jigFrameSize
|
||||
holes = [
|
||||
toKiCADPoint((0, (w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint((0, -(w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint(((h + INNER_BORDER) / 2, 0)),
|
||||
toKiCADPoint((-(h + INNER_BORDER) / 2, 0)),
|
||||
]
|
||||
return [x + origin for x in holes]
|
||||
|
||||
def createOuterPolygon(board, jigFrameSize, outerBorder):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
holes = jigMountingHoles(jigFrameSize, centerpoint)
|
||||
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
|
||||
tabs = []
|
||||
for hole in holes:
|
||||
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, maxHeight=fromMm(1000))
|
||||
tabs.append(tab)
|
||||
outerSubstrate.union(tabs)
|
||||
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
|
||||
outerSubstrate.millFillets(fromMm(3))
|
||||
return outerSubstrate.exterior(), holes
|
||||
|
||||
def createOffsetPolygon(board, offset):
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
|
||||
return outerSubstrate.exterior()
|
||||
|
||||
def m2countersink():
|
||||
HEAD_DIA = fromMm(4.5)
|
||||
HOLE_LEN = fromMm(10)
|
||||
SINK_EXTRA = fromMm(0.3)
|
||||
sinkH = np.sqrt(HEAD_DIA**2 / 4)
|
||||
|
||||
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
|
||||
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
|
||||
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
|
||||
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
|
||||
|
||||
def mirrorX(linestring, origin):
|
||||
return [(2 * origin - x, y) for x, y in linestring]
|
||||
|
||||
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, topSide):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
|
||||
top = jigThickness - fromMm(0.15)
|
||||
pcbBottom = jigThickness - pcbThickness
|
||||
|
||||
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
|
||||
outerRing = outerPolygon.exterior.coords
|
||||
if topSide:
|
||||
outerRing = mirrorX(outerRing, centerpoint[0])
|
||||
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
|
||||
outerRing))
|
||||
|
||||
innerRings = [x.exterior.coords for x in listGeometries(createOffsetPolygon(board, - innerBorder))]
|
||||
if topSide:
|
||||
innerRings = [mirrorX(innerRing, centerpoint[0]) for innerRing in innerRings]
|
||||
|
||||
innerCutout = solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRings[0])))
|
||||
for innerRing in innerRings[1:]:
|
||||
innerCutout = innerCutout + solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
|
||||
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
|
||||
if topSide:
|
||||
registerRing = mirrorX(registerRing, centerpoint[0])
|
||||
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
|
||||
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
|
||||
|
||||
register = body - innerCutout - registerCutout
|
||||
for hole in holes:
|
||||
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
|
||||
return solid.scale(toMm(1))(
|
||||
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
|
||||
|
||||
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, True)
|
||||
|
||||
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, False)
|
||||
|
||||
def renderScad(infile, outfile):
|
||||
infile = os.path.abspath(infile)
|
||||
outfile = os.path.abspath(outfile)
|
||||
try:
|
||||
subprocess.run(["openscad", "-o", outfile, infile],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
message = f"Cannot render {outfile}, OpenSCAD error:\n"
|
||||
message += (e.stdout.decode("utf-8") + "\n") if e.stdout is not None else ""
|
||||
message += (e.stderr.decode("utf-8") + "\n") if e.stderr is not None else ""
|
||||
raise RuntimeError(message)
|
||||
except FileNotFoundError as e:
|
||||
message = f"OpenSCAD is not available.\n"
|
||||
message += f"Did you install it? Program `openscad` has to be in PATH"
|
||||
raise RuntimeError(message)
|
||||
|
||||
def shapelyToSHAPE_POLY_SET(polygon):
|
||||
p = pcbnew.SHAPE_POLY_SET()
|
||||
p.AddOutline(linestringToKicad(polygon.exterior))
|
||||
return p
|
||||
|
||||
def cutoutComponents(board, components):
|
||||
topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd)
|
||||
for polygon in topCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.F_Paste)
|
||||
board.Add(zone)
|
||||
bottomCutout = extractComponentPolygons(components, pcbnew.B_CrtYd)
|
||||
for polygon in bottomCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.B_Paste)
|
||||
board.Add(zone)
|
||||
|
||||
def setStencilLayerVisibility(boardName):
|
||||
prlPath = os.path.splitext(boardName)[0] + ".kicad_prl"
|
||||
try:
|
||||
with open(prlPath, encoding="utf-8") as f:
|
||||
# We use ordered dict, so we preserve the ordering of the keys and
|
||||
# thus, formatting
|
||||
prl = json.load(f, object_pairs_hook=OrderedDict)
|
||||
except FileNotFoundError:
|
||||
# KiCAD didn't generate project local settings, let's create an empty one
|
||||
prl = {
|
||||
"board": {}
|
||||
}
|
||||
prl["board"]["visible_layers"] = "ffc000c_7ffffffe"
|
||||
prl["board"]["visible_items"] = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
13,
|
||||
21,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
34,
|
||||
35
|
||||
]
|
||||
with open(prlPath, "w", encoding="utf-8") as f:
|
||||
json.dump(prl, f, indent=2)
|
||||
pass
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
|
||||
registerborder, tolerance, ignore, cutout):
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
removeComponents(board, refs)
|
||||
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
|
||||
addJigFrame(board, jigsize)
|
||||
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
|
||||
|
||||
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
|
||||
board.Save(stencilFile)
|
||||
|
||||
setStencilLayerVisibility(stencilFile)
|
||||
|
||||
plotPlan = [
|
||||
# name, id, comment
|
||||
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
|
||||
("PasteTop", pcbnew.F_Paste, "Paste top"),
|
||||
]
|
||||
# get a copy of exportSettingsJlcpcb dictionary and
|
||||
# exclude the Edge.Cuts layer for creation of stencil gerber files
|
||||
exportSettings = exportSettingsJlcpcb.copy()
|
||||
exportSettings["ExcludeEdgeLayer"] = True
|
||||
gerberDir = os.path.join(outputdir, "gerber")
|
||||
gerberImpl(stencilFile, gerberDir, plotPlan, False, exportSettings)
|
||||
|
||||
shutil.make_archive(os.path.join(outputdir, "gerbers"), "zip", gerberDir)
|
||||
|
||||
jigthickness = fromMm(jigthickness)
|
||||
pcbthickness = fromMm(pcbthickness)
|
||||
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
|
||||
tolerance = fromMm(tolerance)
|
||||
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
|
||||
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
|
||||
solid.scad_render_to_file(topRegister, topRegisterFile)
|
||||
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
|
||||
|
||||
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
|
||||
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
|
||||
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
|
||||
|
||||
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
|
||||
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
|
||||
body = solid.linear_extrude(height=thickness + frameHeight)(
|
||||
bodyOffset(solid.import_dxf(outlineDxf)))
|
||||
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
|
||||
board = solid.utils.up(thickness)(
|
||||
solid.linear_extrude(height=thickness + frameHeight)(
|
||||
boardOffset(solid.import_dxf(outlineDxf))))
|
||||
return body - board
|
||||
|
||||
def getComponents(board, references):
|
||||
"""
|
||||
Return a list of components based on designator
|
||||
"""
|
||||
return [f for f in board.GetFootprints() if f.GetReference() in references]
|
||||
|
||||
def collectFootprintEdges(footprint, layer):
|
||||
"""
|
||||
Return all edges on given layer in given footprint
|
||||
"""
|
||||
return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer]
|
||||
|
||||
def extractComponentPolygons(footprints, srcLayer):
|
||||
"""
|
||||
Return a list of shapely polygons with holes for already placed components.
|
||||
The source layer defines the geometry on which the cutout is computed.
|
||||
Usually it a font or back courtyard
|
||||
"""
|
||||
polygons = []
|
||||
for f in footprints:
|
||||
edges = collectFootprintEdges(f, srcLayer)
|
||||
for ring in extractRings(edges):
|
||||
polygons.append(toShapely(ring, edges))
|
||||
return polygons
|
||||
|
||||
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
|
||||
frameClearance, enlargeHoles, front):
|
||||
zScale = -1 if front else 1
|
||||
xRotate = 180 if front else 0
|
||||
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
|
||||
thickness, frameHeight, frameWidth, frameClearance))
|
||||
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
|
||||
holes = solid.linear_extrude(height=4*thickness, center=True)(
|
||||
holesOffset(solid.import_dxf(holesDxf)))
|
||||
substrate -= holes
|
||||
for h in extraHoles:
|
||||
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
|
||||
solid.linear_extrude(height=4*thickness, center=True)(
|
||||
solid.polygon(h.exterior.coords)))
|
||||
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
|
||||
|
||||
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
|
||||
ignore, cutout, frameclearance, enlargeholes):
|
||||
"""
|
||||
Create a 3D printed self-registering stencil.
|
||||
"""
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
cutoutComponents = getComponents(board, parseReferences(cutout))
|
||||
removeComponents(board, refs)
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# We create the stencil based on DXF export. Using it avoids the necessity
|
||||
# to interpret KiCAD PAD shapes which constantly change with newer and newer
|
||||
# versions.
|
||||
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
|
||||
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
|
||||
# On Windows, OpenSCAD requires to use forward slashes instead of backslashes,
|
||||
# hence, the replacement:
|
||||
if os.name == "nt":
|
||||
bottomPaste = bottomPaste.replace("\\", "/")
|
||||
topPaste = topPaste.replace("\\", "/")
|
||||
outline = outline.replace("\\", "/")
|
||||
|
||||
topCutout = extractComponentPolygons(cutoutComponents, pcbnew.F_CrtYd)
|
||||
bottomCutout = extractComponentPolygons(cutoutComponents, pcbnew.B_CrtYd)
|
||||
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
|
||||
framewidth, frameclearance, enlargeholes, True)
|
||||
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
|
||||
height, framewidth, frameclearance, enlargeholes, False)
|
||||
|
||||
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
|
||||
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
|
||||
|
||||
topStencilFile = os.path.join(outputdir, "topStencil.scad")
|
||||
solid.scad_render_to_file(topStencil, topStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
************************************************/
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"Header": {
|
||||
"GenerationSoftware": {
|
||||
"Vendor": "KiCad",
|
||||
"Application": "Pcbnew",
|
||||
"Version": "9.0.1+1"
|
||||
},
|
||||
"CreationDate": "2025-09-08T06:43:27+00:00"
|
||||
},
|
||||
"GeneralSpecs": {
|
||||
"ProjectId": {
|
||||
"Name": "stencil",
|
||||
"GUID": "7374656e-6369-46c2-9e6b-696361645f70",
|
||||
"Revision": "rev?"
|
||||
},
|
||||
"Size": {
|
||||
"X": 86.2251,
|
||||
"Y": 71.9422
|
||||
},
|
||||
"LayerNumber": 4,
|
||||
"BoardThickness": 1.6,
|
||||
"Finish": "None"
|
||||
},
|
||||
"DesignRules": [
|
||||
{
|
||||
"Layers": "Outer",
|
||||
"PadToPad": 0.2,
|
||||
"PadToTrack": 0.2,
|
||||
"TrackToTrack": 0.2,
|
||||
"MinLineWidth": 0.17,
|
||||
"TrackToRegion": 0.2,
|
||||
"RegionToRegion": 0.2
|
||||
},
|
||||
{
|
||||
"Layers": "Inner",
|
||||
"PadToPad": 0.2,
|
||||
"PadToTrack": 0.2,
|
||||
"TrackToTrack": 0.2,
|
||||
"MinLineWidth": 0.21,
|
||||
"TrackToRegion": 0.4,
|
||||
"RegionToRegion": 0.4
|
||||
}
|
||||
],
|
||||
"FilesAttributes": [
|
||||
{
|
||||
"Path": "c64psu-stencil_for_jig_bottom.gbp",
|
||||
"FileFunction": "SolderPaste,Bot",
|
||||
"FilePolarity": "Positive"
|
||||
},
|
||||
{
|
||||
"Path": "c64psu-stencil_for_jig_top.gtp",
|
||||
"FileFunction": "SolderPaste,Top",
|
||||
"FilePolarity": "Positive"
|
||||
}
|
||||
],
|
||||
"MaterialStackup": [
|
||||
{
|
||||
"Type": "Legend",
|
||||
"Name": "Top Silk Screen"
|
||||
},
|
||||
{
|
||||
"Type": "SolderPaste",
|
||||
"Name": "Top Solder Paste"
|
||||
},
|
||||
{
|
||||
"Type": "SolderMask",
|
||||
"Thickness": 0.01,
|
||||
"Name": "Top Solder Mask"
|
||||
},
|
||||
{
|
||||
"Type": "Copper",
|
||||
"Thickness": 0.035,
|
||||
"Name": "F.Cu"
|
||||
},
|
||||
{
|
||||
"Type": "Dielectric",
|
||||
"Thickness": 0.1,
|
||||
"Material": "FR4",
|
||||
"Name": "F.Cu/In1.Cu",
|
||||
"Notes": "Type: dielectric layer 1 (from F.Cu to In1.Cu)"
|
||||
},
|
||||
{
|
||||
"Type": "Copper",
|
||||
"Thickness": 0.035,
|
||||
"Name": "In1.Cu"
|
||||
},
|
||||
{
|
||||
"Type": "Dielectric",
|
||||
"Thickness": 1.24,
|
||||
"Material": "FR4",
|
||||
"Name": "In1.Cu/In2.Cu",
|
||||
"Notes": "Type: dielectric layer 2 (from In1.Cu to In2.Cu)"
|
||||
},
|
||||
{
|
||||
"Type": "Copper",
|
||||
"Thickness": 0.035,
|
||||
"Name": "In2.Cu"
|
||||
},
|
||||
{
|
||||
"Type": "Dielectric",
|
||||
"Thickness": 0.1,
|
||||
"Material": "FR4",
|
||||
"Name": "In2.Cu/B.Cu",
|
||||
"Notes": "Type: dielectric layer 3 (from In2.Cu to B.Cu)"
|
||||
},
|
||||
{
|
||||
"Type": "Copper",
|
||||
"Thickness": 0.035,
|
||||
"Name": "B.Cu"
|
||||
},
|
||||
{
|
||||
"Type": "SolderMask",
|
||||
"Thickness": 0.01,
|
||||
"Name": "Bottom Solder Mask"
|
||||
},
|
||||
{
|
||||
"Type": "SolderPaste",
|
||||
"Name": "Bottom Solder Paste"
|
||||
},
|
||||
{
|
||||
"Type": "Legend",
|
||||
"Name": "Bottom Silk Screen"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.1+1*
|
||||
G04 #@! TF.CreationDate,2025-09-08T06:43:27+00:00*
|
||||
G04 #@! TF.ProjectId,stencil,7374656e-6369-46c2-9e6b-696361645f70,rev?*
|
||||
G04 #@! TF.SameCoordinates,Original*
|
||||
G04 #@! TF.FileFunction,Paste,Bot*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
%FSLAX46Y46*%
|
||||
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
|
||||
G04 Created by KiCad (PCBNEW 9.0.1+1) date 2025-09-08 06:43:27*
|
||||
%MOMM*%
|
||||
%LPD*%
|
||||
G01*
|
||||
G04 APERTURE LIST*
|
||||
G04 Aperture macros list*
|
||||
%AMRoundRect*
|
||||
0 Rectangle with rounded corners*
|
||||
0 $1 Rounding radius*
|
||||
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
|
||||
0 Add a 4 corners polygon primitive as box body*
|
||||
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
|
||||
0 Add four circle primitives for the rounded corners*
|
||||
1,1,$1+$1,$2,$3*
|
||||
1,1,$1+$1,$4,$5*
|
||||
1,1,$1+$1,$6,$7*
|
||||
1,1,$1+$1,$8,$9*
|
||||
0 Add four rect primitives between the rounded corners*
|
||||
20,1,$1+$1,$2,$3,$4,$5,0*
|
||||
20,1,$1+$1,$4,$5,$6,$7,0*
|
||||
20,1,$1+$1,$6,$7,$8,$9,0*
|
||||
20,1,$1+$1,$8,$9,$2,$3,0*%
|
||||
G04 Aperture macros list end*
|
||||
%ADD10C,0.500000*%
|
||||
%ADD11C,1.000000*%
|
||||
%ADD12C,1.050000*%
|
||||
%ADD13RoundRect,0.250000X0.650000X-1.000000X0.650000X1.000000X-0.650000X1.000000X-0.650000X-1.000000X0*%
|
||||
%ADD14RoundRect,0.200000X-0.275000X0.200000X-0.275000X-0.200000X0.275000X-0.200000X0.275000X0.200000X0*%
|
||||
%ADD15RoundRect,0.150000X0.512500X0.150000X-0.512500X0.150000X-0.512500X-0.150000X0.512500X-0.150000X0*%
|
||||
%ADD16RoundRect,0.250000X-0.650000X0.325000X-0.650000X-0.325000X0.650000X-0.325000X0.650000X0.325000X0*%
|
||||
%ADD17RoundRect,0.200000X0.200000X0.275000X-0.200000X0.275000X-0.200000X-0.275000X0.200000X-0.275000X0*%
|
||||
%ADD18RoundRect,0.200000X0.275000X-0.200000X0.275000X0.200000X-0.275000X0.200000X-0.275000X-0.200000X0*%
|
||||
%ADD19RoundRect,0.200000X-0.053033X0.335876X-0.335876X0.053033X0.053033X-0.335876X0.335876X-0.053033X0*%
|
||||
%ADD20RoundRect,0.200000X-0.200000X-0.275000X0.200000X-0.275000X0.200000X0.275000X-0.200000X0.275000X0*%
|
||||
%ADD21RoundRect,0.140000X-0.170000X0.140000X-0.170000X-0.140000X0.170000X-0.140000X0.170000X0.140000X0*%
|
||||
%ADD22RoundRect,0.150000X-0.587500X-0.150000X0.587500X-0.150000X0.587500X0.150000X-0.587500X0.150000X0*%
|
||||
%ADD23RoundRect,0.140000X0.170000X-0.140000X0.170000X0.140000X-0.170000X0.140000X-0.170000X-0.140000X0*%
|
||||
%ADD24RoundRect,0.200000X-0.335876X-0.053033X-0.053033X-0.335876X0.335876X0.053033X0.053033X0.335876X0*%
|
||||
G04 APERTURE END LIST*
|
||||
D10*
|
||||
X114662531Y-84854546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-84354546I-31J500046D01*
|
||||
G01*
|
||||
X114662531Y-35254546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-34754546I-31J500046D01*
|
||||
G01*
|
||||
X238162531Y-24354546D02*
|
||||
X238162531Y-34754546D01*
|
||||
X187562531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X188062454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
D11*
|
||||
X206037531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X205037531Y-131704546I-500000J0D01*
|
||||
G01*
|
||||
X205037531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X206037531Y-131704546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X237662531Y-85854546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-86354546I-31J-499954D01*
|
||||
G01*
|
||||
X164762531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X164262454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X237662531Y-23854546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-24354546I-31J-499954D01*
|
||||
G01*
|
||||
X114162531Y-73954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-73454531I499969J46D01*
|
||||
G01*
|
||||
X114162531Y-98754546D02*
|
||||
X114162531Y-109154546D01*
|
||||
X115162531Y-134954546D02*
|
||||
X125562531Y-134954546D01*
|
||||
X237662531Y-73454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-73954546I-31J-499954D01*
|
||||
G01*
|
||||
X201962531Y-10954546D02*
|
||||
X212362531Y-10954546D01*
|
||||
X238162531Y-61554546D02*
|
||||
X238162531Y-71954546D01*
|
||||
X138462531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X137962531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
D11*
|
||||
X176662531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X175662531Y-131704546I-500000J0D01*
|
||||
G01*
|
||||
X175662531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X176662531Y-131704546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X115162531Y-10954546D02*
|
||||
X125562531Y-10954546D01*
|
||||
X127562531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X127062454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X237162531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-11954546I-31J-999954D01*
|
||||
G01*
|
||||
X238162531Y-123554546D02*
|
||||
X238162531Y-133954546D01*
|
||||
X114162531Y-11954546D02*
|
||||
X114162531Y-22354546D01*
|
||||
X139962531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X139462454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X139962531Y-10954546D02*
|
||||
X150362531Y-10954546D01*
|
||||
X201962531Y-134954546D02*
|
||||
X212362531Y-134954546D01*
|
||||
X175162531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X175662454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X163262531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X162762531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X114162531Y-123554546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-123054531I499969J46D01*
|
||||
G01*
|
||||
X114162531Y-86354546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-85854531I499969J46D01*
|
||||
G01*
|
||||
X237662531Y-98254546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-98754546I-31J-499954D01*
|
||||
G01*
|
||||
X238162531Y-36754546D02*
|
||||
X238162531Y-47154546D01*
|
||||
X238162531Y-86354546D02*
|
||||
X238162531Y-96754546D01*
|
||||
D11*
|
||||
X117912531Y-72954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X116912531Y-72954546I-500000J0D01*
|
||||
G01*
|
||||
X116912531Y-72954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X117912531Y-72954546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X238162531Y-34754546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-35254531I-500031J46D01*
|
||||
G01*
|
||||
D11*
|
||||
X235412531Y-72954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X234412531Y-72954546I-500000J0D01*
|
||||
G01*
|
||||
X234412531Y-72954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X235412531Y-72954546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X126062531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X125562531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X150362531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X150862454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
D11*
|
||||
X147287531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X146287531Y-14204546I-500000J0D01*
|
||||
G01*
|
||||
X146287531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X147287531Y-14204546I500000J0D01*
|
||||
G01*
|
||||
X117912531Y-43579546D02*
|
||||
G75*
|
||||
G02*
|
||||
X116912531Y-43579546I-500000J0D01*
|
||||
G01*
|
||||
X116912531Y-43579546D02*
|
||||
G75*
|
||||
G02*
|
||||
X117912531Y-43579546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X199962531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X200462454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X238162531Y-11954546D02*
|
||||
X238162531Y-22354546D01*
|
||||
X238162531Y-84354546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-84854531I-500031J46D01*
|
||||
G01*
|
||||
X164762531Y-134954546D02*
|
||||
X175162531Y-134954546D01*
|
||||
X152362531Y-10954546D02*
|
||||
X162762531Y-10954546D01*
|
||||
X189562531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X189062454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X114162531Y-73954546D02*
|
||||
X114162531Y-84354546D01*
|
||||
X139962531Y-134954546D02*
|
||||
X150362531Y-134954546D01*
|
||||
D11*
|
||||
X176662531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X175662531Y-14204546I-500000J0D01*
|
||||
G01*
|
||||
X175662531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X176662531Y-14204546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X225262531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X224762531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X137962531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X138462454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X237662531Y-123054546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-123554546I-31J-499954D01*
|
||||
G01*
|
||||
X114162531Y-111154546D02*
|
||||
X114162531Y-121554546D01*
|
||||
D12*
|
||||
X117937531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X116887531Y-14204546I-525000J0D01*
|
||||
G01*
|
||||
X116887531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X117937531Y-14204546I525000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X114162531Y-24354546D02*
|
||||
X114162531Y-34754546D01*
|
||||
X238162531Y-111154546D02*
|
||||
X238162531Y-121554546D01*
|
||||
X237662531Y-36254546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-36754546I-31J-499954D01*
|
||||
G01*
|
||||
X114662531Y-72454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-71954546I-31J500046D01*
|
||||
G01*
|
||||
X177162531Y-10954546D02*
|
||||
X187562531Y-10954546D01*
|
||||
D12*
|
||||
X117937531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X116887531Y-131704546I-525000J0D01*
|
||||
G01*
|
||||
X116887531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X117937531Y-131704546I525000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X201962531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X201462454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X238162531Y-71954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-72454531I-500031J46D01*
|
||||
G01*
|
||||
X226762531Y-10954546D02*
|
||||
X237162531Y-10954546D01*
|
||||
X201462531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X201962531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X212862531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X212362531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X114162531Y-49154546D02*
|
||||
X114162531Y-59554546D01*
|
||||
X213862531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X214362531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X127562531Y-10954546D02*
|
||||
X137962531Y-10954546D01*
|
||||
X139462531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X139962531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X114162531Y-11954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X115162531Y-10954531I999969J46D01*
|
||||
G01*
|
||||
X115162531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-133954546I-31J1000046D01*
|
||||
G01*
|
||||
X214362531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X213862454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X114162531Y-61554546D02*
|
||||
X114162531Y-71954546D01*
|
||||
X175662531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X175162531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X189062531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X189562531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X114662531Y-109654546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-109154546I-31J500046D01*
|
||||
G01*
|
||||
X114162531Y-86354546D02*
|
||||
X114162531Y-96754546D01*
|
||||
X237662531Y-48654546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-49154546I-31J-499954D01*
|
||||
G01*
|
||||
X177162531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X176662454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X237662531Y-61054546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-61554546I-31J-499954D01*
|
||||
G01*
|
||||
X226262531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X226762531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X152362531Y-134954546D02*
|
||||
X162762531Y-134954546D01*
|
||||
X238162531Y-96754546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-97254531I-500031J46D01*
|
||||
G01*
|
||||
X176662531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X177162531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X114162531Y-36754546D02*
|
||||
X114162531Y-47154546D01*
|
||||
X238162531Y-98754546D02*
|
||||
X238162531Y-109154546D01*
|
||||
X238162531Y-133954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237162531Y-134954531I-1000031J46D01*
|
||||
G01*
|
||||
X226762531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X226262454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X114662531Y-122054546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-121554546I-31J500046D01*
|
||||
G01*
|
||||
X114162531Y-49154546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-48654531I499969J46D01*
|
||||
G01*
|
||||
X237662531Y-110654546D02*
|
||||
G75*
|
||||
G02*
|
||||
X238162454Y-111154546I-31J-499954D01*
|
||||
G01*
|
||||
X238162531Y-59554546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-60054531I-500031J46D01*
|
||||
G01*
|
||||
X224762531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X225262454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X226762531Y-134954546D02*
|
||||
X237162531Y-134954546D01*
|
||||
X238162531Y-109154546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-109654531I-500031J46D01*
|
||||
G01*
|
||||
X238162531Y-73954546D02*
|
||||
X238162531Y-84354546D01*
|
||||
X114662531Y-60054546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-59554546I-31J500046D01*
|
||||
G01*
|
||||
X214362531Y-134954546D02*
|
||||
X224762531Y-134954546D01*
|
||||
D12*
|
||||
X235437531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X234387531Y-14204546I-525000J0D01*
|
||||
G01*
|
||||
X234387531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X235437531Y-14204546I525000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X177162531Y-134954546D02*
|
||||
X187562531Y-134954546D01*
|
||||
X114162531Y-24354546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-23854531I499969J46D01*
|
||||
G01*
|
||||
D12*
|
||||
X235437531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X234387531Y-131704546I-525000J0D01*
|
||||
G01*
|
||||
X234387531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X235437531Y-131704546I525000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X164762531Y-10954546D02*
|
||||
X175162531Y-10954546D01*
|
||||
X164262531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X164762531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X114162531Y-123554546D02*
|
||||
X114162531Y-133954546D01*
|
||||
X114662531Y-47654546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-47154546I-31J500046D01*
|
||||
G01*
|
||||
D11*
|
||||
X235412531Y-43579546D02*
|
||||
G75*
|
||||
G02*
|
||||
X234412531Y-43579546I-500000J0D01*
|
||||
G01*
|
||||
X234412531Y-43579546D02*
|
||||
G75*
|
||||
G02*
|
||||
X235412531Y-43579546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X114662531Y-22854546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-22354546I-31J500046D01*
|
||||
G01*
|
||||
X238162531Y-47154546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-47654531I-500031J46D01*
|
||||
G01*
|
||||
X151862531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X152362531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X127562531Y-134954546D02*
|
||||
X137962531Y-134954546D01*
|
||||
D11*
|
||||
X147287531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X146287531Y-131704546I-500000J0D01*
|
||||
G01*
|
||||
X146287531Y-131704546D02*
|
||||
G75*
|
||||
G02*
|
||||
X147287531Y-131704546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X114162531Y-36754546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-36254531I499969J46D01*
|
||||
G01*
|
||||
X189562531Y-134954546D02*
|
||||
X199962531Y-134954546D01*
|
||||
D11*
|
||||
X117912531Y-102329546D02*
|
||||
G75*
|
||||
G02*
|
||||
X116912531Y-102329546I-500000J0D01*
|
||||
G01*
|
||||
X116912531Y-102329546D02*
|
||||
G75*
|
||||
G02*
|
||||
X117912531Y-102329546I500000J0D01*
|
||||
G01*
|
||||
X206037531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X205037531Y-14204546I-500000J0D01*
|
||||
G01*
|
||||
X205037531Y-14204546D02*
|
||||
G75*
|
||||
G02*
|
||||
X206037531Y-14204546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X162762531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X163262454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X189562531Y-10954546D02*
|
||||
X199962531Y-10954546D01*
|
||||
X152362531Y-134954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X151862454Y-134454546I-31J500046D01*
|
||||
G01*
|
||||
X238162531Y-22354546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-22854531I-500031J46D01*
|
||||
G01*
|
||||
X125562531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X126062454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X238162531Y-49154546D02*
|
||||
X238162531Y-59554546D01*
|
||||
X212362531Y-10954546D02*
|
||||
G75*
|
||||
G02*
|
||||
X212862454Y-11454546I-31J-499954D01*
|
||||
G01*
|
||||
X114162531Y-111154546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-110654531I499969J46D01*
|
||||
G01*
|
||||
X127062531Y-11454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X127562531Y-10954531I499969J46D01*
|
||||
G01*
|
||||
X214362531Y-10954546D02*
|
||||
X224762531Y-10954546D01*
|
||||
X114162531Y-98754546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-98254531I499969J46D01*
|
||||
G01*
|
||||
X200462531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X199962531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X150862531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X150362531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
D11*
|
||||
X235412531Y-102329546D02*
|
||||
G75*
|
||||
G02*
|
||||
X234412531Y-102329546I-500000J0D01*
|
||||
G01*
|
||||
X234412531Y-102329546D02*
|
||||
G75*
|
||||
G02*
|
||||
X235412531Y-102329546I500000J0D01*
|
||||
G01*
|
||||
D10*
|
||||
X238162531Y-121554546D02*
|
||||
G75*
|
||||
G02*
|
||||
X237662531Y-122054531I-500031J46D01*
|
||||
G01*
|
||||
X188062531Y-134454546D02*
|
||||
G75*
|
||||
G02*
|
||||
X187562531Y-134954531I-500031J46D01*
|
||||
G01*
|
||||
X114662531Y-97254546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114162454Y-96754546I-31J500046D01*
|
||||
G01*
|
||||
X114162531Y-61554546D02*
|
||||
G75*
|
||||
G02*
|
||||
X114662531Y-61054531I499969J46D01*
|
||||
G01*
|
||||
D13*
|
||||
G04 #@! TO.C,D2*
|
||||
X196646800Y-50786802D03*
|
||||
X196646800Y-46786800D03*
|
||||
G04 #@! TD*
|
||||
D14*
|
||||
G04 #@! TO.C,R24*
|
||||
X193903600Y-85230200D03*
|
||||
X193903600Y-86880200D03*
|
||||
G04 #@! TD*
|
||||
D15*
|
||||
G04 #@! TO.C,U3*
|
||||
X192043900Y-83581201D03*
|
||||
X192043900Y-84531200D03*
|
||||
X192043900Y-85481199D03*
|
||||
X189768900Y-85481199D03*
|
||||
X189768900Y-83581201D03*
|
||||
G04 #@! TD*
|
||||
D16*
|
||||
G04 #@! TO.C,C25*
|
||||
X180480000Y-82904999D03*
|
||||
X180480000Y-85855001D03*
|
||||
G04 #@! TD*
|
||||
D17*
|
||||
G04 #@! TO.C,R27*
|
||||
X191883800Y-87020400D03*
|
||||
X190233800Y-87020400D03*
|
||||
G04 #@! TD*
|
||||
D18*
|
||||
G04 #@! TO.C,R26*
|
||||
X196951600Y-82296000D03*
|
||||
X196951600Y-80646000D03*
|
||||
G04 #@! TD*
|
||||
D19*
|
||||
G04 #@! TO.C,R28*
|
||||
X199262163Y-53874237D03*
|
||||
X198095437Y-55040963D03*
|
||||
G04 #@! TD*
|
||||
D16*
|
||||
G04 #@! TO.C,C23*
|
||||
X199820000Y-47194999D03*
|
||||
X199820000Y-50145001D03*
|
||||
G04 #@! TD*
|
||||
D20*
|
||||
G04 #@! TO.C,R25*
|
||||
X195364600Y-83769200D03*
|
||||
X197014600Y-83769200D03*
|
||||
G04 #@! TD*
|
||||
D21*
|
||||
G04 #@! TO.C,C21*
|
||||
X189331600Y-81539200D03*
|
||||
X189331600Y-82499200D03*
|
||||
G04 #@! TD*
|
||||
D22*
|
||||
G04 #@! TO.C,U7*
|
||||
X198808100Y-83957200D03*
|
||||
X198808100Y-82057200D03*
|
||||
X200683101Y-83007200D03*
|
||||
G04 #@! TD*
|
||||
D16*
|
||||
G04 #@! TO.C,C19*
|
||||
X186490000Y-82924999D03*
|
||||
X186490000Y-85875001D03*
|
||||
G04 #@! TD*
|
||||
D23*
|
||||
G04 #@! TO.C,C22*
|
||||
X190347600Y-82268000D03*
|
||||
X190347600Y-81308000D03*
|
||||
G04 #@! TD*
|
||||
D24*
|
||||
G04 #@! TO.C,R2*
|
||||
X197790637Y-40920237D03*
|
||||
X198957363Y-42086963D03*
|
||||
G04 #@! TD*
|
||||
D18*
|
||||
G04 #@! TO.C,R22*
|
||||
X193903600Y-83832200D03*
|
||||
X193903600Y-82182200D03*
|
||||
G04 #@! TD*
|
||||
M02*
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,568 @@
|
||||
// Generated by SolidPython <Unknown> on 2025-09-08 06:43:34
|
||||
|
||||
|
||||
scale(v = 0.0000010000) {
|
||||
translate(v = [-176162531, -72954546, 0]) {
|
||||
difference() {
|
||||
linear_extrude(convexity = 10, height = 2850000) {
|
||||
polygon(points = [[131213899.2582568675, 51504136.1925114766], [131322961.1450932473, 51602983.4876628667], [131427033.4434460700, 51707053.7829912156], [131525874.5944232941, 51816104.6726151481], [131619246.5432596207, 51929873.5120421499], [131706924.4075352252, 52048086.2937974632], [131788697.0187926441, 52170458.3073561117], [131864367.4311287850, 52296694.8248580173], [131933753.3955310285, 52426491.8109482452], [131996687.7988158464, 52559536.6550326645], [132053019.0661133975, 52695508.9241863117], [132102611.5259304792, 52834081.1349053234], [132145345.7369083166, 52974919.5418341309], [132181118.7754931301, 53117684.9415800571], [132209844.4838224947, 53262033.4896680042], [132231453.6772334278, 53407617.5286776870], [132245894.3108902276, 53554086.4255596548], [132253131.6051326990, 53701087.4161216170], [132253148.1292413920, 53848266.4546466470], [132245943.8434186429, 53995269.0665961355], [132231536.0988845825, 54141741.2023476064], [132209959.5960872918, 54287330.0899088308], [132181266.3011285216, 54431685.0845522285], [132145525.3206049204, 54574458.5133318007], [132102822.7351685613, 54715306.5124343708], [132053261.3922043890, 54853889.8553650752], [131996960.6581275314, 54989874.7699568421], [131934056.1308933049, 55122933.7422496080], [131864699.3134158701, 55252746.3052921742], [131789057.2486802191, 55378999.8109728545], [131707312.1174248457, 55501390.1830215976], [131619660.7993682474, 55619622.6493629590], [131526314.3990321904, 55733412.4520627260], [131427497.7373047322, 55842485.5331561118], [131323448.8099688888, 55946579.1947044656], [131214409.1214805245, 56045450.9765909687], [131144127.0656185150, 56106104.2244711742], [131144108.6774217337, 56106120.4915215373], [130980510.8611069918, 56254466.1425165683], [130980492.8777911961, 56254482.8560757861], [130953885.3395007998, 56279827.5169951245], [130953850.6306188852, 56279862.2430811599], [130928519.1580728292, 56306482.3375511691], [130928502.4534272254, 56306500.3291470855], [130780237.8909005970, 56470171.6374789476], [130780221.6329644322, 56470190.0337345377], [130756227.2904007584, 56498021.3911554590], [130756196.1535789669, 56498059.3530556262], [130733596.7819408923, 56527034.8829472438], [130733581.9218227565, 56527054.4257105514], [130602079.3822011352, 56704474.1533214524], [130602065.0063161254, 56704494.0549957156], [130580915.0838191807, 56734543.9087273777], [130580887.8191112131, 56734584.7406172901], [130561238.3299171031, 56765636.4797864035], [130561225.4575277567, 56765657.3853909448], [130447752.1806984991, 56955115.8059417307], [130447739.8253995627, 56955137.0212498009], [130429638.1367162168, 56987115.7922980860], [130429615.0068616718, 56987159.1006963924], [130413104.7547553778, 57019987.8153588995], [130413093.9941411167, 57020009.8823453113], [130318743.4788517952, 57219681.2572004274], [130318733.2632022500, 57219703.5816984698], [130303854.2474116236, 57253303.1028273180], [130303835.4753040969, 57253348.4703874663], [130290623.5632968098, 57287637.8027235493], [130290615.0181535929, 57287660.8184406981], [130216296.4843825400, 57495620.9906144068], [130216288.5068267137, 57495644.2091697305], [130204775.5473372191, 57530540.6945905685], [130204761.3138763607, 57530587.6841226742], [130194975.0601314753, 57566007.2008976862], [130194968.8128055781, 57566030.9435518682], [130141398.4399561137, 57780275.8800737187], [130141392.7773706615, 57780299.7689379528], [130133356.8201874495, 57816156.9345130622], [130133347.2625357211, 57816205.0931968987], [130127080.9734529555, 57852413.4700194299], [130127077.0841474831, 57852437.7108117267], [130094771.1094986349, 58070902.8148307130], [130094767.8164517432, 58070927.1437959746], [130090286.3010333180, 58107399.4476787969], [130090281.5112941563, 58107448.3114274666], [130087595.5727148652, 58144096.6219602451], [130087594.0789095610, 58144121.1272913814], [130076863.8228337765, 58364701.1338128000], [130076862.9310592860, 58364725.6684301123], [130075092.9080227315, 58438291.0878120512], [130075092.6188165694, 58438315.6369274780], [130079560.3298669159, 67454047.5810131580], [130076018.2738393843, 67601261.9571875334], [130065255.6406022161, 67748112.7928943783], [130047298.0792614371, 67894258.3648860902], [130022188.8784497529, 68039346.3735452592], [129989988.5665958524, 68183027.0686249435], [129950774.7660140246, 68324954.0923593193], [129904642.0057877898, 68464785.3143943846], [129851701.4938959628, 68602183.6565344334], [129792080.8491359353, 68736817.9052991718], [129725923.7934830189, 68868363.5103545785], [129653389.8056340814, 68996503.3668738455], [129574653.7365666628, 69120928.5799535215], [129489905.3880423903, 69241339.2092369944], [129399349.0550684780, 69357444.9919544607], [129303203.0334239006, 69468966.0426308215], [129201699.0934326351, 69575633.5277812481], [129095081.9212570786, 69677190.3139625043], [128983608.5290567279, 69773391.5876197666], [128867547.6354295462, 69864005.4452389181], [128747179.0176410526, 6994Line truncated
|
||||
}
|
||||
translate(v = [0, 0, -3000000]) {
|
||||
linear_extrude(convexity = 10, height = 9000000) {
|
||||
polygon(points = [[134075000.0000000000, 38008758.2351448461], [134075000.0000000000, 48150000.0000000000], [139700000.0000000000, 48150000.0000000000], [139798017.1403295696, 48154815.2733278051], [139895090.3220161200, 48169214.7195967659], [139990284.6772544682, 48193059.6642677933], [140082683.4323650897, 48226120.4674887136], [140171396.7368260026, 48268078.7356516421], [140255570.2330195904, 48318530.3876974583], [140334393.2841636539, 48376989.5466372594], [140407106.7811865509, 48442893.2188134491], [140473010.4533627331, 48515606.7158363536], [140531469.6123025417, 48594429.7669803947], [140581921.2643483579, 48678603.2631740049], [140623879.5325112939, 48767316.5676349103], [140656940.3357322216, 48859715.3227455392], [140680785.2804032266, 48954909.6779838726], [140695184.7266722023, 49051982.8596704379], [140700000.0000000000, 49150000.0000000000], [140700000.0000000000, 49750000.0000000000], [140695184.7266722023, 49848017.1403295621], [140680785.2804032266, 49945090.3220161274], [140656940.3357322216, 50040284.6772544608], [140623879.5325112939, 50132683.4323650897], [140581921.2643483579, 50221396.7368259951], [140531469.6123025417, 50305570.2330196053], [140473010.4533627331, 50384393.2841636464], [140407106.7811865509, 50457106.7811865509], [140334393.2841636539, 50523010.4533627406], [140255570.2330195904, 50581469.6123025417], [140250000.0000000000, 50584808.2814897224], [140250000.0000000000, 56982940.4849924147], [140309393.2841636539, 57026989.5466372594], [140382106.7811865509, 57092893.2188134491], [140448010.4533627331, 57165606.7158363536], [140506469.6123025417, 57244429.7669803947], [140556921.2643483579, 57328603.2631740049], [140598879.5325112939, 57417316.5676349103], [140631940.3357322216, 57509715.3227455392], [140655785.2804032266, 57604909.6779838726], [140670184.7266722023, 57701982.8596704379], [140675000.0000000000, 57800000.0000000000], [140675000.0000000000, 58400000.0000000000], [140670184.7266722023, 58498017.1403295621], [140655785.2804032266, 58595090.3220161274], [140631940.3357322216, 58690284.6772544608], [140598879.5325112939, 58782683.4323650897], [140556921.2643483579, 58871396.7368259951], [140506469.6123025417, 58955570.2330196053], [140448010.4533627331, 59034393.2841636464], [140382106.7811865509, 59107106.7811865509], [140309393.2841636539, 59173010.4533627406], [140230570.2330195904, 59231469.6123025417], [140146396.7368260026, 59281921.2643483579], [140057683.4323650897, 59323879.5325112864], [139965284.6772544682, 59356940.3357322067], [139870090.3220161200, 59380785.2804032341], [139773017.1403295696, 59395184.7266721949], [139675000.0000000000, 59400000.0000000000], [134075569.6689103544, 59400000.0000000000], [134099591.7203668356, 107875914.0057790428], [218249706.8595425487, 107900333.6632250249], [218224905.8084288836, 38033177.9520300925], [134075000.0000000000, 38008758.2351448461]]);
|
||||
}
|
||||
}
|
||||
translate(v = [0, 0, 1400000]) {
|
||||
linear_extrude(convexity = 10, height = 3000000) {
|
||||
polygon(points = [[133025000.0000000000, 37008468.0000000000], [133025000.0000000000, 49150000.0000000000], [133025240.7636663914, 49154900.8570164815], [133025960.7359798402, 49159754.5161008090], [133027152.9832133949, 49164514.2338627204], [133028806.0233744383, 49169134.1716182530], [133030903.9367825836, 49173569.8368413001], [133033426.5193848759, 49177778.5116509795], [133036349.4773318619, 49181719.6642081812], [133039644.6609406769, 49185355.3390593305], [133043280.3357918113, 49188650.5226681381], [133047221.4883490205, 49191573.4806151241], [133051430.1631586999, 49194096.0632174164], [133055865.8283817470, 49196193.9766255617], [133060485.7661372721, 49197847.0167866126], [133065245.4838991910, 49199039.2640201598], [133070099.1429835260, 49199759.2363336086], [133075000.0000000000, 49200000.0000000000], [139650000.0000000000, 49200000.0000000000], [139650000.0000000000, 49700000.0000000000], [139250000.0000000000, 49700000.0000000000], [139245099.1429835260, 49700240.7636663914], [139240245.4838992059, 49700960.7359798402], [139235485.7661372721, 49702152.9832133874], [139230865.8283817470, 49703806.0233744383], [139226430.1631587148, 49705903.9367825836], [139222221.4883490205, 49708426.5193848759], [139218280.3357918262, 49711349.4773318619], [139214644.6609406769, 49714644.6609406695], [139211349.4773318768, 49718280.3357918188], [139208426.5193848610, 49722221.4883490205], [139205903.9367825687, 49726430.1631586999], [139203806.0233744383, 49730865.8283817470], [139202152.9832133949, 49735485.7661372796], [139200960.7359798253, 49740245.4838991910], [139200240.7636663914, 49745099.1429835185], [139200000.0000000000, 49750000.0000000000], [139200000.0000000000, 57800000.0000000000], [139200240.7636663914, 57804900.8570164815], [139200960.7359798253, 57809754.5161008090], [139202152.9832133949, 57814514.2338627204], [139203806.0233744383, 57819134.1716182530], [139205903.9367825687, 57823569.8368413001], [139208426.5193848610, 57827778.5116509795], [139211349.4773318768, 57831719.6642081812], [139214644.6609406769, 57835355.3390593305], [139218280.3357918262, 57838650.5226681381], [139222221.4883490205, 57841573.4806151241], [139226430.1631587148, 57844096.0632174164], [139230865.8283817470, 57846193.9766255617], [139235485.7661372721, 57847847.0167866126], [139240245.4838992059, 57849039.2640201598], [139245099.1429835260, 57849759.2363336086], [139250000.0000000000, 57850000.0000000000], [139625000.0000000000, 57850000.0000000000], [139625000.0000000000, 58350000.0000000000], [133075074.0000000000, 58350000.0000000000], [133070171.6018611938, 58350240.9154777899], [133065316.4462659806, 58350961.3403004557], [133060555.3204995692, 58352154.3320083842], [133055934.1057175100, 58353808.3941948861], [133051497.3348064721, 58355907.5872926787], [133047287.7632378340, 58358431.6821774468], [133043345.9570494145, 58361356.3551081717], [133039709.9019260556, 58364653.4221258014], [133036414.6371460706, 58368291.1106512994], [133033491.9179209918, 58372234.3656658977], [133030969.9093827754, 58376445.1875229701], [133028872.9151671082, 58380882.9981361181], [133027221.1432085037, 58385505.0320147872], [133026030.5110041946, 58390266.7483789697], [133025312.4922232479, 58395122.2603817284], [133025074.0061391443, 58400024.7773033231], [133050087.0061391443, 108875648.7773033231], [133050329.9565512538, 108880547.0709154904], [133051051.6156015694, 108885398.0000459552], [133052245.0402646214, 108890154.8942835182], [133053898.7486947030, 108894771.9879193306], [133055996.8306918591, 108899204.8602553010], [133058519.1007726640, 108903410.8629720360], [133061441.2923731953, 108907349.5304451138], [133064735.2913157493, 108910982.9690616578], [133068369.4062931240, 108914276.2217918485], [133072308.6737683117, 108917197.6045077294], [133076515.1943558156, 108919719.0108138472], [133080948.4974488616, 108921816.1824565530], [133085565.9305837750, 108923468.9427108914], [133090323.0697961301, 108924661.3904992342], [133095174.1470202357, 108925382.0533742905], [133100072.4904200733, 108925623.9978947192], [219250047.4904200733, 108950623.9978947192], [219254950.4235864878, 108950384.4593511671], [219259806.2399122119, 108949665.3069619238], [219264568.1368401051, 108948473.4722434431], [219269190.2170507610, 108946820.4426385760], [219273627.9308415353, 108944722.1507953852], [219277838.5055169165, 108942198.8210010380], [219281781.3576510847, 108939274.7742510140], [219285418.4842492640, 108935978.1938323081], [219288714.8290379047, 108932340.8536801338], [219291638.6203528941, 108928397.8121263981], [219294161.6773692369, 108924187.0739915371], [219296259.6817206144, 108919749.2242769003], [219297912.4118908346, 108915127.0369881094], [219299103.9381178916, 108910365.0628598481], [219299822.7759324014, 108905509.1999556273], [219300061.9968498349, 108900606.2512815297], [219274550.9968498349, 37033450.2512815222], [219274308.6918088198, 37028551.4864123166], [219273587.5862336457, 37023700.03140Line truncated
|
||||
}
|
||||
}
|
||||
translate(v = [176162531, 125454546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [176162531, 20454546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [228662531, 72954546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [123662531, 72954546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************
|
||||
********* SolidPython code: **********
|
||||
************************************************
|
||||
|
||||
from pcbnewTransition import pcbnew
|
||||
import numpy as np
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from kikit.common import *
|
||||
from kikit.defs import *
|
||||
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
|
||||
from kikit.export import gerberImpl, pasteDxfExport
|
||||
from kikit.export import exportSettingsJlcpcb
|
||||
# Allow importing solid from this dir
|
||||
import os
|
||||
import sys
|
||||
cur_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if cur_path not in sys.path:
|
||||
sys.path.insert(0, cur_path)
|
||||
import solid
|
||||
import solid.utils
|
||||
import subprocess
|
||||
import shutil
|
||||
from kikit.common import removeComponents, parseReferences
|
||||
|
||||
from shapely.geometry import Point
|
||||
|
||||
|
||||
OUTER_BORDER = fromMm(7.5)
|
||||
INNER_BORDER = fromMm(5)
|
||||
MOUNTING_HOLES_COUNT = 3
|
||||
MOUNTING_HOLE_R = fromMm(1)
|
||||
HOLE_SPACING = fromMm(20)
|
||||
|
||||
def addBottomCounterpart(board, item):
|
||||
item = item.Duplicate()
|
||||
item.SetLayer(Layer.B_Paste)
|
||||
board.Add(item)
|
||||
|
||||
def addRoundedCorner(board, center, start, end, thickness):
|
||||
corner = pcbnew.PCB_SHAPE()
|
||||
corner.SetShape(STROKE_T.S_ARC)
|
||||
corner.SetCenter(toKiCADPoint((center[0], center[1])))
|
||||
corner.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
|
||||
if np.cross(start - center, end - center) > 0:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(90), True)
|
||||
else:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(-90), True)
|
||||
corner.SetWidth(thickness)
|
||||
corner.SetLayer(Layer.F_Paste)
|
||||
board.Add(corner)
|
||||
addBottomCounterpart(board, corner)
|
||||
|
||||
def addLine(board, start, end, thickness):
|
||||
line = pcbnew.PCB_SHAPE()
|
||||
line.SetShape(STROKE_T.S_SEGMENT)
|
||||
line.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
line.SetEnd(toKiCADPoint((end[0], end[1])))
|
||||
line.SetWidth(thickness)
|
||||
line.SetLayer(Layer.F_Paste)
|
||||
board.Add(line)
|
||||
addBottomCounterpart(board, line)
|
||||
|
||||
def addBite(board, origin, direction, normal, thickness):
|
||||
"""
|
||||
Adds a bite to the stencil, direction points to the bridge, normal points
|
||||
inside the stencil
|
||||
"""
|
||||
direction = normalize(direction) * thickness
|
||||
normal = normalize(normal) * thickness
|
||||
center = toKiCADPoint((origin[0], origin[1])) + toKiCADPoint((normal[0], normal[1]))
|
||||
start = origin
|
||||
end = center + toKiCADPoint((direction[0], direction[1]))
|
||||
# addLine(board, end, end + normal / 2, thickness)
|
||||
addRoundedCorner(board, center, start, end, thickness)
|
||||
|
||||
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
|
||||
"""
|
||||
Return number of bridges which fit inside the length and cut length
|
||||
"""
|
||||
count = round(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
|
||||
cutLength = round((length - (count - 1) * bridgeWidth) / count)
|
||||
return count, cutLength
|
||||
|
||||
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
|
||||
"""
|
||||
Add rectangular frame to the board
|
||||
"""
|
||||
R=fromMm(1)
|
||||
|
||||
corners = [
|
||||
(tl(rect), toKiCADPoint((R, 0)), toKiCADPoint((0, R))), # TL
|
||||
(tr(rect), toKiCADPoint((0, R)), toKiCADPoint((-R, 0))), # TR
|
||||
(br(rect), toKiCADPoint((-R, 0)), toKiCADPoint((0, -R))), # BR
|
||||
(bl(rect), toKiCADPoint((0, -R)), toKiCADPoint((R, 0))) # BL
|
||||
]
|
||||
for c, sOffset, eOffset in corners:
|
||||
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = round(rect.GetX() + R + i * bridgeWidth + i * cutLength)
|
||||
end = start + cutLength
|
||||
|
||||
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
|
||||
addLine(board, toKiCADPoint((start, y1)), toKiCADPoint((end, y1)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y1)), toKiCADPoint((-1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y1)), toKiCADPoint((1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
addLine(board, toKiCADPoint((start, y2)), toKiCADPoint((end, y2)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y2)), toKiCADPoint((-1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y2)), toKiCADPoint((1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
|
||||
end = start + cutLength
|
||||
|
||||
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
|
||||
addLine(board, toKiCADPoint((x1, start)), toKiCADPoint((x1, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x1, start)), toKiCADPoint((0, -1)), toKiCADPoint((1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x1, end)), toKiCADPoint((0, 1)), toKiCADPoint((1, 0)), clearance)
|
||||
addLine(board, toKiCADPoint((x2, start)), toKiCADPoint((x2, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x2, start)), toKiCADPoint((0, -1)), toKiCADPoint((-1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x2, end)), toKiCADPoint((0, 1)), toKiCADPoint((-1, 0)), clearance)
|
||||
|
||||
def addHole(board, position, radius):
|
||||
circle = pcbnew.PCB_SHAPE()
|
||||
circle.SetShape(STROKE_T.S_CIRCLE)
|
||||
circle.SetCenter(toKiCADPoint((position[0], position[1])))
|
||||
# Set 3'oclock point of the circle to set radius
|
||||
circle.SetEnd(toKiCADPoint((position[0], position[1])) + toKiCADPoint((radius/2, 0)))
|
||||
|
||||
circle.SetWidth(radius)
|
||||
circle.SetLayer(Layer.F_Paste)
|
||||
board.Add(circle)
|
||||
addBottomCounterpart(board, circle)
|
||||
|
||||
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
|
||||
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
|
||||
"""
|
||||
Given a Pcbnew board finds the board outline and creates a stencil for
|
||||
KiKit's stencil jig.
|
||||
|
||||
Mainly, adds mounting holes and mouse bites to define the panel outline.
|
||||
|
||||
jigFrameSize is a tuple (width, height).
|
||||
"""
|
||||
bBox = findBoardBoundingBox(board)
|
||||
frameSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
|
||||
cutSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
|
||||
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
|
||||
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((x, OUTER_BORDER / 2 + frameSize.GetY())), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight())), MOUNTING_HOLE_R)
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((OUTER_BORDER / 2 + frameSize.GetX(), y)), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y)), MOUNTING_HOLE_R)
|
||||
|
||||
PIN_TOLERANCE = fromMm(0.05)
|
||||
addHole(board, tl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, tr(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, br(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, bl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
|
||||
def jigMountingHoles(jigFrameSize, origin=toKiCADPoint((0, 0))):
|
||||
""" Get list of all mounting holes in a jig of given size """
|
||||
w, h = jigFrameSize
|
||||
holes = [
|
||||
toKiCADPoint((0, (w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint((0, -(w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint(((h + INNER_BORDER) / 2, 0)),
|
||||
toKiCADPoint((-(h + INNER_BORDER) / 2, 0)),
|
||||
]
|
||||
return [x + origin for x in holes]
|
||||
|
||||
def createOuterPolygon(board, jigFrameSize, outerBorder):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
holes = jigMountingHoles(jigFrameSize, centerpoint)
|
||||
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
|
||||
tabs = []
|
||||
for hole in holes:
|
||||
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, maxHeight=fromMm(1000))
|
||||
tabs.append(tab)
|
||||
outerSubstrate.union(tabs)
|
||||
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
|
||||
outerSubstrate.millFillets(fromMm(3))
|
||||
return outerSubstrate.exterior(), holes
|
||||
|
||||
def createOffsetPolygon(board, offset):
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
|
||||
return outerSubstrate.exterior()
|
||||
|
||||
def m2countersink():
|
||||
HEAD_DIA = fromMm(4.5)
|
||||
HOLE_LEN = fromMm(10)
|
||||
SINK_EXTRA = fromMm(0.3)
|
||||
sinkH = np.sqrt(HEAD_DIA**2 / 4)
|
||||
|
||||
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
|
||||
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
|
||||
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
|
||||
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
|
||||
|
||||
def mirrorX(linestring, origin):
|
||||
return [(2 * origin - x, y) for x, y in linestring]
|
||||
|
||||
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, topSide):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
|
||||
top = jigThickness - fromMm(0.15)
|
||||
pcbBottom = jigThickness - pcbThickness
|
||||
|
||||
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
|
||||
outerRing = outerPolygon.exterior.coords
|
||||
if topSide:
|
||||
outerRing = mirrorX(outerRing, centerpoint[0])
|
||||
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
|
||||
outerRing))
|
||||
|
||||
innerRings = [x.exterior.coords for x in listGeometries(createOffsetPolygon(board, - innerBorder))]
|
||||
if topSide:
|
||||
innerRings = [mirrorX(innerRing, centerpoint[0]) for innerRing in innerRings]
|
||||
|
||||
innerCutout = solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRings[0])))
|
||||
for innerRing in innerRings[1:]:
|
||||
innerCutout = innerCutout + solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
|
||||
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
|
||||
if topSide:
|
||||
registerRing = mirrorX(registerRing, centerpoint[0])
|
||||
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
|
||||
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
|
||||
|
||||
register = body - innerCutout - registerCutout
|
||||
for hole in holes:
|
||||
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
|
||||
return solid.scale(toMm(1))(
|
||||
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
|
||||
|
||||
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, True)
|
||||
|
||||
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, False)
|
||||
|
||||
def renderScad(infile, outfile):
|
||||
infile = os.path.abspath(infile)
|
||||
outfile = os.path.abspath(outfile)
|
||||
try:
|
||||
subprocess.run(["openscad", "-o", outfile, infile],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
message = f"Cannot render {outfile}, OpenSCAD error:\n"
|
||||
message += (e.stdout.decode("utf-8") + "\n") if e.stdout is not None else ""
|
||||
message += (e.stderr.decode("utf-8") + "\n") if e.stderr is not None else ""
|
||||
raise RuntimeError(message)
|
||||
except FileNotFoundError as e:
|
||||
message = f"OpenSCAD is not available.\n"
|
||||
message += f"Did you install it? Program `openscad` has to be in PATH"
|
||||
raise RuntimeError(message)
|
||||
|
||||
def shapelyToSHAPE_POLY_SET(polygon):
|
||||
p = pcbnew.SHAPE_POLY_SET()
|
||||
p.AddOutline(linestringToKicad(polygon.exterior))
|
||||
return p
|
||||
|
||||
def cutoutComponents(board, components):
|
||||
topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd)
|
||||
for polygon in topCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.F_Paste)
|
||||
board.Add(zone)
|
||||
bottomCutout = extractComponentPolygons(components, pcbnew.B_CrtYd)
|
||||
for polygon in bottomCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.B_Paste)
|
||||
board.Add(zone)
|
||||
|
||||
def setStencilLayerVisibility(boardName):
|
||||
prlPath = os.path.splitext(boardName)[0] + ".kicad_prl"
|
||||
try:
|
||||
with open(prlPath, encoding="utf-8") as f:
|
||||
# We use ordered dict, so we preserve the ordering of the keys and
|
||||
# thus, formatting
|
||||
prl = json.load(f, object_pairs_hook=OrderedDict)
|
||||
except FileNotFoundError:
|
||||
# KiCAD didn't generate project local settings, let's create an empty one
|
||||
prl = {
|
||||
"board": {}
|
||||
}
|
||||
prl["board"]["visible_layers"] = "ffc000c_7ffffffe"
|
||||
prl["board"]["visible_items"] = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
13,
|
||||
21,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
34,
|
||||
35
|
||||
]
|
||||
with open(prlPath, "w", encoding="utf-8") as f:
|
||||
json.dump(prl, f, indent=2)
|
||||
pass
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
|
||||
registerborder, tolerance, ignore, cutout):
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
removeComponents(board, refs)
|
||||
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
|
||||
addJigFrame(board, jigsize)
|
||||
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
|
||||
|
||||
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
|
||||
board.Save(stencilFile)
|
||||
|
||||
setStencilLayerVisibility(stencilFile)
|
||||
|
||||
plotPlan = [
|
||||
# name, id, comment
|
||||
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
|
||||
("PasteTop", pcbnew.F_Paste, "Paste top"),
|
||||
]
|
||||
# get a copy of exportSettingsJlcpcb dictionary and
|
||||
# exclude the Edge.Cuts layer for creation of stencil gerber files
|
||||
exportSettings = exportSettingsJlcpcb.copy()
|
||||
exportSettings["ExcludeEdgeLayer"] = True
|
||||
gerberDir = os.path.join(outputdir, "gerber")
|
||||
gerberImpl(stencilFile, gerberDir, plotPlan, False, exportSettings)
|
||||
|
||||
shutil.make_archive(os.path.join(outputdir, "gerbers"), "zip", gerberDir)
|
||||
|
||||
jigthickness = fromMm(jigthickness)
|
||||
pcbthickness = fromMm(pcbthickness)
|
||||
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
|
||||
tolerance = fromMm(tolerance)
|
||||
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
|
||||
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
|
||||
solid.scad_render_to_file(topRegister, topRegisterFile)
|
||||
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
|
||||
|
||||
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
|
||||
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
|
||||
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
|
||||
|
||||
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
|
||||
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
|
||||
body = solid.linear_extrude(height=thickness + frameHeight)(
|
||||
bodyOffset(solid.import_dxf(outlineDxf)))
|
||||
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
|
||||
board = solid.utils.up(thickness)(
|
||||
solid.linear_extrude(height=thickness + frameHeight)(
|
||||
boardOffset(solid.import_dxf(outlineDxf))))
|
||||
return body - board
|
||||
|
||||
def getComponents(board, references):
|
||||
"""
|
||||
Return a list of components based on designator
|
||||
"""
|
||||
return [f for f in board.GetFootprints() if f.GetReference() in references]
|
||||
|
||||
def collectFootprintEdges(footprint, layer):
|
||||
"""
|
||||
Return all edges on given layer in given footprint
|
||||
"""
|
||||
return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer]
|
||||
|
||||
def extractComponentPolygons(footprints, srcLayer):
|
||||
"""
|
||||
Return a list of shapely polygons with holes for already placed components.
|
||||
The source layer defines the geometry on which the cutout is computed.
|
||||
Usually it a font or back courtyard
|
||||
"""
|
||||
polygons = []
|
||||
for f in footprints:
|
||||
edges = collectFootprintEdges(f, srcLayer)
|
||||
for ring in extractRings(edges):
|
||||
polygons.append(toShapely(ring, edges))
|
||||
return polygons
|
||||
|
||||
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
|
||||
frameClearance, enlargeHoles, front):
|
||||
zScale = -1 if front else 1
|
||||
xRotate = 180 if front else 0
|
||||
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
|
||||
thickness, frameHeight, frameWidth, frameClearance))
|
||||
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
|
||||
holes = solid.linear_extrude(height=4*thickness, center=True)(
|
||||
holesOffset(solid.import_dxf(holesDxf)))
|
||||
substrate -= holes
|
||||
for h in extraHoles:
|
||||
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
|
||||
solid.linear_extrude(height=4*thickness, center=True)(
|
||||
solid.polygon(h.exterior.coords)))
|
||||
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
|
||||
|
||||
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
|
||||
ignore, cutout, frameclearance, enlargeholes):
|
||||
"""
|
||||
Create a 3D printed self-registering stencil.
|
||||
"""
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
cutoutComponents = getComponents(board, parseReferences(cutout))
|
||||
removeComponents(board, refs)
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# We create the stencil based on DXF export. Using it avoids the necessity
|
||||
# to interpret KiCAD PAD shapes which constantly change with newer and newer
|
||||
# versions.
|
||||
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
|
||||
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
|
||||
# On Windows, OpenSCAD requires to use forward slashes instead of backslashes,
|
||||
# hence, the replacement:
|
||||
if os.name == "nt":
|
||||
bottomPaste = bottomPaste.replace("\\", "/")
|
||||
topPaste = topPaste.replace("\\", "/")
|
||||
outline = outline.replace("\\", "/")
|
||||
|
||||
topCutout = extractComponentPolygons(cutoutComponents, pcbnew.F_CrtYd)
|
||||
bottomCutout = extractComponentPolygons(cutoutComponents, pcbnew.B_CrtYd)
|
||||
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
|
||||
framewidth, frameclearance, enlargeholes, True)
|
||||
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
|
||||
height, framewidth, frameclearance, enlargeholes, False)
|
||||
|
||||
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
|
||||
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
|
||||
|
||||
topStencilFile = os.path.join(outputdir, "topStencil.scad")
|
||||
solid.scad_render_to_file(topStencil, topStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
************************************************/
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,568 @@
|
||||
// Generated by SolidPython <Unknown> on 2025-09-08 06:43:27
|
||||
|
||||
|
||||
scale(v = 0.0000010000) {
|
||||
translate(v = [-176162531, -72954546, 0]) {
|
||||
difference() {
|
||||
linear_extrude(convexity = 10, height = 2850000) {
|
||||
polygon(points = [[221111162.7417431474, 51504136.1925114766], [221002100.8549067378, 51602983.4876628667], [220898028.5565539300, 51707053.7829912156], [220799187.4055767059, 51816104.6726151481], [220705815.4567403793, 51929873.5120421499], [220618137.5924647748, 52048086.2937974632], [220536364.9812073708, 52170458.3073561117], [220460694.5688712001, 52296694.8248580173], [220391308.6044689715, 52426491.8109482452], [220328374.2011841536, 52559536.6550326645], [220272042.9338865876, 52695508.9241863117], [220222450.4740695357, 52834081.1349053234], [220179716.2630916834, 52974919.5418341309], [220143943.2245068550, 53117684.9415800571], [220115217.5161775053, 53262033.4896680042], [220093608.3227665722, 53407617.5286776870], [220079167.6891097724, 53554086.4255596548], [220071930.3948673010, 53701087.4161216170], [220071913.8707585931, 53848266.4546466470], [220079118.1565813422, 53995269.0665961355], [220093525.9011154175, 54141741.2023476064], [220115102.4039127231, 54287330.0899088308], [220143795.6988714933, 54431685.0845522285], [220179536.6793950796, 54574458.5133318007], [220222239.2648314238, 54715306.5124343708], [220271800.6077955961, 54853889.8553650752], [220328101.3418724537, 54989874.7699568421], [220391005.8691067100, 55122933.7422496080], [220460362.6865841150, 55252746.3052921742], [220536004.7513197660, 55378999.8109728545], [220617749.8825751543, 55501390.1830215976], [220705401.2006317377, 55619622.6493629590], [220798747.6009678245, 55733412.4520627260], [220897564.2626952529, 55842485.5331561118], [221001613.1900311112, 55946579.1947044656], [221110652.8785194755, 56045450.9765909687], [221180934.9343814850, 56106104.2244711742], [221180953.3225782514, 56106120.4915215373], [221344551.1388930082, 56254466.1425165683], [221344569.1222088039, 56254482.8560757861], [221371176.6604992151, 56279827.5169951245], [221371211.3693811297, 56279862.2430811599], [221396542.8419271708, 56306482.3375511691], [221396559.5465727746, 56306500.3291470855], [221544824.1090993881, 56470171.6374789476], [221544840.3670355678, 56470190.0337345377], [221568834.7095992565, 56498021.3911554590], [221568865.8464210331, 56498059.3530556262], [221591465.2180591226, 56527034.8829472438], [221591480.0781772435, 56527054.4257105514], [221722982.6177988648, 56704474.1533214524], [221722996.9936838746, 56704494.0549957156], [221744146.9161808193, 56734543.9087273777], [221744174.1808887720, 56734584.7406172901], [221763823.6700828969, 56765636.4797864035], [221763836.5424722433, 56765657.3853909448], [221877309.8193014860, 56955115.8059417307], [221877322.1746004224, 56955137.0212498009], [221895423.8632837832, 56987115.7922980860], [221895446.9931383133, 56987159.1006963924], [221911957.2452446222, 57019987.8153588995], [221911968.0058588982, 57020009.8823453113], [222006318.5211482048, 57219681.2572004274], [222006328.7367977500, 57219703.5816984698], [222021207.7525883913, 57253303.1028273180], [222021226.5246959031, 57253348.4703874663], [222034438.4367032051, 57287637.8027235493], [222034446.9818463922, 57287660.8184406981], [222108765.5156174600, 57495620.9906144068], [222108773.4931733012, 57495644.2091697305], [222120286.4526627660, 57530540.6945905685], [222120300.6861236393, 57530587.6841226742], [222130086.9398685098, 57566007.2008976862], [222130093.1871944070, 57566030.9435518682], [222183663.5600438714, 57780275.8800737187], [222183669.2226293385, 57780299.7689379528], [222191705.1798125505, 57816156.9345130622], [222191714.7374642789, 57816205.0931968987], [222197981.0265470445, 57852413.4700194299], [222197984.9158525169, 57852437.7108117267], [222230290.8905013800, 58070902.8148307130], [222230294.1835482717, 58070927.1437959746], [222234775.6989666820, 58107399.4476787969], [222234780.4887058437, 58107448.3114274666], [222237466.4272851348, 58144096.6219602451], [222237467.9210904241, 58144121.1272913814], [222248198.1771662235, 58364701.1338128000], [222248199.0689406991, 58364725.6684301123], [222249969.0919772685, 58438291.0878120512], [222249969.3811834455, 58438315.6369274780], [222245501.6701330841, 67454047.5810131580], [222249043.7261606157, 67601261.9571875334], [222259806.3593977690, 67748112.7928943783], [222277763.9207385778, 67894258.3648860902], [222302873.1215502620, 68039346.3735452592], [222335073.4334041476, 68183027.0686249435], [222374287.2339859605, 68324954.0923593193], [222420419.9942122102, 68464785.3143943846], [222473360.5061040521, 68602183.6565344334], [222532981.1508640647, 68736817.9052991718], [222599138.2065169811, 68868363.5103545785], [222671672.1943659186, 68996503.3668738455], [222750408.2634333372, 69120928.5799535215], [222835156.6119576097, 69241339.2092369944], [222925712.9449315071, 69357444.9919544607], [223021858.9665760994, 69468966.0426308215], [223123362.9065673649, 69575633.5277812481], [223229980.0787429214, 69677190.3139625043], [223341453.4709432721, 69773391.5876197666], [223457514.3645704389, 69864005.4452389181], [223577882.9823589325, 6994Line truncated
|
||||
}
|
||||
translate(v = [0, 0, -3000000]) {
|
||||
linear_extrude(convexity = 10, height = 9000000) {
|
||||
polygon(points = [[218250062.0000000000, 38008758.2351448461], [218250062.0000000000, 48150000.0000000000], [212625062.0000000000, 48150000.0000000000], [212527044.8596704304, 48154815.2733278051], [212429971.6779838800, 48169214.7195967659], [212334777.3227455318, 48193059.6642677933], [212242378.5676349103, 48226120.4674887136], [212153665.2631739974, 48268078.7356516421], [212069491.7669804096, 48318530.3876974583], [211990668.7158363461, 48376989.5466372594], [211917955.2188134491, 48442893.2188134491], [211852051.5466372669, 48515606.7158363536], [211793592.3876974583, 48594429.7669803947], [211743140.7356516421, 48678603.2631740049], [211701182.4674887061, 48767316.5676349103], [211668121.6642677784, 48859715.3227455392], [211644276.7195967734, 48954909.6779838726], [211629877.2733277977, 49051982.8596704379], [211625062.0000000000, 49150000.0000000000], [211625062.0000000000, 49750000.0000000000], [211629877.2733277977, 49848017.1403295621], [211644276.7195967734, 49945090.3220161274], [211668121.6642677784, 50040284.6772544608], [211701182.4674887061, 50132683.4323650897], [211743140.7356516421, 50221396.7368259951], [211793592.3876974583, 50305570.2330196053], [211852051.5466372669, 50384393.2841636464], [211917955.2188134491, 50457106.7811865509], [211990668.7158363461, 50523010.4533627406], [212069491.7669804096, 50581469.6123025417], [212075062.0000000000, 50584808.2814897224], [212075062.0000000000, 56982940.4849924147], [212015668.7158363461, 57026989.5466372594], [211942955.2188134491, 57092893.2188134491], [211877051.5466372669, 57165606.7158363536], [211818592.3876974583, 57244429.7669803947], [211768140.7356516421, 57328603.2631740049], [211726182.4674887061, 57417316.5676349103], [211693121.6642677784, 57509715.3227455392], [211669276.7195967734, 57604909.6779838726], [211654877.2733277977, 57701982.8596704379], [211650062.0000000000, 57800000.0000000000], [211650062.0000000000, 58400000.0000000000], [211654877.2733277977, 58498017.1403295621], [211669276.7195967734, 58595090.3220161274], [211693121.6642677784, 58690284.6772544608], [211726182.4674887061, 58782683.4323650897], [211768140.7356516421, 58871396.7368259951], [211818592.3876974583, 58955570.2330196053], [211877051.5466372669, 59034393.2841636464], [211942955.2188134491, 59107106.7811865509], [212015668.7158363461, 59173010.4533627406], [212094491.7669804096, 59231469.6123025417], [212178665.2631739974, 59281921.2643483579], [212267378.5676349103, 59323879.5325112864], [212359777.3227455318, 59356940.3357322067], [212454971.6779838800, 59380785.2804032341], [212552044.8596704304, 59395184.7266721949], [212650062.0000000000, 59400000.0000000000], [218249492.3310896456, 59400000.0000000000], [218225470.2796331644, 107875914.0057790428], [134075355.1404574513, 107900333.6632250249], [134100156.1915711164, 38033177.9520300925], [218250062.0000000000, 38008758.2351448461]]);
|
||||
}
|
||||
}
|
||||
translate(v = [0, 0, 1400000]) {
|
||||
linear_extrude(convexity = 10, height = 3000000) {
|
||||
polygon(points = [[219300062.0000000000, 37008468.0000000000], [219300062.0000000000, 49150000.0000000000], [219299821.2363336086, 49154900.8570164815], [219299101.2640201449, 49159754.5161008090], [219297909.0167866051, 49164514.2338627204], [219296255.9766255617, 49169134.1716182530], [219294158.0632174015, 49173569.8368413001], [219291635.4806151390, 49177778.5116509795], [219288712.5226681232, 49181719.6642081812], [219285417.3390593231, 49185355.3390593305], [219281781.6642081738, 49188650.5226681381], [219277840.5116509795, 49191573.4806151241], [219273631.8368412852, 49194096.0632174164], [219269196.1716182530, 49196193.9766255617], [219264576.2338627279, 49197847.0167866126], [219259816.5161008239, 49199039.2640201598], [219254962.8570164740, 49199759.2363336086], [219250062.0000000000, 49200000.0000000000], [212675062.0000000000, 49200000.0000000000], [212675062.0000000000, 49700000.0000000000], [213075062.0000000000, 49700000.0000000000], [213079962.8570164740, 49700240.7636663914], [213084816.5161007941, 49700960.7359798402], [213089576.2338627279, 49702152.9832133874], [213094196.1716182530, 49703806.0233744383], [213098631.8368412852, 49705903.9367825836], [213102840.5116509795, 49708426.5193848759], [213106781.6642081738, 49711349.4773318619], [213110417.3390593231, 49714644.6609406695], [213113712.5226681232, 49718280.3357918188], [213116635.4806151390, 49722221.4883490205], [213119158.0632174313, 49726430.1631586999], [213121255.9766255617, 49730865.8283817470], [213122909.0167866051, 49735485.7661372796], [213124101.2640201747, 49740245.4838991910], [213124821.2363336086, 49745099.1429835185], [213125062.0000000000, 49750000.0000000000], [213125062.0000000000, 57800000.0000000000], [213124821.2363336086, 57804900.8570164815], [213124101.2640201747, 57809754.5161008090], [213122909.0167866051, 57814514.2338627204], [213121255.9766255617, 57819134.1716182530], [213119158.0632174313, 57823569.8368413001], [213116635.4806151390, 57827778.5116509795], [213113712.5226681232, 57831719.6642081812], [213110417.3390593231, 57835355.3390593305], [213106781.6642081738, 57838650.5226681381], [213102840.5116509795, 57841573.4806151241], [213098631.8368412852, 57844096.0632174164], [213094196.1716182530, 57846193.9766255617], [213089576.2338627279, 57847847.0167866126], [213084816.5161007941, 57849039.2640201598], [213079962.8570164740, 57849759.2363336086], [213075062.0000000000, 57850000.0000000000], [212700062.0000000000, 57850000.0000000000], [212700062.0000000000, 58350000.0000000000], [219249988.0000000000, 58350000.0000000000], [219254890.3981388211, 58350240.9154777899], [219259745.5537340045, 58350961.3403004557], [219264506.6795004308, 58352154.3320083842], [219269127.8942824900, 58353808.3941948861], [219273564.6651935279, 58355907.5872926787], [219277774.2367621660, 58358431.6821774468], [219281716.0429505706, 58361356.3551081717], [219285352.0980739594, 58364653.4221258014], [219288647.3628539443, 58368291.1106512994], [219291570.0820789933, 58372234.3656658977], [219294092.0906172395, 58376445.1875229701], [219296189.0848329067, 58380882.9981361181], [219297840.8567914963, 58385505.0320147872], [219299031.4889957905, 58390266.7483789697], [219299749.5077767372, 58395122.2603817284], [219299987.9938608408, 58400024.7773033231], [219274974.9938608408, 108875648.7773033231], [219274732.0434487462, 108880547.0709154904], [219274010.3843984306, 108885398.0000459552], [219272816.9597353935, 108890154.8942835182], [219271163.2513052821, 108894771.9879193306], [219269065.1693081260, 108899204.8602553010], [219266542.8992273211, 108903410.8629720360], [219263620.7076268196, 108907349.5304451138], [219260326.7086842656, 108910982.9690616578], [219256692.5937068760, 108914276.2217918485], [219252753.3262316883, 108917197.6045077294], [219248546.8056441844, 108919719.0108138472], [219244113.5025511384, 108921816.1824565530], [219239496.0694162250, 108923468.9427108914], [219234738.9302038550, 108924661.3904992342], [219229887.8529797792, 108925382.0533742905], [219224989.5095799267, 108925623.9978947192], [133075014.5095799267, 108950623.9978947192], [133070111.5764135122, 108950384.4593511671], [133065255.7600877881, 108949665.3069619238], [133060493.8631598949, 108948473.4722434431], [133055871.7829492390, 108946820.4426385760], [133051434.0691584647, 108944722.1507953852], [133047223.4944830835, 108942198.8210010380], [133043280.6423489153, 108939274.7742510140], [133039643.5157507360, 108935978.1938323081], [133036347.1709620953, 108932340.8536801338], [133033423.3796471059, 108928397.8121263981], [133030900.3226307631, 108924187.0739915371], [133028802.3182793856, 108919749.2242769003], [133027149.5881091654, 108915127.0369881094], [133025958.0618821084, 108910365.0628598481], [133025239.2240675986, 108905509.1999556273], [133025000.0031501651, 108900606.2512815297], [133050511.0031501651, 37033450.2512815222], [133050753.3081911802, 37028551.4864123166], [133051474.4137663543, 37023700.03140Line truncated
|
||||
}
|
||||
}
|
||||
translate(v = [176162531, 125454546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [176162531, 20454546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [228662531, 72954546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
translate(v = [123662531, 72954546, 2850000]) {
|
||||
union() {
|
||||
cylinder(d = 4500000, h = 300000);
|
||||
translate(v = [0, 0, -2250000.0]) {
|
||||
cylinder(d1 = 0, d2 = 4500000, h = 2250000.0);
|
||||
}
|
||||
translate(v = [0, 0, -10000000]) {
|
||||
cylinder(d = 2000000, h = 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/***********************************************
|
||||
********* SolidPython code: **********
|
||||
************************************************
|
||||
|
||||
from pcbnewTransition import pcbnew
|
||||
import numpy as np
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from kikit.common import *
|
||||
from kikit.defs import *
|
||||
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
|
||||
from kikit.export import gerberImpl, pasteDxfExport
|
||||
from kikit.export import exportSettingsJlcpcb
|
||||
# Allow importing solid from this dir
|
||||
import os
|
||||
import sys
|
||||
cur_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if cur_path not in sys.path:
|
||||
sys.path.insert(0, cur_path)
|
||||
import solid
|
||||
import solid.utils
|
||||
import subprocess
|
||||
import shutil
|
||||
from kikit.common import removeComponents, parseReferences
|
||||
|
||||
from shapely.geometry import Point
|
||||
|
||||
|
||||
OUTER_BORDER = fromMm(7.5)
|
||||
INNER_BORDER = fromMm(5)
|
||||
MOUNTING_HOLES_COUNT = 3
|
||||
MOUNTING_HOLE_R = fromMm(1)
|
||||
HOLE_SPACING = fromMm(20)
|
||||
|
||||
def addBottomCounterpart(board, item):
|
||||
item = item.Duplicate()
|
||||
item.SetLayer(Layer.B_Paste)
|
||||
board.Add(item)
|
||||
|
||||
def addRoundedCorner(board, center, start, end, thickness):
|
||||
corner = pcbnew.PCB_SHAPE()
|
||||
corner.SetShape(STROKE_T.S_ARC)
|
||||
corner.SetCenter(toKiCADPoint((center[0], center[1])))
|
||||
corner.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
|
||||
if np.cross(start - center, end - center) > 0:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(90), True)
|
||||
else:
|
||||
corner.SetArcAngleAndEnd(fromDegrees(-90), True)
|
||||
corner.SetWidth(thickness)
|
||||
corner.SetLayer(Layer.F_Paste)
|
||||
board.Add(corner)
|
||||
addBottomCounterpart(board, corner)
|
||||
|
||||
def addLine(board, start, end, thickness):
|
||||
line = pcbnew.PCB_SHAPE()
|
||||
line.SetShape(STROKE_T.S_SEGMENT)
|
||||
line.SetStart(toKiCADPoint((start[0], start[1])))
|
||||
line.SetEnd(toKiCADPoint((end[0], end[1])))
|
||||
line.SetWidth(thickness)
|
||||
line.SetLayer(Layer.F_Paste)
|
||||
board.Add(line)
|
||||
addBottomCounterpart(board, line)
|
||||
|
||||
def addBite(board, origin, direction, normal, thickness):
|
||||
"""
|
||||
Adds a bite to the stencil, direction points to the bridge, normal points
|
||||
inside the stencil
|
||||
"""
|
||||
direction = normalize(direction) * thickness
|
||||
normal = normalize(normal) * thickness
|
||||
center = toKiCADPoint((origin[0], origin[1])) + toKiCADPoint((normal[0], normal[1]))
|
||||
start = origin
|
||||
end = center + toKiCADPoint((direction[0], direction[1]))
|
||||
# addLine(board, end, end + normal / 2, thickness)
|
||||
addRoundedCorner(board, center, start, end, thickness)
|
||||
|
||||
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
|
||||
"""
|
||||
Return number of bridges which fit inside the length and cut length
|
||||
"""
|
||||
count = round(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
|
||||
cutLength = round((length - (count - 1) * bridgeWidth) / count)
|
||||
return count, cutLength
|
||||
|
||||
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
|
||||
"""
|
||||
Add rectangular frame to the board
|
||||
"""
|
||||
R=fromMm(1)
|
||||
|
||||
corners = [
|
||||
(tl(rect), toKiCADPoint((R, 0)), toKiCADPoint((0, R))), # TL
|
||||
(tr(rect), toKiCADPoint((0, R)), toKiCADPoint((-R, 0))), # TR
|
||||
(br(rect), toKiCADPoint((-R, 0)), toKiCADPoint((0, -R))), # BR
|
||||
(bl(rect), toKiCADPoint((0, -R)), toKiCADPoint((R, 0))) # BL
|
||||
]
|
||||
for c, sOffset, eOffset in corners:
|
||||
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = round(rect.GetX() + R + i * bridgeWidth + i * cutLength)
|
||||
end = start + cutLength
|
||||
|
||||
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
|
||||
addLine(board, toKiCADPoint((start, y1)), toKiCADPoint((end, y1)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y1)), toKiCADPoint((-1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y1)), toKiCADPoint((1, 0)), toKiCADPoint((0, 1)), clearance)
|
||||
addLine(board, toKiCADPoint((start, y2)), toKiCADPoint((end, y2)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((start, y2)), toKiCADPoint((-1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((end, y2)), toKiCADPoint((1, 0)), toKiCADPoint((0, -1)), clearance)
|
||||
|
||||
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
|
||||
for i in range(count):
|
||||
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
|
||||
end = start + cutLength
|
||||
|
||||
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
|
||||
addLine(board, toKiCADPoint((x1, start)), toKiCADPoint((x1, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x1, start)), toKiCADPoint((0, -1)), toKiCADPoint((1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x1, end)), toKiCADPoint((0, 1)), toKiCADPoint((1, 0)), clearance)
|
||||
addLine(board, toKiCADPoint((x2, start)), toKiCADPoint((x2, end)), clearance)
|
||||
if i != 0:
|
||||
addBite(board, toKiCADPoint((x2, start)), toKiCADPoint((0, -1)), toKiCADPoint((-1, 0)), clearance)
|
||||
if i != count - 1:
|
||||
addBite(board, toKiCADPoint((x2, end)), toKiCADPoint((0, 1)), toKiCADPoint((-1, 0)), clearance)
|
||||
|
||||
def addHole(board, position, radius):
|
||||
circle = pcbnew.PCB_SHAPE()
|
||||
circle.SetShape(STROKE_T.S_CIRCLE)
|
||||
circle.SetCenter(toKiCADPoint((position[0], position[1])))
|
||||
# Set 3'oclock point of the circle to set radius
|
||||
circle.SetEnd(toKiCADPoint((position[0], position[1])) + toKiCADPoint((radius/2, 0)))
|
||||
|
||||
circle.SetWidth(radius)
|
||||
circle.SetLayer(Layer.F_Paste)
|
||||
board.Add(circle)
|
||||
addBottomCounterpart(board, circle)
|
||||
|
||||
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
|
||||
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
|
||||
"""
|
||||
Given a Pcbnew board finds the board outline and creates a stencil for
|
||||
KiKit's stencil jig.
|
||||
|
||||
Mainly, adds mounting holes and mouse bites to define the panel outline.
|
||||
|
||||
jigFrameSize is a tuple (width, height).
|
||||
"""
|
||||
bBox = findBoardBoundingBox(board)
|
||||
frameSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
|
||||
cutSize = rectByCenter(rectCenter(bBox),
|
||||
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
|
||||
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
|
||||
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
|
||||
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((x, OUTER_BORDER / 2 + frameSize.GetY())), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight())), MOUNTING_HOLE_R)
|
||||
for i in range(MOUNTING_HOLES_COUNT):
|
||||
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
|
||||
addHole(board, toKiCADPoint((OUTER_BORDER / 2 + frameSize.GetX(), y)), MOUNTING_HOLE_R)
|
||||
addHole(board, toKiCADPoint((- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y)), MOUNTING_HOLE_R)
|
||||
|
||||
PIN_TOLERANCE = fromMm(0.05)
|
||||
addHole(board, tl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, tr(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, br(frameSize) + toKiCADPoint((-OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
addHole(board, bl(frameSize) + toKiCADPoint((OUTER_BORDER / 2, -OUTER_BORDER / 2)), MOUNTING_HOLE_R + PIN_TOLERANCE)
|
||||
|
||||
def jigMountingHoles(jigFrameSize, origin=toKiCADPoint((0, 0))):
|
||||
""" Get list of all mounting holes in a jig of given size """
|
||||
w, h = jigFrameSize
|
||||
holes = [
|
||||
toKiCADPoint((0, (w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint((0, -(w + INNER_BORDER) / 2)),
|
||||
toKiCADPoint(((h + INNER_BORDER) / 2, 0)),
|
||||
toKiCADPoint((-(h + INNER_BORDER) / 2, 0)),
|
||||
]
|
||||
return [x + origin for x in holes]
|
||||
|
||||
def createOuterPolygon(board, jigFrameSize, outerBorder):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
holes = jigMountingHoles(jigFrameSize, centerpoint)
|
||||
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
|
||||
tabs = []
|
||||
for hole in holes:
|
||||
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, maxHeight=fromMm(1000))
|
||||
tabs.append(tab)
|
||||
outerSubstrate.union(tabs)
|
||||
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
|
||||
outerSubstrate.millFillets(fromMm(3))
|
||||
return outerSubstrate.exterior(), holes
|
||||
|
||||
def createOffsetPolygon(board, offset):
|
||||
outerSubstrate = Substrate(collectEdges(board, Layer.Edge_Cuts))
|
||||
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
|
||||
return outerSubstrate.exterior()
|
||||
|
||||
def m2countersink():
|
||||
HEAD_DIA = fromMm(4.5)
|
||||
HOLE_LEN = fromMm(10)
|
||||
SINK_EXTRA = fromMm(0.3)
|
||||
sinkH = np.sqrt(HEAD_DIA**2 / 4)
|
||||
|
||||
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
|
||||
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
|
||||
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
|
||||
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
|
||||
|
||||
def mirrorX(linestring, origin):
|
||||
return [(2 * origin - x, y) for x, y in linestring]
|
||||
|
||||
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, topSide):
|
||||
bBox = findBoardBoundingBox(board)
|
||||
centerpoint = rectCenter(bBox)
|
||||
|
||||
top = jigThickness - fromMm(0.15)
|
||||
pcbBottom = jigThickness - pcbThickness
|
||||
|
||||
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
|
||||
outerRing = outerPolygon.exterior.coords
|
||||
if topSide:
|
||||
outerRing = mirrorX(outerRing, centerpoint[0])
|
||||
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
|
||||
outerRing))
|
||||
|
||||
innerRings = [x.exterior.coords for x in listGeometries(createOffsetPolygon(board, - innerBorder))]
|
||||
if topSide:
|
||||
innerRings = [mirrorX(innerRing, centerpoint[0]) for innerRing in innerRings]
|
||||
|
||||
innerCutout = solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRings[0])))
|
||||
for innerRing in innerRings[1:]:
|
||||
innerCutout = innerCutout + solid.utils.down(jigThickness)(
|
||||
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
|
||||
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
|
||||
if topSide:
|
||||
registerRing = mirrorX(registerRing, centerpoint[0])
|
||||
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
|
||||
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
|
||||
|
||||
register = body - innerCutout - registerCutout
|
||||
for hole in holes:
|
||||
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
|
||||
return solid.scale(toMm(1))(
|
||||
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
|
||||
|
||||
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, True)
|
||||
|
||||
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder=fromMm(3), innerBorder=fromMm(1),
|
||||
tolerance=fromMm(0.05)):
|
||||
"""
|
||||
Create a SolidPython representation of the top register
|
||||
"""
|
||||
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
|
||||
outerBorder, innerBorder, tolerance, False)
|
||||
|
||||
def renderScad(infile, outfile):
|
||||
infile = os.path.abspath(infile)
|
||||
outfile = os.path.abspath(outfile)
|
||||
try:
|
||||
subprocess.run(["openscad", "-o", outfile, infile],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
message = f"Cannot render {outfile}, OpenSCAD error:\n"
|
||||
message += (e.stdout.decode("utf-8") + "\n") if e.stdout is not None else ""
|
||||
message += (e.stderr.decode("utf-8") + "\n") if e.stderr is not None else ""
|
||||
raise RuntimeError(message)
|
||||
except FileNotFoundError as e:
|
||||
message = f"OpenSCAD is not available.\n"
|
||||
message += f"Did you install it? Program `openscad` has to be in PATH"
|
||||
raise RuntimeError(message)
|
||||
|
||||
def shapelyToSHAPE_POLY_SET(polygon):
|
||||
p = pcbnew.SHAPE_POLY_SET()
|
||||
p.AddOutline(linestringToKicad(polygon.exterior))
|
||||
return p
|
||||
|
||||
def cutoutComponents(board, components):
|
||||
topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd)
|
||||
for polygon in topCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.F_Paste)
|
||||
board.Add(zone)
|
||||
bottomCutout = extractComponentPolygons(components, pcbnew.B_CrtYd)
|
||||
for polygon in bottomCutout:
|
||||
zone = pcbnew.PCB_SHAPE()
|
||||
zone.SetShape(STROKE_T.S_POLYGON)
|
||||
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
|
||||
zone.SetLayer(Layer.B_Paste)
|
||||
board.Add(zone)
|
||||
|
||||
def setStencilLayerVisibility(boardName):
|
||||
prlPath = os.path.splitext(boardName)[0] + ".kicad_prl"
|
||||
try:
|
||||
with open(prlPath, encoding="utf-8") as f:
|
||||
# We use ordered dict, so we preserve the ordering of the keys and
|
||||
# thus, formatting
|
||||
prl = json.load(f, object_pairs_hook=OrderedDict)
|
||||
except FileNotFoundError:
|
||||
# KiCAD didn't generate project local settings, let's create an empty one
|
||||
prl = {
|
||||
"board": {}
|
||||
}
|
||||
prl["board"]["visible_layers"] = "ffc000c_7ffffffe"
|
||||
prl["board"]["visible_items"] = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
13,
|
||||
21,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
34,
|
||||
35
|
||||
]
|
||||
with open(prlPath, "w", encoding="utf-8") as f:
|
||||
json.dump(prl, f, indent=2)
|
||||
pass
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
|
||||
registerborder, tolerance, ignore, cutout):
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
removeComponents(board, refs)
|
||||
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
|
||||
addJigFrame(board, jigsize)
|
||||
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
|
||||
|
||||
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
|
||||
board.Save(stencilFile)
|
||||
|
||||
setStencilLayerVisibility(stencilFile)
|
||||
|
||||
plotPlan = [
|
||||
# name, id, comment
|
||||
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
|
||||
("PasteTop", pcbnew.F_Paste, "Paste top"),
|
||||
]
|
||||
# get a copy of exportSettingsJlcpcb dictionary and
|
||||
# exclude the Edge.Cuts layer for creation of stencil gerber files
|
||||
exportSettings = exportSettingsJlcpcb.copy()
|
||||
exportSettings["ExcludeEdgeLayer"] = True
|
||||
gerberDir = os.path.join(outputdir, "gerber")
|
||||
gerberImpl(stencilFile, gerberDir, plotPlan, False, exportSettings)
|
||||
|
||||
shutil.make_archive(os.path.join(outputdir, "gerbers"), "zip", gerberDir)
|
||||
|
||||
jigthickness = fromMm(jigthickness)
|
||||
pcbthickness = fromMm(pcbthickness)
|
||||
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
|
||||
tolerance = fromMm(tolerance)
|
||||
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
|
||||
outerBorder, innerBorder, tolerance)
|
||||
|
||||
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
|
||||
solid.scad_render_to_file(topRegister, topRegisterFile)
|
||||
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
|
||||
|
||||
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
|
||||
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
|
||||
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
|
||||
|
||||
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
|
||||
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
|
||||
body = solid.linear_extrude(height=thickness + frameHeight)(
|
||||
bodyOffset(solid.import_dxf(outlineDxf)))
|
||||
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
|
||||
board = solid.utils.up(thickness)(
|
||||
solid.linear_extrude(height=thickness + frameHeight)(
|
||||
boardOffset(solid.import_dxf(outlineDxf))))
|
||||
return body - board
|
||||
|
||||
def getComponents(board, references):
|
||||
"""
|
||||
Return a list of components based on designator
|
||||
"""
|
||||
return [f for f in board.GetFootprints() if f.GetReference() in references]
|
||||
|
||||
def collectFootprintEdges(footprint, layer):
|
||||
"""
|
||||
Return all edges on given layer in given footprint
|
||||
"""
|
||||
return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer]
|
||||
|
||||
def extractComponentPolygons(footprints, srcLayer):
|
||||
"""
|
||||
Return a list of shapely polygons with holes for already placed components.
|
||||
The source layer defines the geometry on which the cutout is computed.
|
||||
Usually it a font or back courtyard
|
||||
"""
|
||||
polygons = []
|
||||
for f in footprints:
|
||||
edges = collectFootprintEdges(f, srcLayer)
|
||||
for ring in extractRings(edges):
|
||||
polygons.append(toShapely(ring, edges))
|
||||
return polygons
|
||||
|
||||
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
|
||||
frameClearance, enlargeHoles, front):
|
||||
zScale = -1 if front else 1
|
||||
xRotate = 180 if front else 0
|
||||
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
|
||||
thickness, frameHeight, frameWidth, frameClearance))
|
||||
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
|
||||
holes = solid.linear_extrude(height=4*thickness, center=True)(
|
||||
holesOffset(solid.import_dxf(holesDxf)))
|
||||
substrate -= holes
|
||||
for h in extraHoles:
|
||||
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
|
||||
solid.linear_extrude(height=4*thickness, center=True)(
|
||||
solid.polygon(h.exterior.coords)))
|
||||
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
|
||||
|
||||
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
|
||||
ignore, cutout, frameclearance, enlargeholes):
|
||||
"""
|
||||
Create a 3D printed self-registering stencil.
|
||||
"""
|
||||
board = pcbnew.LoadBoard(inputboard)
|
||||
refs = parseReferences(ignore)
|
||||
cutoutComponents = getComponents(board, parseReferences(cutout))
|
||||
removeComponents(board, refs)
|
||||
Path(outputdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# We create the stencil based on DXF export. Using it avoids the necessity
|
||||
# to interpret KiCAD PAD shapes which constantly change with newer and newer
|
||||
# versions.
|
||||
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
|
||||
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
|
||||
# On Windows, OpenSCAD requires to use forward slashes instead of backslashes,
|
||||
# hence, the replacement:
|
||||
if os.name == "nt":
|
||||
bottomPaste = bottomPaste.replace("\\", "/")
|
||||
topPaste = topPaste.replace("\\", "/")
|
||||
outline = outline.replace("\\", "/")
|
||||
|
||||
topCutout = extractComponentPolygons(cutoutComponents, pcbnew.F_CrtYd)
|
||||
bottomCutout = extractComponentPolygons(cutoutComponents, pcbnew.B_CrtYd)
|
||||
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
|
||||
framewidth, frameclearance, enlargeholes, True)
|
||||
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
|
||||
height, framewidth, frameclearance, enlargeholes, False)
|
||||
|
||||
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
|
||||
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
|
||||
|
||||
topStencilFile = os.path.join(outputdir, "topStencil.scad")
|
||||
solid.scad_render_to_file(topStencil, topStencilFile,
|
||||
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
|
||||
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
************************************************/
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user