Compare commits

..

7 Commits

Author SHA1 Message Date
Bane Stojkovic 8abaf2884b Update update.xml 2026-11-02 10:16:52 +01:00
Bane Stojkovic 79d230ff07 mark old ones for removal 2026-11-01 21:22:29 +01:00
Bane Stojkovic 23ecf9e8d7 more models 2026-11-01 20:39:57 +01:00
Bane Stojkovic fce069895f test fixes 2026-11-01 20:19:52 +01:00
Bane Stojkovic 4688879d0e IC improvements 2026-11-01 17:08:20 +01:00
Bane Stojkovic 15277452f4 Re-modeled factory variants of Resistor component type as datasheet in order to make them available to the AI; this will produce more accurate AI-driven edits 2026-10-31 20:21:04 +01:00
Bane Stojkovic 5d92812d3e catalog 2026-10-31 19:46:09 +01:00
146 changed files with 2873 additions and 9128 deletions
@@ -2,7 +2,7 @@ Switch setup: [ ALL ]
2K2.+ <-> D1.K <-> D4.K <-> Q1.2
2K2.+ <-> D2.A <-> D3.A <-> Q2.2
2K2.- <-> 2K2.- <-> C6.- <-> C7.- <-> J1.Tip <-> J2.Tip <-> J3.Tip <-> LM324.10 <-> LM324.3 <-> LM324.5 <-> Q1.1 <-> Q2.1 <-> R5.2 <-> VR1.3 <-> VR2.3
2K2.- <-> 2K2.- <-> C6.- <-> C7.- <-> J1.Tip <-> J2.Tip <-> J3.Tip <-> LM324.12 <-> LM324.3 <-> LM324.5 <-> Q1.1 <-> Q2.1 <-> R5.2 <-> VR1.3 <-> VR2.3
C1.+ <-> J1.Sleeve
C1.- <-> R1.1
C2.+ <-> J2.Sleeve
@@ -11,15 +11,15 @@ Switch setup: [ ALL ]
C3.- <-> VR2.1
C4.+ <-> LM324.1 <-> R1.2
C4.- <-> VR1.1
C5.+ <-> LM324.8 <-> R3.2
C5.+ <-> LM324.14 <-> R3.2
C5.- <-> J3.Sleeve <-> R5.1
C6.+ <-> LM324.11 <-> Q2.3
C7.+ <-> LM324.4 <-> Q1.3
D1.A <-> D2.K
D3.K <-> D4.A
LM324.13 <-> R3.1 <-> R3.1 <-> R4.1
LM324.2 <-> R1.1 <-> R1.2
LM324.6 <-> R1.1 <-> R2.2
LM324.9 <-> R3.1 <-> R3.1 <-> R4.1
R3.2 <-> VR2.2
R4.2 <-> VR1.2
@@ -2,7 +2,7 @@ Switch setup: [ ALL ]
2K2.+ <-> D1.K <-> D4.K <-> Q1.2
2K2.+ <-> D2.A <-> D3.A <-> Q2.2
2K2.- <-> 2K2.- <-> C6.- <-> C7.- <-> J1.Tip <-> J2.Tip <-> J3.Tip <-> LM324.10 <-> LM324.3 <-> LM324.5 <-> Q1.1 <-> Q2.1 <-> R5.2 <-> VR1.3 <-> VR2.3
2K2.- <-> 2K2.- <-> C6.- <-> C7.- <-> J1.Tip <-> J2.Tip <-> J3.Tip <-> LM324.12 <-> LM324.3 <-> LM324.5 <-> Q1.1 <-> Q2.1 <-> R5.2 <-> VR1.3 <-> VR2.3
C1.+ <-> J1.Sleeve
C1.- <-> R1.1
C2.+ <-> J2.Sleeve
@@ -11,15 +11,15 @@ Switch setup: [ ALL ]
C3.- <-> VR2.1
C4.+ <-> LM324.1 <-> R1.2
C4.- <-> VR1.1
C5.+ <-> LM324.8 <-> R3.2
C5.+ <-> LM324.14 <-> R3.2
C5.- <-> J3.Sleeve <-> R5.1
C6.+ <-> LM324.11 <-> Q2.3
C7.+ <-> LM324.4 <-> Q1.3
D1.A <-> D2.K
D3.K <-> D4.A
LM324.13 <-> R3.1 <-> R3.1 <-> R4.1
LM324.2 <-> R1.1 <-> R1.2
LM324.6 <-> R1.1 <-> R2.2
LM324.9 <-> R3.1 <-> R3.1 <-> R4.1
R3.2 <-> VR2.2
R4.2 <-> VR1.2
@@ -1,4 +1,4 @@
Switch setup: [ ALL ]
+12V.+ <-> +12V.- <-> Arduino Nano.10 <-> Arduino Nano.11 <-> Arduino Nano.12 <-> Arduino Nano.13 <-> Arduino Nano.14 <-> Arduino Nano.15 <-> Arduino Nano.25 <-> Arduino Nano.26 <-> Arduino Nano.27 <-> Arduino Nano.28 <-> Arduino Nano.29 <-> Arduino Nano.30 <-> Arduino Nano.4 <-> Arduino Nano.5 <-> Arduino Nano.6 <-> Arduino Nano.7 <-> Arduino Nano.8 <-> Arduino Nano.9 <-> C1.1 <-> C1.2 <-> C2.1 <-> C2.2 <-> D1.A <-> D1.K <-> D2.A <-> D2.K <-> LM2902.1 <-> LM2902.1 <-> LM2902.10 <-> LM2902.10 <-> LM2902.11 <-> LM2902.11 <-> LM2902.12 <-> LM2902.12 <-> LM2902.13 <-> LM2902.13 <-> LM2902.14 <-> LM2902.14 <-> LM2902.2 <-> LM2902.2 <-> LM2902.3 <-> LM2902.3 <-> LM2902.4 <-> LM2902.4 <-> LM2902.5 <-> LM2902.5 <-> LM2902.6 <-> LM2902.6 <-> LM2902.7 <-> LM2902.7 <-> LM2902.8 <-> LM2902.8 <-> LM2902.9 <-> LM2902.9 <-> MPSA05.1 <-> MPSA05.1 <-> MPSA05.2 <-> MPSA05.2 <-> MPSA05.3 <-> MPSA05.3 <-> PH1.1 <-> PH1.2 <-> PH1.3 <-> PH2.1 <-> PH2.2 <-> PH2.3 <-> PH3.1 <-> PH3.2 <-> PH4.1 <-> PH4.2 <-> PH4.3 <-> PH5.1 <-> PH5.2 <-> PH5.3 <-> PH6.1 <-> Q3.1 <-> Q3.2 <-> Q3.3 <-> R1.1 <-> R1.1 <-> R1.2 <-> R1.2 <-> R10.1 <-> R10.1 <-> R10.2 <-> R10.2 <-> R11.1 <-> R11.1 <-> R11.2 <-> R11.2 <-> R12.1 <-> R12.1 <-> R12.2 <-> R12.2 <-> R3.1 <-> R3.1 <-> R3.2 <-> R3.2 <-> R4.1 <-> R4.1 <-> R4.2 <-> R4.2 <-> R5.1 <-> R5.1 <-> R5.2 <-> R5.2 <-> R6.1 <-> R6.1 <-> R6.2 <-> R6.2 <-> R8.1 <-> R8.1 <-> R8.2 <-> R8.2 <-> R9.1 <-> R9.1 <-> R9.2 <-> R9.2 <-> VR1.1 <-> VR1.2 <-> VR1.3 <-> VR2.1 <-> VR2.2 <-> VR2.3 <-> VR3.1 <-> VR3.2 <-> VR3.3 <-> VR4.1 <-> VR4.2 <-> VR4.3
+12V.+ <-> +12V.- <-> Arduino Nano.10 <-> Arduino Nano.11 <-> Arduino Nano.12 <-> Arduino Nano.13 <-> Arduino Nano.14 <-> Arduino Nano.15 <-> Arduino Nano.16 <-> Arduino Nano.17 <-> Arduino Nano.18 <-> Arduino Nano.19 <-> Arduino Nano.20 <-> Arduino Nano.21 <-> Arduino Nano.4 <-> Arduino Nano.5 <-> Arduino Nano.6 <-> Arduino Nano.7 <-> Arduino Nano.8 <-> Arduino Nano.9 <-> C1.1 <-> C1.2 <-> C2.1 <-> C2.2 <-> D1.A <-> D1.K <-> D2.A <-> D2.K <-> LM2902.1 <-> LM2902.1 <-> LM2902.10 <-> LM2902.10 <-> LM2902.11 <-> LM2902.11 <-> LM2902.12 <-> LM2902.12 <-> LM2902.13 <-> LM2902.13 <-> LM2902.14 <-> LM2902.14 <-> LM2902.2 <-> LM2902.2 <-> LM2902.3 <-> LM2902.3 <-> LM2902.4 <-> LM2902.4 <-> LM2902.5 <-> LM2902.5 <-> LM2902.6 <-> LM2902.6 <-> LM2902.7 <-> LM2902.7 <-> LM2902.8 <-> LM2902.8 <-> LM2902.9 <-> LM2902.9 <-> MPSA05.1 <-> MPSA05.1 <-> MPSA05.2 <-> MPSA05.2 <-> MPSA05.3 <-> MPSA05.3 <-> PH1.1 <-> PH1.2 <-> PH1.3 <-> PH2.1 <-> PH2.2 <-> PH2.3 <-> PH3.1 <-> PH3.2 <-> PH4.1 <-> PH4.2 <-> PH4.3 <-> PH5.1 <-> PH5.2 <-> PH5.3 <-> PH6.1 <-> Q3.1 <-> Q3.2 <-> Q3.3 <-> R1.1 <-> R1.1 <-> R1.2 <-> R1.2 <-> R10.1 <-> R10.1 <-> R10.2 <-> R10.2 <-> R11.1 <-> R11.1 <-> R11.2 <-> R11.2 <-> R12.1 <-> R12.1 <-> R12.2 <-> R12.2 <-> R3.1 <-> R3.1 <-> R3.2 <-> R3.2 <-> R4.1 <-> R4.1 <-> R4.2 <-> R4.2 <-> R5.1 <-> R5.1 <-> R5.2 <-> R5.2 <-> R6.1 <-> R6.1 <-> R6.2 <-> R6.2 <-> R8.1 <-> R8.1 <-> R8.2 <-> R8.2 <-> R9.1 <-> R9.1 <-> R9.2 <-> R9.2 <-> VR1.1 <-> VR1.2 <-> VR1.3 <-> VR2.1 <-> VR2.2 <-> VR2.3 <-> VR3.1 <-> VR3.2 <-> VR3.3 <-> VR4.1 <-> VR4.2 <-> VR4.3
@@ -1,4 +1,4 @@
Switch setup: [ ALL ]
+12V.+ <-> +12V.- <-> Arduino Nano.10 <-> Arduino Nano.11 <-> Arduino Nano.12 <-> Arduino Nano.13 <-> Arduino Nano.14 <-> Arduino Nano.15 <-> Arduino Nano.25 <-> Arduino Nano.26 <-> Arduino Nano.27 <-> Arduino Nano.28 <-> Arduino Nano.29 <-> Arduino Nano.30 <-> Arduino Nano.4 <-> Arduino Nano.5 <-> Arduino Nano.6 <-> Arduino Nano.7 <-> Arduino Nano.8 <-> Arduino Nano.9 <-> C1.1 <-> C1.2 <-> C2.1 <-> C2.2 <-> D1.A <-> D1.K <-> D2.A <-> D2.K <-> LM2902.1 <-> LM2902.1 <-> LM2902.10 <-> LM2902.10 <-> LM2902.11 <-> LM2902.11 <-> LM2902.12 <-> LM2902.12 <-> LM2902.13 <-> LM2902.13 <-> LM2902.14 <-> LM2902.14 <-> LM2902.2 <-> LM2902.2 <-> LM2902.3 <-> LM2902.3 <-> LM2902.4 <-> LM2902.4 <-> LM2902.5 <-> LM2902.5 <-> LM2902.6 <-> LM2902.6 <-> LM2902.7 <-> LM2902.7 <-> LM2902.8 <-> LM2902.8 <-> LM2902.9 <-> LM2902.9 <-> MPSA05.1 <-> MPSA05.1 <-> MPSA05.2 <-> MPSA05.2 <-> MPSA05.3 <-> MPSA05.3 <-> PH1.1 <-> PH1.2 <-> PH1.3 <-> PH2.1 <-> PH2.2 <-> PH2.3 <-> PH3.1 <-> PH3.2 <-> PH4.1 <-> PH4.2 <-> PH4.3 <-> PH5.1 <-> PH5.2 <-> PH5.3 <-> PH6.1 <-> Q3.1 <-> Q3.2 <-> Q3.3 <-> R1.1 <-> R1.1 <-> R1.2 <-> R1.2 <-> R10.1 <-> R10.1 <-> R10.2 <-> R10.2 <-> R11.1 <-> R11.1 <-> R11.2 <-> R11.2 <-> R12.1 <-> R12.1 <-> R12.2 <-> R12.2 <-> R3.1 <-> R3.1 <-> R3.2 <-> R3.2 <-> R4.1 <-> R4.1 <-> R4.2 <-> R4.2 <-> R5.1 <-> R5.1 <-> R5.2 <-> R5.2 <-> R6.1 <-> R6.1 <-> R6.2 <-> R6.2 <-> R8.1 <-> R8.1 <-> R8.2 <-> R8.2 <-> R9.1 <-> R9.1 <-> R9.2 <-> R9.2 <-> VR1.1 <-> VR1.2 <-> VR1.3 <-> VR2.1 <-> VR2.2 <-> VR2.3 <-> VR3.1 <-> VR3.2 <-> VR3.3 <-> VR4.1 <-> VR4.2 <-> VR4.3
+12V.+ <-> +12V.- <-> Arduino Nano.10 <-> Arduino Nano.11 <-> Arduino Nano.12 <-> Arduino Nano.13 <-> Arduino Nano.14 <-> Arduino Nano.15 <-> Arduino Nano.16 <-> Arduino Nano.17 <-> Arduino Nano.18 <-> Arduino Nano.19 <-> Arduino Nano.20 <-> Arduino Nano.21 <-> Arduino Nano.4 <-> Arduino Nano.5 <-> Arduino Nano.6 <-> Arduino Nano.7 <-> Arduino Nano.8 <-> Arduino Nano.9 <-> C1.1 <-> C1.2 <-> C2.1 <-> C2.2 <-> D1.A <-> D1.K <-> D2.A <-> D2.K <-> LM2902.1 <-> LM2902.1 <-> LM2902.10 <-> LM2902.10 <-> LM2902.11 <-> LM2902.11 <-> LM2902.12 <-> LM2902.12 <-> LM2902.13 <-> LM2902.13 <-> LM2902.14 <-> LM2902.14 <-> LM2902.2 <-> LM2902.2 <-> LM2902.3 <-> LM2902.3 <-> LM2902.4 <-> LM2902.4 <-> LM2902.5 <-> LM2902.5 <-> LM2902.6 <-> LM2902.6 <-> LM2902.7 <-> LM2902.7 <-> LM2902.8 <-> LM2902.8 <-> LM2902.9 <-> LM2902.9 <-> MPSA05.1 <-> MPSA05.1 <-> MPSA05.2 <-> MPSA05.2 <-> MPSA05.3 <-> MPSA05.3 <-> PH1.1 <-> PH1.2 <-> PH1.3 <-> PH2.1 <-> PH2.2 <-> PH2.3 <-> PH3.1 <-> PH3.2 <-> PH4.1 <-> PH4.2 <-> PH4.3 <-> PH5.1 <-> PH5.2 <-> PH5.3 <-> PH6.1 <-> Q3.1 <-> Q3.2 <-> Q3.3 <-> R1.1 <-> R1.1 <-> R1.2 <-> R1.2 <-> R10.1 <-> R10.1 <-> R10.2 <-> R10.2 <-> R11.1 <-> R11.1 <-> R11.2 <-> R11.2 <-> R12.1 <-> R12.1 <-> R12.2 <-> R12.2 <-> R3.1 <-> R3.1 <-> R3.2 <-> R3.2 <-> R4.1 <-> R4.1 <-> R4.2 <-> R4.2 <-> R5.1 <-> R5.1 <-> R5.2 <-> R5.2 <-> R6.1 <-> R6.1 <-> R6.2 <-> R6.2 <-> R8.1 <-> R8.1 <-> R8.2 <-> R8.2 <-> R9.1 <-> R9.1 <-> R9.2 <-> R9.2 <-> VR1.1 <-> VR1.2 <-> VR1.3 <-> VR2.1 <-> VR2.2 <-> VR2.3 <-> VR3.1 <-> VR3.2 <-> VR3.3 <-> VR4.1 <-> VR4.2 <-> VR4.3
@@ -1,23 +1,23 @@
Switch setup: [ ALL ]
C1.+ <-> R10.2 <-> R2.1 <-> R9.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.11 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.13 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.14 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.12 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C10.1 <-> R22.1 <-> R30.2
C10.2 <-> R16.1 <-> V3.7
C11.1 <-> IC3.9
C11.1 <-> IC3.16
C11.2 <-> C14.1 <-> C8.2 <-> R18.1 <-> R25.2
C12.1 <-> R11.1 <-> V2.3
C12.2 <-> R21.2
C13.2 <-> R17.2 <-> V3.6
C14.2 <-> C23.1 <-> IC2.9 <-> R18.2 <-> R33.1 <-> V3.2
C14.2 <-> C23.1 <-> IC2.16 <-> R18.2 <-> R33.1 <-> V3.2
C15.1 <-> C9.1 <-> R19.1
C15.2 <-> C22.1 <-> IC2.13 <-> R19.2 <-> R29.1
C15.2 <-> C22.1 <-> IC2.12 <-> R19.2 <-> R29.1
C16.1 <-> C21.1 <-> R27.1 <-> V2.1
C16.2 <-> R32.2 <-> V2.2 <-> VR1.2
C17.1 <-> R13.2 <-> V3.3
C17.2 <-> R34.1
C18.+ <-> IC1.4 <-> Q2.1
C18.- <-> C29.- <-> IC1.3 <-> Q2.2 <-> SW1.2
C18.+ <-> IC1.3 <-> Q2.1
C18.- <-> C29.- <-> IC1.4 <-> Q2.2 <-> SW1.2
C19.2 <-> R21.1 <-> R28.2 <-> V2.7
C2.+ <-> C6.2 <-> R11.2 <-> R12.2 <-> R2.2 <-> R3.1
C20.1 <-> C27.1 <-> R26.2 <-> V1.1
@@ -40,11 +40,11 @@ Switch setup: [ ALL ]
C8.1 <-> R10.1 <-> V1.8
D2.A <-> D3.K
D4.A <-> D5.K
D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC2.16 <-> IC3.16 <-> IC4.16 <-> SW1.1
D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC2.9 <-> IC3.9 <-> IC4.9 <-> SW1.1
D9.A <-> R37.2
IC3.13 <-> R36.2
IC4.11 <-> VR6.2 <-> VR6.3
IC4.9 <-> VR7.2 <-> VR7.3
IC3.12 <-> R36.2
IC4.14 <-> VR6.2 <-> VR6.3
IC4.16 <-> VR7.2 <-> VR7.3
Q1.3 <-> R1.1 <-> R4.1
R16.2 <-> R17.1 <-> R31.2
R22.2 <-> R34.2 <-> VR6.1 <-> VR7.1
@@ -1,23 +1,23 @@
Switch setup: [ SW1.ON1 ]
C1.+ <-> R10.2 <-> R2.1 <-> R9.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.11 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.13 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.14 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.12 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C10.1 <-> R22.1 <-> R30.2
C10.2 <-> R16.1 <-> V3.7
C11.1 <-> IC3.9
C11.1 <-> IC3.16
C11.2 <-> C14.1 <-> C8.2 <-> R18.1 <-> R25.2
C12.1 <-> R11.1 <-> V2.3
C12.2 <-> R21.2
C13.2 <-> R17.2 <-> V3.6
C14.2 <-> C23.1 <-> IC2.9 <-> R18.2 <-> R33.1 <-> V3.2
C14.2 <-> C23.1 <-> IC2.16 <-> R18.2 <-> R33.1 <-> V3.2
C15.1 <-> C9.1 <-> R19.1
C15.2 <-> C22.1 <-> IC2.13 <-> R19.2 <-> R29.1
C15.2 <-> C22.1 <-> IC2.12 <-> R19.2 <-> R29.1
C16.1 <-> C21.1 <-> R27.1 <-> V2.1
C16.2 <-> R32.2 <-> V2.2 <-> VR1.2
C17.1 <-> R13.2 <-> V3.3
C17.2 <-> R34.1
C18.+ <-> IC1.4 <-> Q2.1
C18.- <-> C29.- <-> D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC1.3 <-> IC2.16 <-> IC3.16 <-> IC4.16 <-> Q2.2
C18.+ <-> IC1.3 <-> Q2.1
C18.- <-> C29.- <-> D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC1.4 <-> IC2.9 <-> IC3.9 <-> IC4.9 <-> Q2.2
C19.2 <-> R21.1 <-> R28.2 <-> V2.7
C2.+ <-> C6.2 <-> R11.2 <-> R12.2 <-> R2.2 <-> R3.1
C20.1 <-> C27.1 <-> R26.2 <-> V1.1
@@ -41,9 +41,9 @@ Switch setup: [ SW1.ON1 ]
D2.A <-> D3.K
D4.A <-> D5.K
D9.A <-> R37.2
IC3.13 <-> R36.2
IC4.11 <-> VR6.2 <-> VR6.3
IC4.9 <-> VR7.2 <-> VR7.3
IC3.12 <-> R36.2
IC4.14 <-> VR6.2 <-> VR6.3
IC4.16 <-> VR7.2 <-> VR7.3
Q1.3 <-> R1.1 <-> R4.1
R16.2 <-> R17.1 <-> R31.2
R22.2 <-> R34.2 <-> VR6.1 <-> VR7.1
@@ -53,23 +53,23 @@ Switch setup: [ SW1.ON1 ]
Switch setup: [ SW1.ON2 ]
C1.+ <-> R10.2 <-> R2.1 <-> R9.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.11 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.13 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C1.- <-> C19.1 <-> C2.- <-> C20.2 <-> C21.2 <-> C22.2 <-> C23.2 <-> C28.2 <-> C3.- <-> C4.- <-> C5.- <-> D3.A <-> D5.A <-> IC2.1 <-> IC2.2 <-> IC2.3 <-> IC2.4 <-> IC2.5 <-> IC3.1 <-> IC3.14 <-> IC3.2 <-> IC3.3 <-> IC3.4 <-> IC3.5 <-> IC4.1 <-> IC4.12 <-> IC4.2 <-> IC4.3 <-> IC4.4 <-> IC4.5 <-> R23.2 <-> R24.2 <-> R25.1 <-> R26.1 <-> R27.2 <-> R28.1 <-> R29.2 <-> R30.1 <-> R31.1 <-> R32.1 <-> R33.2 <-> R35.2 <-> R4.2 <-> R5.2 <-> R6.2 <-> R7.2 <-> VR1.3 <-> VR2.3 <-> VR4.1 <-> VR4.2
C10.1 <-> R22.1 <-> R30.2
C10.2 <-> R16.1 <-> V3.7
C11.1 <-> IC3.9
C11.1 <-> IC3.16
C11.2 <-> C14.1 <-> C8.2 <-> R18.1 <-> R25.2
C12.1 <-> R11.1 <-> V2.3
C12.2 <-> R21.2
C13.2 <-> R17.2 <-> V3.6
C14.2 <-> C23.1 <-> IC2.9 <-> R18.2 <-> R33.1 <-> V3.2
C14.2 <-> C23.1 <-> IC2.16 <-> R18.2 <-> R33.1 <-> V3.2
C15.1 <-> C9.1 <-> R19.1
C15.2 <-> C22.1 <-> IC2.13 <-> R19.2 <-> R29.1
C15.2 <-> C22.1 <-> IC2.12 <-> R19.2 <-> R29.1
C16.1 <-> C21.1 <-> R27.1 <-> V2.1
C16.2 <-> R32.2 <-> V2.2 <-> VR1.2
C17.1 <-> R13.2 <-> V3.3
C17.2 <-> R34.1
C18.+ <-> IC1.4 <-> Q2.1
C18.- <-> C29.- <-> IC1.3 <-> Q2.2
C18.+ <-> IC1.3 <-> Q2.1
C18.- <-> C29.- <-> IC1.4 <-> Q2.2
C19.2 <-> R21.1 <-> R28.2 <-> V2.7
C2.+ <-> C6.2 <-> R11.2 <-> R12.2 <-> R2.2 <-> R3.1
C20.1 <-> C27.1 <-> R26.2 <-> V1.1
@@ -92,11 +92,11 @@ Switch setup: [ SW1.ON2 ]
C8.1 <-> R10.1 <-> V1.8
D2.A <-> D3.K
D4.A <-> D5.K
D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC2.16 <-> IC3.16 <-> IC4.16
D6.A <-> D7.A <-> D8.A <-> D9.K <-> IC2.9 <-> IC3.9 <-> IC4.9
D9.A <-> R37.2
IC3.13 <-> R36.2
IC4.11 <-> VR6.2 <-> VR6.3
IC4.9 <-> VR7.2 <-> VR7.3
IC3.12 <-> R36.2
IC4.14 <-> VR6.2 <-> VR6.3
IC4.16 <-> VR7.2 <-> VR7.3
Q1.3 <-> R1.1 <-> R4.1
R16.2 <-> R17.1 <-> R31.2
R22.2 <-> R34.2 <-> VR6.1 <-> VR7.1
@@ -2,15 +2,15 @@ Switch setup: [ ALL ]
C1.1 <-> Q1.2 <-> R2.1
C1.2 <-> VR2.3
C2.+ <-> IC1.8 <-> R3.1
C2.+ <-> IC1.5 <-> R3.1
C2.- <-> R4.2 <-> SW1.2
C3.1 <-> IC1.6
C3.1 <-> IC1.7
C3.2 <-> C4.- <-> C5.2 <-> IC1.3 <-> IC1.4 <-> J1.Sleeve <-> J2.Sleeve <-> J3.Center <-> R1.1 <-> R2.2 <-> VR2.1
C4.+ <-> IC1.7 <-> J3.Sleeve <-> Q1.1
C4.+ <-> IC1.6 <-> J3.Sleeve <-> Q1.1
C5.1 <-> R3.2
IC1.1 <-> VR1.3
IC1.2 <-> VR2.2
IC1.5 <-> VR1.2
IC1.8 <-> VR1.2
J1.Tip <-> R4.1 <-> R5.1 <-> SW1.1
J2.Tip <-> Q1.3 <-> R1.2
R5.2 <-> SW1.3
@@ -1,47 +1,47 @@
Switch setup: [ SW1.OFF ]
C1.1 <-> Q1.2 <-> R2.1
C1.2 <-> VR2.3
C2.+ <-> IC1.8 <-> R3.1
C2.- <-> R4.2
C3.1 <-> IC1.6
C3.2 <-> C4.- <-> C5.2 <-> IC1.3 <-> IC1.4 <-> J1.Sleeve <-> J2.Sleeve <-> J3.Center <-> R1.1 <-> R2.2 <-> VR2.1
C4.+ <-> IC1.7 <-> J3.Sleeve <-> Q1.1
C5.1 <-> R3.2
IC1.1 <-> VR1.3
IC1.2 <-> VR2.2
IC1.5 <-> VR1.2
J1.Tip <-> R4.1 <-> R5.1
J2.Tip <-> Q1.3 <-> R1.2
Switch setup: [ SW1.ON1 ]
C1.1 <-> Q1.2 <-> R2.1
C1.2 <-> VR2.3
C2.+ <-> IC1.8 <-> R3.1
C2.+ <-> IC1.5 <-> R3.1
C2.- <-> J1.Tip <-> R4.1 <-> R4.2 <-> R5.1
C3.1 <-> IC1.6
C3.1 <-> IC1.7
C3.2 <-> C4.- <-> C5.2 <-> IC1.3 <-> IC1.4 <-> J1.Sleeve <-> J2.Sleeve <-> J3.Center <-> R1.1 <-> R2.2 <-> VR2.1
C4.+ <-> IC1.7 <-> J3.Sleeve <-> Q1.1
C4.+ <-> IC1.6 <-> J3.Sleeve <-> Q1.1
C5.1 <-> R3.2
IC1.1 <-> VR1.3
IC1.2 <-> VR2.2
IC1.5 <-> VR1.2
IC1.8 <-> VR1.2
J2.Tip <-> Q1.3 <-> R1.2
Switch setup: [ SW1.OFF ]
C1.1 <-> Q1.2 <-> R2.1
C1.2 <-> VR2.3
C2.+ <-> IC1.5 <-> R3.1
C2.- <-> R4.2
C3.1 <-> IC1.7
C3.2 <-> C4.- <-> C5.2 <-> IC1.3 <-> IC1.4 <-> J1.Sleeve <-> J2.Sleeve <-> J3.Center <-> R1.1 <-> R2.2 <-> VR2.1
C4.+ <-> IC1.6 <-> J3.Sleeve <-> Q1.1
C5.1 <-> R3.2
IC1.1 <-> VR1.3
IC1.2 <-> VR2.2
IC1.8 <-> VR1.2
J1.Tip <-> R4.1 <-> R5.1
J2.Tip <-> Q1.3 <-> R1.2
Switch setup: [ SW1.ON2 ]
C1.1 <-> Q1.2 <-> R2.1
C1.2 <-> VR2.3
C2.+ <-> IC1.8 <-> R3.1
C2.+ <-> IC1.5 <-> R3.1
C2.- <-> R4.2 <-> R5.2
C3.1 <-> IC1.6
C3.1 <-> IC1.7
C3.2 <-> C4.- <-> C5.2 <-> IC1.3 <-> IC1.4 <-> J1.Sleeve <-> J2.Sleeve <-> J3.Center <-> R1.1 <-> R2.2 <-> VR2.1
C4.+ <-> IC1.7 <-> J3.Sleeve <-> Q1.1
C4.+ <-> IC1.6 <-> J3.Sleeve <-> Q1.1
C5.1 <-> R3.2
IC1.1 <-> VR1.3
IC1.2 <-> VR2.2
IC1.5 <-> VR1.2
IC1.8 <-> VR1.2
J1.Tip <-> R4.1 <-> R5.1
J2.Tip <-> Q1.3 <-> R1.2
@@ -1,33 +1,33 @@
Switch setup: [ ALL ]
C1.1 <-> IC1.8 <-> IC2.16 <-> IC3.2 <-> R3.2 <-> VR1.3
C1.2 <-> D14.A <-> D7.K <-> IC1.1 <-> IC2.9 <-> IC3.1 <-> R1.2 <-> VR1.1 <-> VR4.2 <-> VR4.3
C2.1 <-> IC3.6 <-> R2.2
C1.1 <-> IC1.8 <-> IC2.9 <-> IC3.2 <-> R3.2 <-> VR1.3
C1.2 <-> D14.A <-> D7.K <-> IC1.1 <-> IC2.16 <-> IC3.1 <-> R1.2 <-> VR1.1 <-> VR4.2 <-> VR4.3
C2.1 <-> IC3.7 <-> R2.2
C2.2 <-> VR2.3
C3.1 <-> R3.1 <-> VR3.2
C3.2 <-> C6.2 <-> C8.+ <-> R4.1 <-> R5.2 <-> R7.2 <-> R9.1
C4.1 <-> C7.- <-> C8.- <-> D16.A <-> IC3.4 <-> R7.1 <-> R8.1 <-> VR2.1
C4.2 <-> IC3.8 <-> R6.1
C4.2 <-> IC3.5 <-> R6.1
C5.1 <-> IC3.3 <-> R4.2
C5.2 <-> R8.2
C6.1 <-> VR3.3
C7.+ <-> D16.K <-> IC3.5 <-> R9.2
D1.A <-> IC1.15 <-> IC1.16
C7.+ <-> D16.K <-> IC3.8 <-> R9.2
D1.A <-> IC1.10 <-> IC1.9
D1.K <-> D2.A <-> IC1.6 <-> IC1.7
D10.A <-> D11.K <-> IC2.12 <-> IC2.13
D10.K <-> D9.A <-> IC2.5 <-> IC2.6
D11.A <-> D12.K <-> IC2.3 <-> IC2.4
D12.A <-> D13.K <-> IC2.10 <-> IC2.11
D12.A <-> D13.K <-> IC2.14 <-> IC2.15
D13.A <-> D14.K <-> IC2.1 <-> IC2.2
D15.K <-> Q1.1
D2.K <-> D3.A <-> IC1.13 <-> IC1.14
D2.K <-> D3.A <-> IC1.11 <-> IC1.12
D3.K <-> D4.A <-> IC1.4 <-> IC1.5
D4.K <-> D5.A <-> IC1.11 <-> IC1.12
D4.K <-> D5.A <-> IC1.13 <-> IC1.14
D5.K <-> D6.A <-> IC1.2 <-> IC1.3
D6.K <-> D7.A <-> IC1.10 <-> IC1.9
D8.A <-> D9.K <-> IC2.14 <-> IC2.15
D6.K <-> D7.A <-> IC1.15 <-> IC1.16
D8.A <-> D9.K <-> IC2.10 <-> IC2.11
D8.K <-> IC2.7 <-> IC2.8
IC3.7 <-> R2.1 <-> R5.1
IC3.6 <-> R2.1 <-> R5.1
Q1.2 <-> Q1.3
R1.1 <-> VR1.2
R6.2 <-> VR4.1
@@ -1,33 +1,33 @@
Switch setup: [ ALL ]
C1.1 <-> IC1.8 <-> IC2.16 <-> IC3.2 <-> R3.2 <-> VR1.3
C1.2 <-> D14.A <-> D7.K <-> IC1.1 <-> IC2.9 <-> IC3.1 <-> R1.2 <-> VR1.1 <-> VR4.2 <-> VR4.3
C2.1 <-> IC3.6 <-> R2.2
C1.1 <-> IC1.8 <-> IC2.9 <-> IC3.2 <-> R3.2 <-> VR1.3
C1.2 <-> D14.A <-> D7.K <-> IC1.1 <-> IC2.16 <-> IC3.1 <-> R1.2 <-> VR1.1 <-> VR4.2 <-> VR4.3
C2.1 <-> IC3.7 <-> R2.2
C2.2 <-> VR2.3
C3.1 <-> R3.1 <-> VR3.2
C3.2 <-> C6.2 <-> C8.+ <-> R4.1 <-> R5.2 <-> R7.2 <-> R9.1
C4.1 <-> C7.- <-> C8.- <-> D16.A <-> IC3.4 <-> R7.1 <-> R8.1 <-> VR2.1
C4.2 <-> IC3.8 <-> R6.1
C4.2 <-> IC3.5 <-> R6.1
C5.1 <-> IC3.3 <-> R4.2
C5.2 <-> R8.2
C6.1 <-> VR3.3
C7.+ <-> D16.K <-> IC3.5 <-> R9.2
D1.A <-> IC1.15 <-> IC1.16
C7.+ <-> D16.K <-> IC3.8 <-> R9.2
D1.A <-> IC1.10 <-> IC1.9
D1.K <-> D2.A <-> IC1.6 <-> IC1.7
D10.A <-> D11.K <-> IC2.12 <-> IC2.13
D10.K <-> D9.A <-> IC2.5 <-> IC2.6
D11.A <-> D12.K <-> IC2.3 <-> IC2.4
D12.A <-> D13.K <-> IC2.10 <-> IC2.11
D12.A <-> D13.K <-> IC2.14 <-> IC2.15
D13.A <-> D14.K <-> IC2.1 <-> IC2.2
D15.K <-> Q1.1
D2.K <-> D3.A <-> IC1.13 <-> IC1.14
D2.K <-> D3.A <-> IC1.11 <-> IC1.12
D3.K <-> D4.A <-> IC1.4 <-> IC1.5
D4.K <-> D5.A <-> IC1.11 <-> IC1.12
D4.K <-> D5.A <-> IC1.13 <-> IC1.14
D5.K <-> D6.A <-> IC1.2 <-> IC1.3
D6.K <-> D7.A <-> IC1.10 <-> IC1.9
D8.A <-> D9.K <-> IC2.14 <-> IC2.15
D6.K <-> D7.A <-> IC1.15 <-> IC1.16
D8.A <-> D9.K <-> IC2.10 <-> IC2.11
D8.K <-> IC2.7 <-> IC2.8
IC3.7 <-> R2.1 <-> R5.1
IC3.6 <-> R2.1 <-> R5.1
Q1.2 <-> Q1.3
R1.1 <-> VR1.2
R6.2 <-> VR4.1
@@ -1,7 +1,7 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> IC1.10 <-> IC1.11 <-> IC1.20 <-> IC1.21 <-> IC1.22 <-> IC1.28 <-> IC1.29 <-> IC1.3 <-> IC1.30 <-> IC1.31 <-> IC1.33 <-> IC1.4 <-> IC2.10 <-> IC2.11 <-> IC2.20 <-> IC2.21 <-> IC2.22 <-> IC2.28 <-> IC2.29 <-> IC2.3 <-> IC2.30 <-> IC2.31 <-> IC2.33 <-> IC2.4 <-> IC3.4 <-> IC3.8 <-> IC4.8
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> IC1.32 <-> IC1.42 <-> IC1.5 <-> IC1.6 <-> IC1.8 <-> IC2.32 <-> IC2.42 <-> IC2.5 <-> IC2.6 <-> IC2.8 <-> IC3.9 <-> IC4.6 <-> IC4.9
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> IC1.10 <-> IC1.11 <-> IC1.20 <-> IC1.21 <-> IC1.22 <-> IC1.3 <-> IC1.4 <-> IC1.40 <-> IC1.42 <-> IC1.43 <-> IC1.44 <-> IC1.45 <-> IC2.10 <-> IC2.11 <-> IC2.20 <-> IC2.21 <-> IC2.22 <-> IC2.3 <-> IC2.4 <-> IC2.40 <-> IC2.42 <-> IC2.43 <-> IC2.44 <-> IC2.45 <-> IC3.4 <-> IC3.8 <-> IC4.8
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> IC1.31 <-> IC1.41 <-> IC1.5 <-> IC1.6 <-> IC1.8 <-> IC2.31 <-> IC2.41 <-> IC2.5 <-> IC2.6 <-> IC2.8 <-> IC3.16 <-> IC4.16 <-> IC4.6
IC1.1 <-> IC2.1
IC1.12 <-> IC2.12
IC1.13 <-> IC2.13
@@ -12,9 +12,9 @@ Switch setup: [ ALL ]
IC1.18 <-> IC2.18
IC1.19 <-> IC2.19
IC1.2 <-> IC2.2
IC1.25 <-> IC2.25
IC1.26 <-> IC2.26
IC1.27 <-> IC2.27 <-> IC3.5 <-> IC4.5
IC1.46 <-> IC2.46 <-> IC3.5 <-> IC4.5
IC1.47 <-> IC2.47
IC1.48 <-> IC2.48
IC3.1 <-> IC4.1
IC3.2 <-> IC4.2
IC3.3 <-> IC4.3
@@ -1,7 +1,7 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> IC1.10 <-> IC1.11 <-> IC1.20 <-> IC1.21 <-> IC1.22 <-> IC1.28 <-> IC1.29 <-> IC1.3 <-> IC1.30 <-> IC1.31 <-> IC1.33 <-> IC1.4 <-> IC2.10 <-> IC2.11 <-> IC2.20 <-> IC2.21 <-> IC2.22 <-> IC2.28 <-> IC2.29 <-> IC2.3 <-> IC2.30 <-> IC2.31 <-> IC2.33 <-> IC2.4 <-> IC3.4 <-> IC3.8 <-> IC4.8
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> IC1.32 <-> IC1.42 <-> IC1.5 <-> IC1.6 <-> IC1.8 <-> IC2.32 <-> IC2.42 <-> IC2.5 <-> IC2.6 <-> IC2.8 <-> IC3.9 <-> IC4.6 <-> IC4.9
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> IC1.10 <-> IC1.11 <-> IC1.20 <-> IC1.21 <-> IC1.22 <-> IC1.3 <-> IC1.4 <-> IC1.40 <-> IC1.42 <-> IC1.43 <-> IC1.44 <-> IC1.45 <-> IC2.10 <-> IC2.11 <-> IC2.20 <-> IC2.21 <-> IC2.22 <-> IC2.3 <-> IC2.4 <-> IC2.40 <-> IC2.42 <-> IC2.43 <-> IC2.44 <-> IC2.45 <-> IC3.4 <-> IC3.8 <-> IC4.8
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> IC1.31 <-> IC1.41 <-> IC1.5 <-> IC1.6 <-> IC1.8 <-> IC2.31 <-> IC2.41 <-> IC2.5 <-> IC2.6 <-> IC2.8 <-> IC3.16 <-> IC4.16 <-> IC4.6
IC1.1 <-> IC2.1
IC1.12 <-> IC2.12
IC1.13 <-> IC2.13
@@ -12,9 +12,9 @@ Switch setup: [ ALL ]
IC1.18 <-> IC2.18
IC1.19 <-> IC2.19
IC1.2 <-> IC2.2
IC1.25 <-> IC2.25
IC1.26 <-> IC2.26
IC1.27 <-> IC2.27 <-> IC3.5 <-> IC4.5
IC1.46 <-> IC2.46 <-> IC3.5 <-> IC4.5
IC1.47 <-> IC2.47
IC1.48 <-> IC2.48
IC3.1 <-> IC4.1
IC3.2 <-> IC4.2
IC3.3 <-> IC4.3
@@ -3,21 +3,21 @@ Switch setup: [ ALL ]
C1.1 <-> IC1.2 <-> R1.1
C1.2 <-> C3.- <-> C7.- <-> C8.+ <-> D3.K <-> IC1.4 <-> R3.2
C10.+ <-> D5.K <-> D6.A
C10.- <-> C9.+ <-> IC2.8
C10.- <-> C9.+ <-> IC2.5
C12.+ <-> D6.K
C12.- <-> C14.- <-> C2.2 <-> IC2.4 <-> R4.2
C14.+ <-> D5.A <-> IC2.7
C14.+ <-> D5.A <-> IC2.6
C2.1 <-> IC2.2 <-> R5.1
C3.+ <-> D1.A <-> IC1.7
C4.+ <-> C5.- <-> C6.+ <-> IC1.8
C3.+ <-> D1.A <-> IC1.6
C4.+ <-> C5.- <-> C6.+ <-> IC1.5
C4.- <-> R1.2 <-> R2.2
C5.+ <-> D1.K <-> D2.A
C6.- <-> D3.A <-> D4.K
C7.+ <-> D2.K
C8.- <-> D4.A
C9.- <-> R5.2 <-> R6.2
IC1.1 <-> IC1.5
IC1.1 <-> IC1.8
IC1.3 <-> R2.1 <-> R3.1
IC2.1 <-> IC2.5
IC2.1 <-> IC2.8
IC2.3 <-> R4.1 <-> R6.1
@@ -3,21 +3,21 @@ Switch setup: [ ALL ]
C1.1 <-> IC1.2 <-> R1.1
C1.2 <-> C3.- <-> C7.- <-> C8.+ <-> D3.K <-> IC1.4 <-> R3.2
C10.+ <-> D5.K <-> D6.A
C10.- <-> C9.+ <-> IC2.8
C10.- <-> C9.+ <-> IC2.5
C12.+ <-> D6.K
C12.- <-> C14.- <-> C2.2 <-> IC2.4 <-> R4.2
C14.+ <-> D5.A <-> IC2.7
C14.+ <-> D5.A <-> IC2.6
C2.1 <-> IC2.2 <-> R5.1
C3.+ <-> D1.A <-> IC1.7
C4.+ <-> C5.- <-> C6.+ <-> IC1.8
C3.+ <-> D1.A <-> IC1.6
C4.+ <-> C5.- <-> C6.+ <-> IC1.5
C4.- <-> R1.2 <-> R2.2
C5.+ <-> D1.K <-> D2.A
C6.- <-> D3.A <-> D4.K
C7.+ <-> D2.K
C8.- <-> D4.A
C9.- <-> R5.2 <-> R6.2
IC1.1 <-> IC1.5
IC1.1 <-> IC1.8
IC1.3 <-> R2.1 <-> R3.1
IC2.1 <-> IC2.5
IC2.1 <-> IC2.8
IC2.3 <-> R4.1 <-> R6.1
@@ -1,20 +1,20 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> IC1.10 <-> IC2.10 <-> IC5.10
C1.2 <-> C10.2 <-> C2.2 <-> C3.2 <-> C4.1 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.2 <-> IC1.11 <-> IC10.11 <-> IC10.12 <-> IC11.11 <-> IC2.11 <-> IC3.11 <-> IC4.17 <-> IC5.11 <-> IC6.12 <-> IC6.6 <-> IC6.9 <-> IC7.11 <-> IC9.11
C10.1 <-> C4.2 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> C9.1 <-> IC10.1 <-> IC10.10 <-> IC11.1 <-> IC11.10 <-> IC3.1 <-> IC3.10 <-> IC4.16 <-> IC4.27 <-> IC6.8 <-> IC7.1 <-> IC7.10 <-> IC9.1 <-> IC9.10
C1.2 <-> C10.2 <-> C2.2 <-> C3.2 <-> C4.1 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.2 <-> IC1.20 <-> IC10.19 <-> IC10.20 <-> IC11.20 <-> IC2.20 <-> IC3.20 <-> IC4.32 <-> IC5.20 <-> IC6.13 <-> IC6.16 <-> IC6.6 <-> IC7.20 <-> IC9.20
C10.1 <-> C4.2 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> C9.1 <-> IC10.1 <-> IC10.10 <-> IC11.1 <-> IC11.10 <-> IC3.1 <-> IC3.10 <-> IC4.16 <-> IC4.22 <-> IC6.8 <-> IC7.1 <-> IC7.10 <-> IC9.1 <-> IC9.10
IC1.1 <-> IC2.1 <-> IC5.1
IC1.2 <-> IC10.13 <-> IC11.13 <-> IC2.2 <-> IC3.20 <-> IC4.13 <-> IC4.22 <-> IC5.2 <-> IC7.19 <-> IC9.13
IC1.3 <-> IC10.14 <-> IC11.14 <-> IC2.3 <-> IC3.19 <-> IC4.14 <-> IC4.23 <-> IC5.3 <-> IC7.18 <-> IC9.14
IC1.4 <-> IC10.15 <-> IC11.15 <-> IC2.4 <-> IC3.18 <-> IC4.15 <-> IC4.26 <-> IC5.4 <-> IC7.17 <-> IC9.15
IC1.5 <-> IC10.16 <-> IC11.16 <-> IC2.5 <-> IC3.17 <-> IC4.32 <-> IC5.5 <-> IC7.16 <-> IC9.16
IC1.6 <-> IC10.17 <-> IC11.17 <-> IC2.6 <-> IC3.16 <-> IC4.31 <-> IC5.6 <-> IC7.15 <-> IC9.17
IC1.7 <-> IC10.18 <-> IC11.18 <-> IC2.7 <-> IC3.15 <-> IC4.30 <-> IC5.7 <-> IC7.14 <-> IC9.18
IC1.8 <-> IC10.19 <-> IC11.19 <-> IC2.8 <-> IC3.14 <-> IC4.29 <-> IC5.8 <-> IC7.13 <-> IC9.19
IC1.9 <-> IC10.20 <-> IC11.20 <-> IC2.9 <-> IC3.13 <-> IC4.28 <-> IC5.9 <-> IC7.20 <-> IC9.20
IC11.12 <-> IC6.11
IC3.12 <-> IC6.14
IC4.25 <-> IC6.4 <-> IC6.5
IC6.13 <-> IC9.12
IC6.15 <-> IC7.12
IC1.2 <-> IC10.18 <-> IC11.18 <-> IC2.2 <-> IC3.11 <-> IC4.13 <-> IC4.27 <-> IC5.2 <-> IC7.12 <-> IC9.18
IC1.3 <-> IC10.17 <-> IC11.17 <-> IC2.3 <-> IC3.12 <-> IC4.14 <-> IC4.26 <-> IC5.3 <-> IC7.13 <-> IC9.17
IC1.4 <-> IC10.16 <-> IC11.16 <-> IC2.4 <-> IC3.13 <-> IC4.15 <-> IC4.23 <-> IC5.4 <-> IC7.14 <-> IC9.16
IC1.5 <-> IC10.15 <-> IC11.15 <-> IC2.5 <-> IC3.14 <-> IC4.17 <-> IC5.5 <-> IC7.15 <-> IC9.15
IC1.6 <-> IC10.14 <-> IC11.14 <-> IC2.6 <-> IC3.15 <-> IC4.18 <-> IC5.6 <-> IC7.16 <-> IC9.14
IC1.7 <-> IC10.13 <-> IC11.13 <-> IC2.7 <-> IC3.16 <-> IC4.19 <-> IC5.7 <-> IC7.17 <-> IC9.13
IC1.8 <-> IC10.12 <-> IC11.12 <-> IC2.8 <-> IC3.17 <-> IC4.20 <-> IC5.8 <-> IC7.18 <-> IC9.12
IC1.9 <-> IC10.11 <-> IC11.11 <-> IC2.9 <-> IC3.18 <-> IC4.21 <-> IC5.9 <-> IC7.11 <-> IC9.11
IC11.19 <-> IC6.14
IC3.19 <-> IC6.11
IC4.24 <-> IC6.4 <-> IC6.5
IC6.10 <-> IC7.19
IC6.12 <-> IC9.19
@@ -1,20 +1,20 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> IC1.10 <-> IC2.10 <-> IC5.10
C1.2 <-> C10.2 <-> C2.2 <-> C3.2 <-> C4.1 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.2 <-> IC1.11 <-> IC10.11 <-> IC10.12 <-> IC11.11 <-> IC2.11 <-> IC3.11 <-> IC4.17 <-> IC5.11 <-> IC6.12 <-> IC6.6 <-> IC6.9 <-> IC7.11 <-> IC9.11
C10.1 <-> C4.2 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> C9.1 <-> IC10.1 <-> IC10.10 <-> IC11.1 <-> IC11.10 <-> IC3.1 <-> IC3.10 <-> IC4.16 <-> IC4.27 <-> IC6.8 <-> IC7.1 <-> IC7.10 <-> IC9.1 <-> IC9.10
C1.2 <-> C10.2 <-> C2.2 <-> C3.2 <-> C4.1 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.2 <-> IC1.20 <-> IC10.19 <-> IC10.20 <-> IC11.20 <-> IC2.20 <-> IC3.20 <-> IC4.32 <-> IC5.20 <-> IC6.13 <-> IC6.16 <-> IC6.6 <-> IC7.20 <-> IC9.20
C10.1 <-> C4.2 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> C9.1 <-> IC10.1 <-> IC10.10 <-> IC11.1 <-> IC11.10 <-> IC3.1 <-> IC3.10 <-> IC4.16 <-> IC4.22 <-> IC6.8 <-> IC7.1 <-> IC7.10 <-> IC9.1 <-> IC9.10
IC1.1 <-> IC2.1 <-> IC5.1
IC1.2 <-> IC10.13 <-> IC11.13 <-> IC2.2 <-> IC3.20 <-> IC4.13 <-> IC4.22 <-> IC5.2 <-> IC7.19 <-> IC9.13
IC1.3 <-> IC10.14 <-> IC11.14 <-> IC2.3 <-> IC3.19 <-> IC4.14 <-> IC4.23 <-> IC5.3 <-> IC7.18 <-> IC9.14
IC1.4 <-> IC10.15 <-> IC11.15 <-> IC2.4 <-> IC3.18 <-> IC4.15 <-> IC4.26 <-> IC5.4 <-> IC7.17 <-> IC9.15
IC1.5 <-> IC10.16 <-> IC11.16 <-> IC2.5 <-> IC3.17 <-> IC4.32 <-> IC5.5 <-> IC7.16 <-> IC9.16
IC1.6 <-> IC10.17 <-> IC11.17 <-> IC2.6 <-> IC3.16 <-> IC4.31 <-> IC5.6 <-> IC7.15 <-> IC9.17
IC1.7 <-> IC10.18 <-> IC11.18 <-> IC2.7 <-> IC3.15 <-> IC4.30 <-> IC5.7 <-> IC7.14 <-> IC9.18
IC1.8 <-> IC10.19 <-> IC11.19 <-> IC2.8 <-> IC3.14 <-> IC4.29 <-> IC5.8 <-> IC7.13 <-> IC9.19
IC1.9 <-> IC10.20 <-> IC11.20 <-> IC2.9 <-> IC3.13 <-> IC4.28 <-> IC5.9 <-> IC7.20 <-> IC9.20
IC11.12 <-> IC6.11
IC3.12 <-> IC6.14
IC4.25 <-> IC6.4 <-> IC6.5
IC6.13 <-> IC9.12
IC6.15 <-> IC7.12
IC1.2 <-> IC10.18 <-> IC11.18 <-> IC2.2 <-> IC3.11 <-> IC4.13 <-> IC4.27 <-> IC5.2 <-> IC7.12 <-> IC9.18
IC1.3 <-> IC10.17 <-> IC11.17 <-> IC2.3 <-> IC3.12 <-> IC4.14 <-> IC4.26 <-> IC5.3 <-> IC7.13 <-> IC9.17
IC1.4 <-> IC10.16 <-> IC11.16 <-> IC2.4 <-> IC3.13 <-> IC4.15 <-> IC4.23 <-> IC5.4 <-> IC7.14 <-> IC9.16
IC1.5 <-> IC10.15 <-> IC11.15 <-> IC2.5 <-> IC3.14 <-> IC4.17 <-> IC5.5 <-> IC7.15 <-> IC9.15
IC1.6 <-> IC10.14 <-> IC11.14 <-> IC2.6 <-> IC3.15 <-> IC4.18 <-> IC5.6 <-> IC7.16 <-> IC9.14
IC1.7 <-> IC10.13 <-> IC11.13 <-> IC2.7 <-> IC3.16 <-> IC4.19 <-> IC5.7 <-> IC7.17 <-> IC9.13
IC1.8 <-> IC10.12 <-> IC11.12 <-> IC2.8 <-> IC3.17 <-> IC4.20 <-> IC5.8 <-> IC7.18 <-> IC9.12
IC1.9 <-> IC10.11 <-> IC11.11 <-> IC2.9 <-> IC3.18 <-> IC4.21 <-> IC5.9 <-> IC7.11 <-> IC9.11
IC11.19 <-> IC6.14
IC3.19 <-> IC6.11
IC4.24 <-> IC6.4 <-> IC6.5
IC6.10 <-> IC7.19
IC6.12 <-> IC9.19
@@ -1,7 +1,7 @@
Switch setup: [ ALL ]
C1.1 <-> C10.2 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.1 <-> C6.2 <-> C9.2 <-> IC1.1 <-> IC1.10 <-> IC10.12 <-> IC11.12 <-> IC2.1 <-> IC2.10 <-> IC3.1 <-> IC3.10 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.8 <-> IC8.1 <-> IC8.10 <-> IC9.10 <-> IC9.12
C1.2 <-> C10.1 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.2 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.1 <-> IC1.11 <-> IC10.11 <-> IC11.11 <-> IC2.11 <-> IC3.11 <-> IC4.9 <-> IC5.11 <-> IC6.9 <-> IC8.11 <-> IC9.11
C1.1 <-> C10.2 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.1 <-> C6.2 <-> C9.2 <-> IC1.1 <-> IC1.10 <-> IC10.19 <-> IC11.19 <-> IC2.1 <-> IC2.10 <-> IC3.1 <-> IC3.10 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.8 <-> IC8.1 <-> IC8.10 <-> IC9.10 <-> IC9.19
C1.2 <-> C10.1 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.2 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.1 <-> IC1.20 <-> IC10.20 <-> IC11.20 <-> IC2.20 <-> IC3.20 <-> IC4.16 <-> IC5.20 <-> IC6.16 <-> IC8.20 <-> IC9.20
C7.2 <-> C8.2 <-> IC10.10 <-> IC11.10
IC1.2 <-> IC2.2 <-> IC3.2 <-> IC5.2 <-> IC6.3 <-> IC8.2
IC1.3 <-> IC2.3 <-> IC3.3 <-> IC5.3 <-> IC6.2 <-> IC8.3
@@ -11,16 +11,16 @@ Switch setup: [ ALL ]
IC1.7 <-> IC2.7 <-> IC3.7 <-> IC4.2 <-> IC5.7 <-> IC8.7
IC1.8 <-> IC2.8 <-> IC3.8 <-> IC4.6 <-> IC5.8 <-> IC8.8
IC1.9 <-> IC2.9 <-> IC3.9 <-> IC4.7 <-> IC5.9 <-> IC8.9
IC10.13 <-> IC11.13 <-> IC6.10 <-> IC9.13
IC10.14 <-> IC11.14 <-> IC6.1 <-> IC9.14
IC10.15 <-> IC11.15 <-> IC6.15 <-> IC9.15
IC10.16 <-> IC11.16 <-> IC6.16 <-> IC9.16
IC10.17 <-> IC11.17 <-> IC4.10 <-> IC9.17
IC10.18 <-> IC11.18 <-> IC4.1 <-> IC9.18
IC10.19 <-> IC11.19 <-> IC4.15 <-> IC9.19
IC10.20 <-> IC11.20 <-> IC4.16 <-> IC9.20
IC10.11 <-> IC11.11 <-> IC4.9 <-> IC9.11
IC10.12 <-> IC11.12 <-> IC4.10 <-> IC9.12
IC10.13 <-> IC11.13 <-> IC4.1 <-> IC9.13
IC10.14 <-> IC11.14 <-> IC4.15 <-> IC9.14
IC10.15 <-> IC11.15 <-> IC6.9 <-> IC9.15
IC10.16 <-> IC11.16 <-> IC6.10 <-> IC9.16
IC10.17 <-> IC11.17 <-> IC6.1 <-> IC9.17
IC10.18 <-> IC11.18 <-> IC6.15 <-> IC9.18
IC4.11 <-> IC6.11
IC4.14 <-> IC6.14
IC4.4 <-> IC6.12
IC4.5 <-> IC6.13
IC4.4 <-> IC6.13
IC4.5 <-> IC6.12
@@ -1,7 +1,7 @@
Switch setup: [ ALL ]
C1.1 <-> C10.2 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.1 <-> C6.2 <-> C9.2 <-> IC1.1 <-> IC1.10 <-> IC10.12 <-> IC11.12 <-> IC2.1 <-> IC2.10 <-> IC3.1 <-> IC3.10 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.8 <-> IC8.1 <-> IC8.10 <-> IC9.10 <-> IC9.12
C1.2 <-> C10.1 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.2 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.1 <-> IC1.11 <-> IC10.11 <-> IC11.11 <-> IC2.11 <-> IC3.11 <-> IC4.9 <-> IC5.11 <-> IC6.9 <-> IC8.11 <-> IC9.11
C1.1 <-> C10.2 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.1 <-> C6.2 <-> C9.2 <-> IC1.1 <-> IC1.10 <-> IC10.19 <-> IC11.19 <-> IC2.1 <-> IC2.10 <-> IC3.1 <-> IC3.10 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.8 <-> IC8.1 <-> IC8.10 <-> IC9.10 <-> IC9.19
C1.2 <-> C10.1 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.2 <-> C6.1 <-> C7.1 <-> C8.1 <-> C9.1 <-> IC1.20 <-> IC10.20 <-> IC11.20 <-> IC2.20 <-> IC3.20 <-> IC4.16 <-> IC5.20 <-> IC6.16 <-> IC8.20 <-> IC9.20
C7.2 <-> C8.2 <-> IC10.10 <-> IC11.10
IC1.2 <-> IC2.2 <-> IC3.2 <-> IC5.2 <-> IC6.3 <-> IC8.2
IC1.3 <-> IC2.3 <-> IC3.3 <-> IC5.3 <-> IC6.2 <-> IC8.3
@@ -11,16 +11,16 @@ Switch setup: [ ALL ]
IC1.7 <-> IC2.7 <-> IC3.7 <-> IC4.2 <-> IC5.7 <-> IC8.7
IC1.8 <-> IC2.8 <-> IC3.8 <-> IC4.6 <-> IC5.8 <-> IC8.8
IC1.9 <-> IC2.9 <-> IC3.9 <-> IC4.7 <-> IC5.9 <-> IC8.9
IC10.13 <-> IC11.13 <-> IC6.10 <-> IC9.13
IC10.14 <-> IC11.14 <-> IC6.1 <-> IC9.14
IC10.15 <-> IC11.15 <-> IC6.15 <-> IC9.15
IC10.16 <-> IC11.16 <-> IC6.16 <-> IC9.16
IC10.17 <-> IC11.17 <-> IC4.10 <-> IC9.17
IC10.18 <-> IC11.18 <-> IC4.1 <-> IC9.18
IC10.19 <-> IC11.19 <-> IC4.15 <-> IC9.19
IC10.20 <-> IC11.20 <-> IC4.16 <-> IC9.20
IC10.11 <-> IC11.11 <-> IC4.9 <-> IC9.11
IC10.12 <-> IC11.12 <-> IC4.10 <-> IC9.12
IC10.13 <-> IC11.13 <-> IC4.1 <-> IC9.13
IC10.14 <-> IC11.14 <-> IC4.15 <-> IC9.14
IC10.15 <-> IC11.15 <-> IC6.9 <-> IC9.15
IC10.16 <-> IC11.16 <-> IC6.10 <-> IC9.16
IC10.17 <-> IC11.17 <-> IC6.1 <-> IC9.17
IC10.18 <-> IC11.18 <-> IC6.15 <-> IC9.18
IC4.11 <-> IC6.11
IC4.14 <-> IC6.14
IC4.4 <-> IC6.12
IC4.5 <-> IC6.13
IC4.4 <-> IC6.13
IC4.5 <-> IC6.12
@@ -1,20 +1,20 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> IC1.10 <-> IC1.14 <-> IC1.15 <-> IC1.16 <-> IC1.9 <-> IC2.1 <-> IC2.10 <-> IC2.14 <-> IC2.15 <-> IC2.16 <-> IC2.9 <-> IC3.10 <-> IC3.14 <-> IC3.15 <-> IC3.16 <-> IC3.9 <-> IC4.10 <-> IC4.14 <-> IC4.15 <-> IC4.16 <-> IC4.9 <-> IC5.11 <-> IC6.11 <-> IC7.11 <-> IC8.11
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> IC1.10 <-> IC1.11 <-> IC1.15 <-> IC1.16 <-> IC1.9 <-> IC2.1 <-> IC2.10 <-> IC2.11 <-> IC2.15 <-> IC2.16 <-> IC2.9 <-> IC3.10 <-> IC3.11 <-> IC3.15 <-> IC3.16 <-> IC3.9 <-> IC4.10 <-> IC4.11 <-> IC4.15 <-> IC4.16 <-> IC4.9 <-> IC5.20 <-> IC6.20 <-> IC7.20 <-> IC8.20
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> IC1.1 <-> IC1.8 <-> IC2.8 <-> IC3.1 <-> IC3.8 <-> IC4.1 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.1 <-> IC6.10 <-> IC7.1 <-> IC7.10 <-> IC8.1 <-> IC8.10
IC1.11 <-> IC2.11 <-> IC3.11 <-> IC4.11
IC1.12 <-> IC3.4
IC1.13 <-> IC3.5
IC1.12 <-> IC3.5
IC1.13 <-> IC3.4
IC1.14 <-> IC2.14 <-> IC3.14 <-> IC4.14
IC1.2 <-> IC5.3 <-> IC8.3
IC1.3 <-> IC5.2 <-> IC8.2
IC1.4 <-> IC2.12
IC1.5 <-> IC2.13
IC1.4 <-> IC2.13
IC1.5 <-> IC2.12
IC1.6 <-> IC5.4 <-> IC8.4
IC1.7 <-> IC5.5 <-> IC8.5
IC2.2 <-> IC6.7 <-> IC7.7
IC2.3 <-> IC6.6 <-> IC7.6
IC2.4 <-> IC4.12
IC2.5 <-> IC4.13
IC2.4 <-> IC4.13
IC2.5 <-> IC4.12
IC2.6 <-> IC6.8 <-> IC7.8
IC2.7 <-> IC6.9 <-> IC7.9
IC3.2 <-> IC5.7 <-> IC8.7
@@ -1,20 +1,20 @@
Switch setup: [ ALL ]
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> IC1.10 <-> IC1.14 <-> IC1.15 <-> IC1.16 <-> IC1.9 <-> IC2.1 <-> IC2.10 <-> IC2.14 <-> IC2.15 <-> IC2.16 <-> IC2.9 <-> IC3.10 <-> IC3.14 <-> IC3.15 <-> IC3.16 <-> IC3.9 <-> IC4.10 <-> IC4.14 <-> IC4.15 <-> IC4.16 <-> IC4.9 <-> IC5.11 <-> IC6.11 <-> IC7.11 <-> IC8.11
C1.1 <-> C2.1 <-> C3.1 <-> C4.1 <-> C5.2 <-> C6.2 <-> C7.2 <-> C8.2 <-> IC1.10 <-> IC1.11 <-> IC1.15 <-> IC1.16 <-> IC1.9 <-> IC2.1 <-> IC2.10 <-> IC2.11 <-> IC2.15 <-> IC2.16 <-> IC2.9 <-> IC3.10 <-> IC3.11 <-> IC3.15 <-> IC3.16 <-> IC3.9 <-> IC4.10 <-> IC4.11 <-> IC4.15 <-> IC4.16 <-> IC4.9 <-> IC5.20 <-> IC6.20 <-> IC7.20 <-> IC8.20
C1.2 <-> C2.2 <-> C3.2 <-> C4.2 <-> C5.1 <-> C6.1 <-> C7.1 <-> C8.1 <-> IC1.1 <-> IC1.8 <-> IC2.8 <-> IC3.1 <-> IC3.8 <-> IC4.1 <-> IC4.8 <-> IC5.1 <-> IC5.10 <-> IC6.1 <-> IC6.10 <-> IC7.1 <-> IC7.10 <-> IC8.1 <-> IC8.10
IC1.11 <-> IC2.11 <-> IC3.11 <-> IC4.11
IC1.12 <-> IC3.4
IC1.13 <-> IC3.5
IC1.12 <-> IC3.5
IC1.13 <-> IC3.4
IC1.14 <-> IC2.14 <-> IC3.14 <-> IC4.14
IC1.2 <-> IC5.3 <-> IC8.3
IC1.3 <-> IC5.2 <-> IC8.2
IC1.4 <-> IC2.12
IC1.5 <-> IC2.13
IC1.4 <-> IC2.13
IC1.5 <-> IC2.12
IC1.6 <-> IC5.4 <-> IC8.4
IC1.7 <-> IC5.5 <-> IC8.5
IC2.2 <-> IC6.7 <-> IC7.7
IC2.3 <-> IC6.6 <-> IC7.6
IC2.4 <-> IC4.12
IC2.5 <-> IC4.13
IC2.4 <-> IC4.13
IC2.5 <-> IC4.12
IC2.6 <-> IC6.8 <-> IC7.8
IC2.7 <-> IC6.9 <-> IC7.9
IC3.2 <-> IC5.7 <-> IC8.7
@@ -1,31 +1,31 @@
Switch setup: [ ALL ]
BC548.1 <-> C1.1 <-> D1.K <-> IC1.9 <-> IC2.10
BC548.1 <-> C2.2 <-> D2.K <-> IC1.11 <-> IC5.10
BC548.1 <-> C3.1 <-> D4.K <-> IC1.1 <-> IC4.10
BC548.1 <-> C4.1 <-> D3.K <-> IC1.3 <-> IC6.12
BC548.1 <-> C1.1 <-> D1.K <-> IC1.13 <-> IC2.12
BC548.1 <-> C2.2 <-> D2.K <-> IC1.11 <-> IC5.12
BC548.1 <-> C3.1 <-> D4.K <-> IC1.1 <-> IC4.12
BC548.1 <-> C4.1 <-> D3.K <-> IC1.3 <-> IC6.10
BC548.2 <-> BC558.3 <-> R13.1
BC548.2 <-> BC558.3 <-> R17.1
BC548.2 <-> BC558.3 <-> R2.1
BC548.2 <-> BC558.3 <-> R36.1
BC548.3 <-> BC548.3 <-> BC548.3 <-> BC548.3 <-> C1.2 <-> C2.1 <-> C3.2 <-> C4.2 <-> IC1.13 <-> IC1.5 <-> IC1.7 <-> IC2.5 <-> IC5.5 <-> R11.2 <-> R14.2 <-> R18.1 <-> R22.2 <-> R23.2 <-> R24.2 <-> R26.2 <-> R27.2 <-> R28.2 <-> R3.2 <-> R30.2 <-> R37.2 <-> R39.2 <-> R40.2 <-> R5.1 <-> R8.2 <-> R9.2 <-> VR13.2 <-> VR2.2 <-> VR7.2 <-> VR8.2
BC548.3 <-> BC548.3 <-> BC548.3 <-> BC548.3 <-> C1.2 <-> C2.1 <-> C3.2 <-> C4.2 <-> IC1.5 <-> IC1.7 <-> IC1.9 <-> IC2.5 <-> IC5.5 <-> R11.2 <-> R14.2 <-> R18.1 <-> R22.2 <-> R23.2 <-> R24.2 <-> R26.2 <-> R27.2 <-> R28.2 <-> R3.2 <-> R30.2 <-> R37.2 <-> R39.2 <-> R40.2 <-> R5.1 <-> R8.2 <-> R9.2 <-> VR13.2 <-> VR2.2 <-> VR7.2 <-> VR8.2
BC558.1 <-> BC558.1 <-> IC2.11 <-> IC3.11 <-> IC4.11 <-> IC5.11 <-> IC6.11 <-> IC7.11 <-> R21.2 <-> R7.2
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R1.1
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R12.1
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R16.1
BC558.2 <-> R31.2 <-> R32.2 <-> R33.2 <-> R34.2 <-> R35.1
C10.1 <-> IC6.10 <-> IC6.3 <-> R37.1
C10.2 <-> IC6.13 <-> IC6.14
C5.1 <-> IC2.8 <-> IC2.9
C5.2 <-> IC2.12 <-> IC2.3 <-> R3.1
C10.1 <-> IC6.12 <-> IC6.3 <-> R37.1
C10.2 <-> IC6.8 <-> IC6.9
C5.1 <-> IC2.13 <-> IC2.14
C5.2 <-> IC2.10 <-> IC2.3 <-> R3.1
C6.1 <-> D5.K <-> D6.K <-> R7.1
C6.2 <-> R8.1 <-> Square/Triangle 1.3
C7.1 <-> IC5.8 <-> IC5.9
C7.2 <-> IC5.12 <-> IC5.3 <-> R24.1
C7.1 <-> IC5.13 <-> IC5.14
C7.2 <-> IC5.10 <-> IC5.3 <-> R24.1
C8.1 <-> D12.K <-> D7.K <-> R21.1
C8.2 <-> R22.1 <-> Square/Triangle 2.3
C9.1 <-> IC4.12 <-> IC4.5 <-> R14.1
C9.2 <-> IC4.8 <-> IC4.9
C9.1 <-> IC4.10 <-> IC4.5 <-> R14.1
C9.2 <-> IC4.13 <-> IC4.14
Coarse 1.2 <-> NTC.1
Coarse 2.2 <-> NTC.1
Coarse 3.2 <-> NTC.1
@@ -34,13 +34,13 @@ Switch setup: [ ALL ]
CV In 2.Tip <-> NTC.1
CV In 3.Tip <-> NTC.1
CV In 4.Tip <-> R33.1
D1.A <-> IC1.10
D12.A <-> IC5.14 <-> R19.2 <-> R20.1
D2.A <-> IC1.12
D1.A <-> IC1.12
D12.A <-> IC5.8 <-> R19.2 <-> R20.1
D2.A <-> IC1.10
D3.A <-> IC1.4
D4.A <-> IC1.2
D5.A <-> IC2.7 <-> Slope Shift 1.3
D6.A <-> IC2.14 <-> R4.2 <-> R6.1
D6.A <-> IC2.8 <-> R4.2 <-> R6.1
D7.A <-> IC5.7 <-> Slope Shift 2.3
Fine 1.2 <-> NTC.1
Fine 2.2 <-> NTC.1
@@ -50,38 +50,38 @@ Switch setup: [ ALL ]
FM In 2.Tip <-> VR4.2
FM In 3.Tip <-> VR6.2
FM In 4.Tip <-> VR10.2
IC1.8 <-> IC2.4 <-> IC3.4 <-> IC4.4 <-> IC5.4 <-> IC6.4 <-> IC7.4 <-> R13.2 <-> R17.2 <-> R2.2 <-> R36.2 <-> R42.2
IC1.14 <-> IC2.4 <-> IC3.4 <-> IC4.4 <-> IC5.4 <-> IC6.4 <-> IC7.4 <-> R13.2 <-> R17.2 <-> R2.2 <-> R36.2 <-> R42.2
IC2.1 <-> R10.1
IC2.13 <-> R4.1 <-> R5.2
IC2.2 <-> Pulsewidth 1.2 <-> R9.1 <-> VR3.1
IC2.6 <-> R6.2 <-> Slope Shift 1.2
IC2.9 <-> R4.1 <-> R5.2
IC3.1 <-> IC3.2 <-> Square/Triangle 2.1
IC3.10 <-> Square/Triangle 1.2
IC3.12 <-> R10.2 <-> R11.1
IC3.13 <-> IC3.14 <-> Square/Triangle 1.1
IC3.10 <-> R10.2 <-> R11.1
IC3.12 <-> Square/Triangle 1.2
IC3.13 <-> IC3.14 <-> Output 1.Tip
IC3.3 <-> R25.2 <-> R26.1
IC3.5 <-> Square/Triangle 2.2
IC3.6 <-> IC3.7 <-> Output 2.Tip
IC3.8 <-> IC3.9 <-> Output 1.Tip
IC3.8 <-> IC3.9 <-> Square/Triangle 1.1
IC4.1 <-> IC4.2 <-> Square/Sawtooth 3.1
IC4.13 <-> R15.1 <-> R27.1
IC4.14 <-> R15.2 <-> Square/Sawtooth 3.3
IC4.3 <-> R29.2 <-> R30.1
IC4.6 <-> Pulsewidth 3.2 <-> R28.1 <-> VR9.1
IC4.7 <-> R29.1
IC4.8 <-> R15.2 <-> Square/Sawtooth 3.3
IC4.9 <-> R15.1 <-> R27.1
IC5.1 <-> R25.1
IC5.13 <-> R18.2 <-> R19.1
IC5.2 <-> Pulsewidth 2.2 <-> R23.1 <-> VR5.1
IC5.6 <-> R20.2 <-> Slope Shift 2.2
IC5.9 <-> R18.2 <-> R19.1
IC6.1 <-> R41.1
IC6.13 <-> R38.1 <-> R39.1
IC6.14 <-> R38.2 <-> Square/Sawtooth 4.3
IC6.2 <-> Pulsewidth 4.2 <-> R40.1 <-> VR16.3
IC6.5 <-> IC7.12 <-> R41.2 <-> R42.1 <-> Square/Sawtooth 4.2
IC6.5 <-> IC7.10 <-> R41.2 <-> R42.1 <-> Square/Sawtooth 4.2
IC6.6 <-> IC6.7 <-> Square/Sawtooth 4.1
IC6.8 <-> R38.2 <-> Square/Sawtooth 4.3
IC6.9 <-> R38.1 <-> R39.1
IC7.13 <-> IC7.14 <-> Output 4.Tip
IC7.5 <-> Square/Sawtooth 3.2
IC7.6 <-> IC7.7 <-> Output 3.Tip
IC7.8 <-> IC7.9 <-> Output 4.Tip
NTC.1 <-> VR1.1
NTC.1 <-> VR4.1
PWM In 1.Tip <-> VR3.2
@@ -1,31 +1,31 @@
Switch setup: [ ALL ]
BC548.1 <-> C1.1 <-> D1.K <-> IC1.9 <-> IC2.10
BC548.1 <-> C2.2 <-> D2.K <-> IC1.11 <-> IC5.10
BC548.1 <-> C3.1 <-> D4.K <-> IC1.1 <-> IC4.10
BC548.1 <-> C4.1 <-> D3.K <-> IC1.3 <-> IC6.12
BC548.1 <-> C1.1 <-> D1.K <-> IC1.13 <-> IC2.12
BC548.1 <-> C2.2 <-> D2.K <-> IC1.11 <-> IC5.12
BC548.1 <-> C3.1 <-> D4.K <-> IC1.1 <-> IC4.12
BC548.1 <-> C4.1 <-> D3.K <-> IC1.3 <-> IC6.10
BC548.2 <-> BC558.3 <-> R13.1
BC548.2 <-> BC558.3 <-> R17.1
BC548.2 <-> BC558.3 <-> R2.1
BC548.2 <-> BC558.3 <-> R36.1
BC548.3 <-> BC548.3 <-> BC548.3 <-> BC548.3 <-> C1.2 <-> C2.1 <-> C3.2 <-> C4.2 <-> IC1.13 <-> IC1.5 <-> IC1.7 <-> IC2.5 <-> IC5.5 <-> R11.2 <-> R14.2 <-> R18.1 <-> R22.2 <-> R23.2 <-> R24.2 <-> R26.2 <-> R27.2 <-> R28.2 <-> R3.2 <-> R30.2 <-> R37.2 <-> R39.2 <-> R40.2 <-> R5.1 <-> R8.2 <-> R9.2 <-> VR13.2 <-> VR2.2 <-> VR7.2 <-> VR8.2
BC548.3 <-> BC548.3 <-> BC548.3 <-> BC548.3 <-> C1.2 <-> C2.1 <-> C3.2 <-> C4.2 <-> IC1.5 <-> IC1.7 <-> IC1.9 <-> IC2.5 <-> IC5.5 <-> R11.2 <-> R14.2 <-> R18.1 <-> R22.2 <-> R23.2 <-> R24.2 <-> R26.2 <-> R27.2 <-> R28.2 <-> R3.2 <-> R30.2 <-> R37.2 <-> R39.2 <-> R40.2 <-> R5.1 <-> R8.2 <-> R9.2 <-> VR13.2 <-> VR2.2 <-> VR7.2 <-> VR8.2
BC558.1 <-> BC558.1 <-> IC2.11 <-> IC3.11 <-> IC4.11 <-> IC5.11 <-> IC6.11 <-> IC7.11 <-> R21.2 <-> R7.2
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R1.1
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R12.1
BC558.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> NTC.2 <-> R16.1
BC558.2 <-> R31.2 <-> R32.2 <-> R33.2 <-> R34.2 <-> R35.1
C10.1 <-> IC6.10 <-> IC6.3 <-> R37.1
C10.2 <-> IC6.13 <-> IC6.14
C5.1 <-> IC2.8 <-> IC2.9
C5.2 <-> IC2.12 <-> IC2.3 <-> R3.1
C10.1 <-> IC6.12 <-> IC6.3 <-> R37.1
C10.2 <-> IC6.8 <-> IC6.9
C5.1 <-> IC2.13 <-> IC2.14
C5.2 <-> IC2.10 <-> IC2.3 <-> R3.1
C6.1 <-> D5.K <-> D6.K <-> R7.1
C6.2 <-> R8.1 <-> Square/Triangle 1.3
C7.1 <-> IC5.8 <-> IC5.9
C7.2 <-> IC5.12 <-> IC5.3 <-> R24.1
C7.1 <-> IC5.13 <-> IC5.14
C7.2 <-> IC5.10 <-> IC5.3 <-> R24.1
C8.1 <-> D12.K <-> D7.K <-> R21.1
C8.2 <-> R22.1 <-> Square/Triangle 2.3
C9.1 <-> IC4.12 <-> IC4.5 <-> R14.1
C9.2 <-> IC4.8 <-> IC4.9
C9.1 <-> IC4.10 <-> IC4.5 <-> R14.1
C9.2 <-> IC4.13 <-> IC4.14
Coarse 1.2 <-> NTC.1
Coarse 2.2 <-> NTC.1
Coarse 3.2 <-> NTC.1
@@ -34,13 +34,13 @@ Switch setup: [ ALL ]
CV In 2.Tip <-> NTC.1
CV In 3.Tip <-> NTC.1
CV In 4.Tip <-> R33.1
D1.A <-> IC1.10
D12.A <-> IC5.14 <-> R19.2 <-> R20.1
D2.A <-> IC1.12
D1.A <-> IC1.12
D12.A <-> IC5.8 <-> R19.2 <-> R20.1
D2.A <-> IC1.10
D3.A <-> IC1.4
D4.A <-> IC1.2
D5.A <-> IC2.7 <-> Slope Shift 1.3
D6.A <-> IC2.14 <-> R4.2 <-> R6.1
D6.A <-> IC2.8 <-> R4.2 <-> R6.1
D7.A <-> IC5.7 <-> Slope Shift 2.3
Fine 1.2 <-> NTC.1
Fine 2.2 <-> NTC.1
@@ -50,38 +50,38 @@ Switch setup: [ ALL ]
FM In 2.Tip <-> VR4.2
FM In 3.Tip <-> VR6.2
FM In 4.Tip <-> VR10.2
IC1.8 <-> IC2.4 <-> IC3.4 <-> IC4.4 <-> IC5.4 <-> IC6.4 <-> IC7.4 <-> R13.2 <-> R17.2 <-> R2.2 <-> R36.2 <-> R42.2
IC1.14 <-> IC2.4 <-> IC3.4 <-> IC4.4 <-> IC5.4 <-> IC6.4 <-> IC7.4 <-> R13.2 <-> R17.2 <-> R2.2 <-> R36.2 <-> R42.2
IC2.1 <-> R10.1
IC2.13 <-> R4.1 <-> R5.2
IC2.2 <-> Pulsewidth 1.2 <-> R9.1 <-> VR3.1
IC2.6 <-> R6.2 <-> Slope Shift 1.2
IC2.9 <-> R4.1 <-> R5.2
IC3.1 <-> IC3.2 <-> Square/Triangle 2.1
IC3.10 <-> Square/Triangle 1.2
IC3.12 <-> R10.2 <-> R11.1
IC3.13 <-> IC3.14 <-> Square/Triangle 1.1
IC3.10 <-> R10.2 <-> R11.1
IC3.12 <-> Square/Triangle 1.2
IC3.13 <-> IC3.14 <-> Output 1.Tip
IC3.3 <-> R25.2 <-> R26.1
IC3.5 <-> Square/Triangle 2.2
IC3.6 <-> IC3.7 <-> Output 2.Tip
IC3.8 <-> IC3.9 <-> Output 1.Tip
IC3.8 <-> IC3.9 <-> Square/Triangle 1.1
IC4.1 <-> IC4.2 <-> Square/Sawtooth 3.1
IC4.13 <-> R15.1 <-> R27.1
IC4.14 <-> R15.2 <-> Square/Sawtooth 3.3
IC4.3 <-> R29.2 <-> R30.1
IC4.6 <-> Pulsewidth 3.2 <-> R28.1 <-> VR9.1
IC4.7 <-> R29.1
IC4.8 <-> R15.2 <-> Square/Sawtooth 3.3
IC4.9 <-> R15.1 <-> R27.1
IC5.1 <-> R25.1
IC5.13 <-> R18.2 <-> R19.1
IC5.2 <-> Pulsewidth 2.2 <-> R23.1 <-> VR5.1
IC5.6 <-> R20.2 <-> Slope Shift 2.2
IC5.9 <-> R18.2 <-> R19.1
IC6.1 <-> R41.1
IC6.13 <-> R38.1 <-> R39.1
IC6.14 <-> R38.2 <-> Square/Sawtooth 4.3
IC6.2 <-> Pulsewidth 4.2 <-> R40.1 <-> VR16.3
IC6.5 <-> IC7.12 <-> R41.2 <-> R42.1 <-> Square/Sawtooth 4.2
IC6.5 <-> IC7.10 <-> R41.2 <-> R42.1 <-> Square/Sawtooth 4.2
IC6.6 <-> IC6.7 <-> Square/Sawtooth 4.1
IC6.8 <-> R38.2 <-> Square/Sawtooth 4.3
IC6.9 <-> R38.1 <-> R39.1
IC7.13 <-> IC7.14 <-> Output 4.Tip
IC7.5 <-> Square/Sawtooth 3.2
IC7.6 <-> IC7.7 <-> Output 3.Tip
IC7.8 <-> IC7.9 <-> Output 4.Tip
NTC.1 <-> VR1.1
NTC.1 <-> VR4.1
PWM In 1.Tip <-> VR3.2
@@ -4,17 +4,17 @@ Switch setup: [ ALL ]
1k.2 <-> 3PDT.5
3PDT.1 <-> C1.1
3PDT.3 <-> 3PDT.9
3PDT.4 <-> IC1.5 <-> IC2.5 <-> R2.1
3PDT.4 <-> IC1.8 <-> IC2.8 <-> R2.1
3PDT.7 <-> Volume.2
Blend.1 <-> C3.+ <-> C6.1
Blend.2 <-> C5.2
Blend.3 <-> C8.+
C1.2 <-> IC1.3 <-> IC2.3 <-> R1.1
C2.+ <-> IC2.4 <-> LED.K <-> R3.1 <-> R4.2 <-> Volume.1
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.8 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.5 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C4.+ <-> R4.1
C4.- <-> IC2.1 <-> IC2.2
C6.2 <-> IC2.8 <-> R6.1
C6.2 <-> IC2.5 <-> R6.1
C7.+ <-> Volume.3
C7.- <-> IC1.6 <-> IC1.7
C8.- <-> IC2.6 <-> IC2.7
@@ -1,16 +1,16 @@
Switch setup: [ 3PDT.ON1 ]
1k.1 <-> LED.A
1k.2 <-> IC1.5 <-> IC2.5 <-> R2.1
1k.2 <-> IC1.8 <-> IC2.8 <-> R2.1
Blend.1 <-> C3.+ <-> C6.1
Blend.2 <-> C5.2
Blend.3 <-> C8.+
C1.2 <-> IC1.3 <-> IC2.3 <-> R1.1
C2.+ <-> IC2.4 <-> LED.K <-> R3.1 <-> R4.2 <-> Volume.1
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.8 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.5 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C4.+ <-> R4.1
C4.- <-> IC2.1 <-> IC2.2
C6.2 <-> IC2.8 <-> R6.1
C6.2 <-> IC2.5 <-> R6.1
C7.+ <-> Volume.3
C7.- <-> IC1.6 <-> IC1.7
C8.- <-> IC2.6 <-> IC2.7
@@ -23,12 +23,12 @@ Switch setup: [ 3PDT.ON2 ]
Blend.3 <-> C8.+
C1.2 <-> IC1.3 <-> IC2.3 <-> R1.1
C2.+ <-> IC2.4 <-> LED.K <-> R3.1 <-> R4.2 <-> Volume.1
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.8 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C2.- <-> C3.- <-> C5.1 <-> IC1.1 <-> IC1.2 <-> IC1.5 <-> R1.2 <-> R2.2 <-> R3.2 <-> R5.1 <-> R5.2 <-> R6.2
C4.+ <-> R4.1
C4.- <-> IC2.1 <-> IC2.2
C6.2 <-> IC2.8 <-> R6.1
C6.2 <-> IC2.5 <-> R6.1
C7.+ <-> Volume.3
C7.- <-> IC1.6 <-> IC1.7
C8.- <-> IC2.6 <-> IC2.7
IC1.5 <-> IC2.5 <-> R2.1
IC1.8 <-> IC2.8 <-> R2.1
@@ -7,28 +7,28 @@ Switch setup: [ ALL ]
C11.2 <-> C14.- <-> C5.1 <-> C8.- <-> C9.- <-> D2.A <-> D3.K <-> IC1.4 <-> IC2.4 <-> IC3.4 <-> R15.2 <-> R19.2 <-> R2.2 <-> R21.2
C12.2 <-> R11.1
C13.1 <-> C14.+ <-> C15.- <-> IC1.3 <-> IC3.3 <-> R12.2 <-> R19.1 <-> R20.1 <-> R3.1
C13.2 <-> D1.K <-> IC3.8 <-> R12.1
C15.+ <-> IC1.6 <-> IC2.6 <-> IC3.5 <-> R13.2 <-> R20.2
C13.2 <-> D1.K <-> IC3.5 <-> R12.1
C15.+ <-> IC1.7 <-> IC2.7 <-> IC3.8 <-> R13.2 <-> R20.2
C16.+ <-> IC3.1 <-> LEVEL.2
C16.- <-> R21.1
C2.1 <-> IC1.1
C2.2 <-> IC1.5
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.7
C2.2 <-> IC1.8
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.6
C3.- <-> C4.1 <-> R2.1
C4.2 <-> R3.2 <-> R4.2
C5.2 <-> IC2.3 <-> R4.1
C6.1 <-> IC2.2 <-> LDR.1 <-> R5.2 <-> R6.2 <-> spst1.1 <-> spst2.1
C6.2 <-> IC2.7 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C6.2 <-> IC2.6 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C7.1 <-> IC2.1
C7.2 <-> IC2.5
C7.2 <-> IC2.8
C8.+ <-> R5.1
C9.+ <-> R6.1
D4.A <-> R14.1
D4.K <-> R13.1
GAIN.1 <-> IC1.2 <-> R1.2
IC3.2 <-> LEVEL.1 <-> R11.2
IC3.6 <-> R17.2
IC3.7 <-> R15.1 <-> R18.2
IC3.6 <-> R15.1 <-> R18.2
IC3.7 <-> R17.2
Q1.1 <-> Q1.2 <-> R14.2 <-> R16.2 <-> R17.1 <-> R18.1
Q1.3 <-> R16.1
R10.1 <-> TONE.2
@@ -7,28 +7,28 @@ Switch setup: [ spst1.ON1:spst2.ON1 ]
C11.2 <-> C14.- <-> C5.1 <-> C8.- <-> C9.- <-> D2.A <-> D3.K <-> IC1.4 <-> IC2.4 <-> IC3.4 <-> R15.2 <-> R19.2 <-> R2.2 <-> R21.2
C12.2 <-> R11.1
C13.1 <-> C14.+ <-> C15.- <-> IC1.3 <-> IC3.3 <-> R12.2 <-> R19.1 <-> R20.1 <-> R3.1
C13.2 <-> D1.K <-> IC3.8 <-> R12.1
C15.+ <-> IC1.6 <-> IC2.6 <-> IC3.5 <-> R13.2 <-> R20.2
C13.2 <-> D1.K <-> IC3.5 <-> R12.1
C15.+ <-> IC1.7 <-> IC2.7 <-> IC3.8 <-> R13.2 <-> R20.2
C16.+ <-> IC3.1 <-> LEVEL.2
C16.- <-> R21.1
C2.1 <-> IC1.1
C2.2 <-> IC1.5
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.7
C2.2 <-> IC1.8
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.6
C3.- <-> C4.1 <-> R2.1
C4.2 <-> R3.2 <-> R4.2
C5.2 <-> IC2.3 <-> R4.1
C6.1 <-> IC2.2 <-> LDR.1 <-> R5.2 <-> R6.2 <-> R7.2 <-> R8.2
C6.2 <-> IC2.7 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C6.2 <-> IC2.6 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C7.1 <-> IC2.1
C7.2 <-> IC2.5
C7.2 <-> IC2.8
C8.+ <-> R5.1
C9.+ <-> R6.1
D4.A <-> R14.1
D4.K <-> R13.1
GAIN.1 <-> IC1.2 <-> R1.2
IC3.2 <-> LEVEL.1 <-> R11.2
IC3.6 <-> R17.2
IC3.7 <-> R15.1 <-> R18.2
IC3.6 <-> R15.1 <-> R18.2
IC3.7 <-> R17.2
Q1.1 <-> Q1.2 <-> R14.2 <-> R16.2 <-> R17.1 <-> R18.1
Q1.3 <-> R16.1
R10.1 <-> TONE.2
@@ -42,28 +42,28 @@ Switch setup: [ spst1.ON1:spst2.ON2 ]
C11.2 <-> C14.- <-> C5.1 <-> C8.- <-> C9.- <-> D2.A <-> D3.K <-> IC1.4 <-> IC2.4 <-> IC3.4 <-> R15.2 <-> R19.2 <-> R2.2 <-> R21.2
C12.2 <-> R11.1
C13.1 <-> C14.+ <-> C15.- <-> IC1.3 <-> IC3.3 <-> R12.2 <-> R19.1 <-> R20.1 <-> R3.1
C13.2 <-> D1.K <-> IC3.8 <-> R12.1
C15.+ <-> IC1.6 <-> IC2.6 <-> IC3.5 <-> R13.2 <-> R20.2
C13.2 <-> D1.K <-> IC3.5 <-> R12.1
C15.+ <-> IC1.7 <-> IC2.7 <-> IC3.8 <-> R13.2 <-> R20.2
C16.+ <-> IC3.1 <-> LEVEL.2
C16.- <-> R21.1
C2.1 <-> IC1.1
C2.2 <-> IC1.5
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.7
C2.2 <-> IC1.8
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.6
C3.- <-> C4.1 <-> R2.1
C4.2 <-> R3.2 <-> R4.2
C5.2 <-> IC2.3 <-> R4.1
C6.1 <-> IC2.2 <-> LDR.1 <-> R5.2 <-> R6.2 <-> R7.2
C6.2 <-> IC2.7 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C6.2 <-> IC2.6 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C7.1 <-> IC2.1
C7.2 <-> IC2.5
C7.2 <-> IC2.8
C8.+ <-> R5.1
C9.+ <-> R6.1
D4.A <-> R14.1
D4.K <-> R13.1
GAIN.1 <-> IC1.2 <-> R1.2
IC3.2 <-> LEVEL.1 <-> R11.2
IC3.6 <-> R17.2
IC3.7 <-> R15.1 <-> R18.2
IC3.6 <-> R15.1 <-> R18.2
IC3.7 <-> R17.2
Q1.1 <-> Q1.2 <-> R14.2 <-> R16.2 <-> R17.1 <-> R18.1
Q1.3 <-> R16.1
R10.1 <-> TONE.2
@@ -77,28 +77,28 @@ Switch setup: [ spst1.ON2:spst2.ON1 ]
C11.2 <-> C14.- <-> C5.1 <-> C8.- <-> C9.- <-> D2.A <-> D3.K <-> IC1.4 <-> IC2.4 <-> IC3.4 <-> R15.2 <-> R19.2 <-> R2.2 <-> R21.2
C12.2 <-> R11.1
C13.1 <-> C14.+ <-> C15.- <-> IC1.3 <-> IC3.3 <-> R12.2 <-> R19.1 <-> R20.1 <-> R3.1
C13.2 <-> D1.K <-> IC3.8 <-> R12.1
C15.+ <-> IC1.6 <-> IC2.6 <-> IC3.5 <-> R13.2 <-> R20.2
C13.2 <-> D1.K <-> IC3.5 <-> R12.1
C15.+ <-> IC1.7 <-> IC2.7 <-> IC3.8 <-> R13.2 <-> R20.2
C16.+ <-> IC3.1 <-> LEVEL.2
C16.- <-> R21.1
C2.1 <-> IC1.1
C2.2 <-> IC1.5
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.7
C2.2 <-> IC1.8
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.6
C3.- <-> C4.1 <-> R2.1
C4.2 <-> R3.2 <-> R4.2
C5.2 <-> IC2.3 <-> R4.1
C6.1 <-> IC2.2 <-> LDR.1 <-> R5.2 <-> R6.2 <-> R8.2
C6.2 <-> IC2.7 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C6.2 <-> IC2.6 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C7.1 <-> IC2.1
C7.2 <-> IC2.5
C7.2 <-> IC2.8
C8.+ <-> R5.1
C9.+ <-> R6.1
D4.A <-> R14.1
D4.K <-> R13.1
GAIN.1 <-> IC1.2 <-> R1.2
IC3.2 <-> LEVEL.1 <-> R11.2
IC3.6 <-> R17.2
IC3.7 <-> R15.1 <-> R18.2
IC3.6 <-> R15.1 <-> R18.2
IC3.7 <-> R17.2
Q1.1 <-> Q1.2 <-> R14.2 <-> R16.2 <-> R17.1 <-> R18.1
Q1.3 <-> R16.1
R10.1 <-> TONE.2
@@ -112,28 +112,28 @@ Switch setup: [ spst1.ON2:spst2.ON2 ]
C11.2 <-> C14.- <-> C5.1 <-> C8.- <-> C9.- <-> D2.A <-> D3.K <-> IC1.4 <-> IC2.4 <-> IC3.4 <-> R15.2 <-> R19.2 <-> R2.2 <-> R21.2
C12.2 <-> R11.1
C13.1 <-> C14.+ <-> C15.- <-> IC1.3 <-> IC3.3 <-> R12.2 <-> R19.1 <-> R20.1 <-> R3.1
C13.2 <-> D1.K <-> IC3.8 <-> R12.1
C15.+ <-> IC1.6 <-> IC2.6 <-> IC3.5 <-> R13.2 <-> R20.2
C13.2 <-> D1.K <-> IC3.5 <-> R12.1
C15.+ <-> IC1.7 <-> IC2.7 <-> IC3.8 <-> R13.2 <-> R20.2
C16.+ <-> IC3.1 <-> LEVEL.2
C16.- <-> R21.1
C2.1 <-> IC1.1
C2.2 <-> IC1.5
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.7
C2.2 <-> IC1.8
C3.+ <-> D1.A <-> GAIN.2 <-> IC1.6
C3.- <-> C4.1 <-> R2.1
C4.2 <-> R3.2 <-> R4.2
C5.2 <-> IC2.3 <-> R4.1
C6.1 <-> IC2.2 <-> LDR.1 <-> R5.2 <-> R6.2
C6.2 <-> IC2.7 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C6.2 <-> IC2.6 <-> LDR.2 <-> R7.1 <-> R8.1 <-> R9.1
C7.1 <-> IC2.1
C7.2 <-> IC2.5
C7.2 <-> IC2.8
C8.+ <-> R5.1
C9.+ <-> R6.1
D4.A <-> R14.1
D4.K <-> R13.1
GAIN.1 <-> IC1.2 <-> R1.2
IC3.2 <-> LEVEL.1 <-> R11.2
IC3.6 <-> R17.2
IC3.7 <-> R15.1 <-> R18.2
IC3.6 <-> R15.1 <-> R18.2
IC3.7 <-> R17.2
Q1.1 <-> Q1.2 <-> R14.2 <-> R16.2 <-> R17.1 <-> R18.1
Q1.3 <-> R16.1
R10.1 <-> TONE.2
@@ -13,10 +13,10 @@ Switch setup: [ ALL ]
C14.2 <-> R21.1
C15.2 <-> D3.K <-> Q3.2 <-> Q3.3 <-> R18.2
C16.+ <-> Q5.3 <-> R20.1
C3.1 <-> IC1.7 <-> Q2.3 <-> R3.2
C3.1 <-> IC1.6 <-> Q2.3 <-> R3.2
C3.2 <-> C4.+ <-> R2.1 <-> R3.1 <-> R4.1 <-> R5.1
C5.+ <-> C6.1 <-> D1.K <-> IC1.6 <-> R10.1 <-> R16.2 <-> R20.2 <-> R4.2
IC1.8 <-> R19.1
C5.+ <-> C6.1 <-> D1.K <-> IC1.7 <-> R10.1 <-> R16.2 <-> R20.2 <-> R4.2
IC1.5 <-> R19.1
Q1.3 <-> R5.2 <-> R6.2
Q5.1 <-> Q5.2
R10.2 <-> VR1.2
@@ -13,10 +13,10 @@ Switch setup: [ ALL ]
C14.2 <-> R21.1
C15.2 <-> D3.K <-> Q3.2 <-> Q3.3 <-> R18.2
C16.+ <-> Q5.3 <-> R20.1
C3.1 <-> IC1.7 <-> Q2.3 <-> R3.2
C3.1 <-> IC1.6 <-> Q2.3 <-> R3.2
C3.2 <-> C4.+ <-> R2.1 <-> R3.1 <-> R4.1 <-> R5.1
C5.+ <-> C6.1 <-> D1.K <-> IC1.6 <-> R10.1 <-> R16.2 <-> R20.2 <-> R4.2
IC1.8 <-> R19.1
C5.+ <-> C6.1 <-> D1.K <-> IC1.7 <-> R10.1 <-> R16.2 <-> R20.2 <-> R4.2
IC1.5 <-> R19.1
Q1.3 <-> R5.2 <-> R6.2
Q5.1 <-> Q5.2
R10.2 <-> VR1.2
@@ -2,42 +2,42 @@ Switch setup: [ ALL ]
Bass Switch.1 <-> C8.1 <-> R13.2 <-> R14.1
Bass Switch.2 <-> Blend.3
Bass Switch.3 <-> C10.2 <-> C11.2 <-> IC1.14 <-> R15.1 <-> R16.2
Bass Switch.3 <-> C10.2 <-> C11.2 <-> IC1.8 <-> R15.1 <-> R16.2
Blend.1 <-> C5.2 <-> C6.2 <-> Q1.2 <-> Q1.3 <-> R12.1
Blend.2 <-> C17.1
C1.2 <-> IC1.3 <-> IC1.5 <-> R1.1
C10.1 <-> Lo Filter.2 <-> R14.2
C11.1 <-> IC1.13 <-> R16.1 <-> R17.1
C12.1 <-> IC1.12 <-> Lo Filter.1
C11.1 <-> IC1.9 <-> R16.1 <-> R17.1
C12.1 <-> IC1.10 <-> Lo Filter.1
C12.2 <-> C13.1 <-> C9.2 <-> D1.K <-> Q1.1 <-> R11.2 <-> R12.2 <-> R17.2
C13.2 <-> D1.A <-> R20.1 <-> R21.1
C14.1 <-> D3.K <-> IC3.2 <-> IC3.6 <-> R24.1
C14.2 <-> C15.1 <-> R23.1
C15.2 <-> D2.A <-> IC3.10 <-> IC3.13 <-> R24.2
C16.1 <-> IC3.12
C15.2 <-> D2.A <-> IC3.12 <-> IC3.9 <-> R24.2
C16.1 <-> IC3.10
C16.2 <-> C18.+ <-> C19.- <-> C2.2 <-> Hi Filter.1 <-> IC3.11 <-> R1.2 <-> R22.1 <-> R23.2 <-> R26.1 <-> R27.1 <-> R5.2
C17.2 <-> R25.1
C18.- <-> C20.+ <-> IC1.11 <-> IC2.12 <-> IC2.14 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C18.- <-> C20.+ <-> IC1.11 <-> IC2.10 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> IC2.8 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C19.+ <-> C20.- <-> R27.2
C2.1 <-> R4.2
C3.1 <-> IC1.6 <-> R2.2 <-> R4.1
C3.2 <-> C4.1 <-> R5.1
C4.2 <-> R6.1 <-> Range.2
C5.1 <-> IC1.1 <-> IC1.2 <-> R3.1 <-> R9.2
C6.1 <-> IC1.10
C6.1 <-> IC1.12
C7.1 <-> R10.2 <-> R13.1
C7.2 <-> C8.2 <-> C9.1 <-> Hi Filter.2
D2.K <-> IC3.14
D2.K <-> IC3.8
D3.A <-> IC3.7
IC1.4 <-> IC2.8 <-> IC3.4
IC1.13 <-> R11.1 <-> R7.2 <-> R9.1
IC1.14 <-> R10.1 <-> R7.1 <-> R8.1
IC1.4 <-> IC2.14 <-> IC3.4
IC1.7 <-> R18.1 <-> R19.1 <-> R2.1 <-> R6.2
IC1.8 <-> R10.1 <-> R7.1 <-> R8.1
IC1.9 <-> R11.1 <-> R7.2 <-> R9.1
IC2.10 <-> IC2.13 <-> R20.2
IC2.11 <-> IC2.2
IC2.12 <-> IC2.9 <-> R20.2
IC2.4 <-> IC3.1
IC2.6 <-> IC3.8
IC3.3 <-> IC3.9 <-> R19.2 <-> R22.2
IC2.6 <-> IC3.14
IC3.13 <-> IC3.3 <-> R19.2 <-> R22.2
IC3.5 <-> R18.2
R28.2 <-> Range.1
@@ -5,38 +5,38 @@ Switch setup: [ Bass Switch.ON1:Range.ON1 ]
Blend.3 <-> C8.1 <-> R13.2 <-> R14.1
C1.2 <-> IC1.3 <-> IC1.5 <-> R1.1
C10.1 <-> Lo Filter.2 <-> R14.2
C10.2 <-> C11.2 <-> IC1.14 <-> R15.1 <-> R16.2
C11.1 <-> IC1.13 <-> R16.1 <-> R17.1
C12.1 <-> IC1.12 <-> Lo Filter.1
C10.2 <-> C11.2 <-> IC1.8 <-> R15.1 <-> R16.2
C11.1 <-> IC1.9 <-> R16.1 <-> R17.1
C12.1 <-> IC1.10 <-> Lo Filter.1
C12.2 <-> C13.1 <-> C9.2 <-> D1.K <-> Q1.1 <-> R11.2 <-> R12.2 <-> R17.2
C13.2 <-> D1.A <-> R20.1 <-> R21.1
C14.1 <-> D3.K <-> IC3.2 <-> IC3.6 <-> R24.1
C14.2 <-> C15.1 <-> R23.1
C15.2 <-> D2.A <-> IC3.10 <-> IC3.13 <-> R24.2
C16.1 <-> IC3.12
C15.2 <-> D2.A <-> IC3.12 <-> IC3.9 <-> R24.2
C16.1 <-> IC3.10
C16.2 <-> C18.+ <-> C19.- <-> C2.2 <-> Hi Filter.1 <-> IC3.11 <-> R1.2 <-> R22.1 <-> R23.2 <-> R26.1 <-> R27.1 <-> R5.2
C17.2 <-> R25.1
C18.- <-> C20.+ <-> IC1.11 <-> IC2.12 <-> IC2.14 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C18.- <-> C20.+ <-> IC1.11 <-> IC2.10 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> IC2.8 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C19.+ <-> C20.- <-> R27.2
C2.1 <-> R4.2
C3.1 <-> IC1.6 <-> R2.2 <-> R4.1
C3.2 <-> C4.1 <-> R5.1
C4.2 <-> R28.2 <-> R6.1
C5.1 <-> IC1.1 <-> IC1.2 <-> R3.1 <-> R9.2
C6.1 <-> IC1.10
C6.1 <-> IC1.12
C7.1 <-> R10.2 <-> R13.1
C7.2 <-> C8.2 <-> C9.1 <-> Hi Filter.2
D2.K <-> IC3.14
D2.K <-> IC3.8
D3.A <-> IC3.7
IC1.4 <-> IC2.8 <-> IC3.4
IC1.13 <-> R11.1 <-> R7.2 <-> R9.1
IC1.14 <-> R10.1 <-> R7.1 <-> R8.1
IC1.4 <-> IC2.14 <-> IC3.4
IC1.7 <-> R18.1 <-> R19.1 <-> R2.1 <-> R6.2
IC1.8 <-> R10.1 <-> R7.1 <-> R8.1
IC1.9 <-> R11.1 <-> R7.2 <-> R9.1
IC2.10 <-> IC2.13 <-> R20.2
IC2.11 <-> IC2.2
IC2.12 <-> IC2.9 <-> R20.2
IC2.4 <-> IC3.1
IC2.6 <-> IC3.8
IC3.3 <-> IC3.9 <-> R19.2 <-> R22.2
IC2.6 <-> IC3.14
IC3.13 <-> IC3.3 <-> R19.2 <-> R22.2
IC3.5 <-> R18.2
Switch setup: [ Bass Switch.ON1:Range.ON2 ]
@@ -46,119 +46,119 @@ Switch setup: [ Bass Switch.ON1:Range.ON2 ]
Blend.3 <-> C8.1 <-> R13.2 <-> R14.1
C1.2 <-> IC1.3 <-> IC1.5 <-> R1.1
C10.1 <-> Lo Filter.2 <-> R14.2
C10.2 <-> C11.2 <-> IC1.14 <-> R15.1 <-> R16.2
C11.1 <-> IC1.13 <-> R16.1 <-> R17.1
C12.1 <-> IC1.12 <-> Lo Filter.1
C10.2 <-> C11.2 <-> IC1.8 <-> R15.1 <-> R16.2
C11.1 <-> IC1.9 <-> R16.1 <-> R17.1
C12.1 <-> IC1.10 <-> Lo Filter.1
C12.2 <-> C13.1 <-> C9.2 <-> D1.K <-> Q1.1 <-> R11.2 <-> R12.2 <-> R17.2
C13.2 <-> D1.A <-> R20.1 <-> R21.1
C14.1 <-> D3.K <-> IC3.2 <-> IC3.6 <-> R24.1
C14.2 <-> C15.1 <-> R23.1
C15.2 <-> D2.A <-> IC3.10 <-> IC3.13 <-> R24.2
C16.1 <-> IC3.12
C15.2 <-> D2.A <-> IC3.12 <-> IC3.9 <-> R24.2
C16.1 <-> IC3.10
C16.2 <-> C18.+ <-> C19.- <-> C2.2 <-> Hi Filter.1 <-> IC3.11 <-> R1.2 <-> R22.1 <-> R23.2 <-> R26.1 <-> R27.1 <-> R5.2
C17.2 <-> R25.1
C18.- <-> C20.+ <-> IC1.11 <-> IC2.12 <-> IC2.14 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C18.- <-> C20.+ <-> IC1.11 <-> IC2.10 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> IC2.8 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C19.+ <-> C20.- <-> R27.2
C2.1 <-> R4.2
C3.1 <-> IC1.6 <-> R2.2 <-> R4.1
C3.2 <-> C4.1 <-> R5.1
C4.2 <-> R6.1
C5.1 <-> IC1.1 <-> IC1.2 <-> R3.1 <-> R9.2
C6.1 <-> IC1.10
C6.1 <-> IC1.12
C7.1 <-> R10.2 <-> R13.1
C7.2 <-> C8.2 <-> C9.1 <-> Hi Filter.2
D2.K <-> IC3.14
D2.K <-> IC3.8
D3.A <-> IC3.7
IC1.4 <-> IC2.8 <-> IC3.4
IC1.13 <-> R11.1 <-> R7.2 <-> R9.1
IC1.14 <-> R10.1 <-> R7.1 <-> R8.1
IC1.4 <-> IC2.14 <-> IC3.4
IC1.7 <-> R18.1 <-> R19.1 <-> R2.1 <-> R6.2
IC1.8 <-> R10.1 <-> R7.1 <-> R8.1
IC1.9 <-> R11.1 <-> R7.2 <-> R9.1
IC2.10 <-> IC2.13 <-> R20.2
IC2.11 <-> IC2.2
IC2.12 <-> IC2.9 <-> R20.2
IC2.4 <-> IC3.1
IC2.6 <-> IC3.8
IC3.3 <-> IC3.9 <-> R19.2 <-> R22.2
IC2.6 <-> IC3.14
IC3.13 <-> IC3.3 <-> R19.2 <-> R22.2
IC3.5 <-> R18.2
Switch setup: [ Bass Switch.ON2:Range.ON1 ]
Blend.1 <-> C5.2 <-> C6.2 <-> Q1.2 <-> Q1.3 <-> R12.1
Blend.2 <-> C17.1
Blend.3 <-> C10.2 <-> C11.2 <-> IC1.14 <-> R15.1 <-> R16.2
Blend.3 <-> C10.2 <-> C11.2 <-> IC1.8 <-> R15.1 <-> R16.2
C1.2 <-> IC1.3 <-> IC1.5 <-> R1.1
C10.1 <-> Lo Filter.2 <-> R14.2
C11.1 <-> IC1.13 <-> R16.1 <-> R17.1
C12.1 <-> IC1.12 <-> Lo Filter.1
C11.1 <-> IC1.9 <-> R16.1 <-> R17.1
C12.1 <-> IC1.10 <-> Lo Filter.1
C12.2 <-> C13.1 <-> C9.2 <-> D1.K <-> Q1.1 <-> R11.2 <-> R12.2 <-> R17.2
C13.2 <-> D1.A <-> R20.1 <-> R21.1
C14.1 <-> D3.K <-> IC3.2 <-> IC3.6 <-> R24.1
C14.2 <-> C15.1 <-> R23.1
C15.2 <-> D2.A <-> IC3.10 <-> IC3.13 <-> R24.2
C16.1 <-> IC3.12
C15.2 <-> D2.A <-> IC3.12 <-> IC3.9 <-> R24.2
C16.1 <-> IC3.10
C16.2 <-> C18.+ <-> C19.- <-> C2.2 <-> Hi Filter.1 <-> IC3.11 <-> R1.2 <-> R22.1 <-> R23.2 <-> R26.1 <-> R27.1 <-> R5.2
C17.2 <-> R25.1
C18.- <-> C20.+ <-> IC1.11 <-> IC2.12 <-> IC2.14 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C18.- <-> C20.+ <-> IC1.11 <-> IC2.10 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> IC2.8 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C19.+ <-> C20.- <-> R27.2
C2.1 <-> R4.2
C3.1 <-> IC1.6 <-> R2.2 <-> R4.1
C3.2 <-> C4.1 <-> R5.1
C4.2 <-> R28.2 <-> R6.1
C5.1 <-> IC1.1 <-> IC1.2 <-> R3.1 <-> R9.2
C6.1 <-> IC1.10
C6.1 <-> IC1.12
C7.1 <-> R10.2 <-> R13.1
C7.2 <-> C8.2 <-> C9.1 <-> Hi Filter.2
C8.1 <-> R13.2 <-> R14.1
D2.K <-> IC3.14
D2.K <-> IC3.8
D3.A <-> IC3.7
IC1.4 <-> IC2.8 <-> IC3.4
IC1.13 <-> R11.1 <-> R7.2 <-> R9.1
IC1.14 <-> R10.1 <-> R7.1 <-> R8.1
IC1.4 <-> IC2.14 <-> IC3.4
IC1.7 <-> R18.1 <-> R19.1 <-> R2.1 <-> R6.2
IC1.8 <-> R10.1 <-> R7.1 <-> R8.1
IC1.9 <-> R11.1 <-> R7.2 <-> R9.1
IC2.10 <-> IC2.13 <-> R20.2
IC2.11 <-> IC2.2
IC2.12 <-> IC2.9 <-> R20.2
IC2.4 <-> IC3.1
IC2.6 <-> IC3.8
IC3.3 <-> IC3.9 <-> R19.2 <-> R22.2
IC2.6 <-> IC3.14
IC3.13 <-> IC3.3 <-> R19.2 <-> R22.2
IC3.5 <-> R18.2
Switch setup: [ Bass Switch.ON2:Range.ON2 ]
Blend.1 <-> C5.2 <-> C6.2 <-> Q1.2 <-> Q1.3 <-> R12.1
Blend.2 <-> C17.1
Blend.3 <-> C10.2 <-> C11.2 <-> IC1.14 <-> R15.1 <-> R16.2
Blend.3 <-> C10.2 <-> C11.2 <-> IC1.8 <-> R15.1 <-> R16.2
C1.2 <-> IC1.3 <-> IC1.5 <-> R1.1
C10.1 <-> Lo Filter.2 <-> R14.2
C11.1 <-> IC1.13 <-> R16.1 <-> R17.1
C12.1 <-> IC1.12 <-> Lo Filter.1
C11.1 <-> IC1.9 <-> R16.1 <-> R17.1
C12.1 <-> IC1.10 <-> Lo Filter.1
C12.2 <-> C13.1 <-> C9.2 <-> D1.K <-> Q1.1 <-> R11.2 <-> R12.2 <-> R17.2
C13.2 <-> D1.A <-> R20.1 <-> R21.1
C14.1 <-> D3.K <-> IC3.2 <-> IC3.6 <-> R24.1
C14.2 <-> C15.1 <-> R23.1
C15.2 <-> D2.A <-> IC3.10 <-> IC3.13 <-> R24.2
C16.1 <-> IC3.12
C15.2 <-> D2.A <-> IC3.12 <-> IC3.9 <-> R24.2
C16.1 <-> IC3.10
C16.2 <-> C18.+ <-> C19.- <-> C2.2 <-> Hi Filter.1 <-> IC3.11 <-> R1.2 <-> R22.1 <-> R23.2 <-> R26.1 <-> R27.1 <-> R5.2
C17.2 <-> R25.1
C18.- <-> C20.+ <-> IC1.11 <-> IC2.12 <-> IC2.14 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C18.- <-> C20.+ <-> IC1.11 <-> IC2.10 <-> IC2.3 <-> IC2.5 <-> IC2.7 <-> IC2.8 <-> R15.2 <-> R21.2 <-> R25.2 <-> R26.2 <-> R28.1 <-> R3.2 <-> R8.2
C19.+ <-> C20.- <-> R27.2
C2.1 <-> R4.2
C3.1 <-> IC1.6 <-> R2.2 <-> R4.1
C3.2 <-> C4.1 <-> R5.1
C4.2 <-> R6.1
C5.1 <-> IC1.1 <-> IC1.2 <-> R3.1 <-> R9.2
C6.1 <-> IC1.10
C6.1 <-> IC1.12
C7.1 <-> R10.2 <-> R13.1
C7.2 <-> C8.2 <-> C9.1 <-> Hi Filter.2
C8.1 <-> R13.2 <-> R14.1
D2.K <-> IC3.14
D2.K <-> IC3.8
D3.A <-> IC3.7
IC1.4 <-> IC2.8 <-> IC3.4
IC1.13 <-> R11.1 <-> R7.2 <-> R9.1
IC1.14 <-> R10.1 <-> R7.1 <-> R8.1
IC1.4 <-> IC2.14 <-> IC3.4
IC1.7 <-> R18.1 <-> R19.1 <-> R2.1 <-> R6.2
IC1.8 <-> R10.1 <-> R7.1 <-> R8.1
IC1.9 <-> R11.1 <-> R7.2 <-> R9.1
IC2.10 <-> IC2.13 <-> R20.2
IC2.11 <-> IC2.2
IC2.12 <-> IC2.9 <-> R20.2
IC2.4 <-> IC3.1
IC2.6 <-> IC3.8
IC3.3 <-> IC3.9 <-> R19.2 <-> R22.2
IC2.6 <-> IC3.14
IC3.13 <-> IC3.3 <-> R19.2 <-> R22.2
IC3.5 <-> R18.2
@@ -2,14 +2,14 @@ Switch setup: [ ALL ]
C1.+ <-> Q1.3
C1.- <-> C4.2 <-> R2.2 <-> R3.2 <-> VR2.1
C2.+ <-> IC1.7 <-> VR1.3
C2.+ <-> IC1.6 <-> VR1.3
C2.- <-> IC1.3 <-> IC1.4
C3.1 <-> Q1.1 <-> VR1.1 <-> VR1.2
C3.2 <-> VR2.3
C4.1 <-> IC1.6
C5.+ <-> IC1.8
C4.1 <-> IC1.7
C5.+ <-> IC1.5
IC1.1 <-> VR3.1 <-> VR3.2
IC1.2 <-> VR2.2
IC1.5 <-> VR3.3
IC1.8 <-> VR3.3
R1.2 <-> R3.1
@@ -2,14 +2,14 @@ Switch setup: [ ALL ]
C1.+ <-> Q1.3
C1.- <-> C4.2 <-> R2.2 <-> R3.2 <-> VR2.1
C2.+ <-> IC1.7 <-> VR1.3
C2.+ <-> IC1.6 <-> VR1.3
C2.- <-> IC1.3 <-> IC1.4
C3.1 <-> Q1.1 <-> VR1.1 <-> VR1.2
C3.2 <-> VR2.3
C4.1 <-> IC1.6
C5.+ <-> IC1.8
C4.1 <-> IC1.7
C5.+ <-> IC1.5
IC1.1 <-> VR3.1 <-> VR3.2
IC1.2 <-> VR2.2
IC1.5 <-> VR3.3
IC1.8 <-> VR3.3
R1.2 <-> R3.1
@@ -4,39 +4,39 @@ Switch setup: [ ALL ]
C10.1 <-> C9.1 <-> R22.1
C10.2 <-> IC3.3 <-> R23.1
C11.1 <-> C12.1 <-> R27.2
C11.2 <-> IC3.9 <-> R28.2
C12.2 <-> IC3.8 <-> R28.1 <-> R31.1
C11.2 <-> IC3.13 <-> R28.2
C12.2 <-> IC3.14 <-> R28.1 <-> R31.1
C2.1 <-> IC1.7 <-> R5.1
C2.2 <-> R6.1
C3.1 <-> IC1.13 <-> IC1.14 <-> R8.1
C3.1 <-> IC1.8 <-> IC1.9 <-> R8.1
C3.2 <-> R12.1
C4.1 <-> IC2.1 <-> R14.1
C4.2 <-> R15.2
C5.1 <-> IC2.7 <-> R18.2
C5.2 <-> R19.1
C6.1 <-> IC2.13 <-> IC2.14 <-> R21.1
C6.1 <-> IC2.8 <-> IC2.9 <-> R21.1
C7.1 <-> C8.1 <-> R21.2
C9.2 <-> IC2.8 <-> IC2.9
D1.A <-> D2.K <-> IC2.12 <-> R19.2
C9.2 <-> IC2.13 <-> IC2.14
D1.A <-> D2.K <-> IC2.10 <-> R19.2
D1.K <-> D2.A <-> IC2.3 <-> IC3.10 <-> IC3.12 <-> IC4.3 <-> R1.2 <-> R10.2 <-> R16.1 <-> R17.2 <-> R2.2 <-> R20.1 <-> R23.2 <-> R25.2 <-> R4.1 <-> R7.2 <-> R9.2 <-> VR1.3
IC1.10 <-> R8.2 <-> R9.1
IC1.10 <-> R7.1
IC1.11 <-> IC2.11 <-> IC3.11 <-> IC4.4
IC1.12 <-> R7.1
IC1.12 <-> R8.2 <-> R9.1
IC1.13 <-> R10.1 <-> R11.2
IC1.14 <-> R11.1
IC1.2 <-> R2.1 <-> R3.1
IC1.3 <-> R1.1
IC1.4 <-> IC2.4 <-> IC3.4 <-> IC4.5
IC1.4 <-> IC2.4 <-> IC3.4 <-> IC4.8
IC1.6 <-> R4.2 <-> R5.2
IC1.8 <-> R11.1
IC1.9 <-> R10.1 <-> R11.2
IC2.10 <-> R20.2
IC2.12 <-> R20.2
IC2.2 <-> R12.2 <-> R13.1 <-> R14.2
IC2.5 <-> R15.1 <-> R16.2
IC2.6 <-> R17.1 <-> R18.1
IC3.1 <-> R22.2 <-> R24.1
IC3.13 <-> R26.2 <-> R29.1
IC3.14 <-> R29.2 <-> R30.2
IC3.2 <-> IC3.5 <-> R24.2
IC3.6 <-> IC3.7 <-> R26.1 <-> R27.1
IC3.8 <-> R29.2 <-> R30.2
IC3.9 <-> R26.2 <-> R29.1
IC4.1 <-> R32.1 <-> VR1.1
IC4.2 <-> R30.1 <-> R32.2
IC4.6 <-> IC4.7
@@ -4,39 +4,39 @@ Switch setup: [ ALL ]
C10.1 <-> C9.1 <-> R22.1
C10.2 <-> IC3.3 <-> R23.1
C11.1 <-> C12.1 <-> R27.2
C11.2 <-> IC3.9 <-> R28.2
C12.2 <-> IC3.8 <-> R28.1 <-> R31.1
C11.2 <-> IC3.13 <-> R28.2
C12.2 <-> IC3.14 <-> R28.1 <-> R31.1
C2.1 <-> IC1.7 <-> R5.1
C2.2 <-> R6.1
C3.1 <-> IC1.13 <-> IC1.14 <-> R8.1
C3.1 <-> IC1.8 <-> IC1.9 <-> R8.1
C3.2 <-> R12.1
C4.1 <-> IC2.1 <-> R14.1
C4.2 <-> R15.2
C5.1 <-> IC2.7 <-> R18.2
C5.2 <-> R19.1
C6.1 <-> IC2.13 <-> IC2.14 <-> R21.1
C6.1 <-> IC2.8 <-> IC2.9 <-> R21.1
C7.1 <-> C8.1 <-> R21.2
C9.2 <-> IC2.8 <-> IC2.9
D1.A <-> D2.K <-> IC2.12 <-> R19.2
C9.2 <-> IC2.13 <-> IC2.14
D1.A <-> D2.K <-> IC2.10 <-> R19.2
D1.K <-> D2.A <-> IC2.3 <-> IC3.10 <-> IC3.12 <-> IC4.3 <-> R1.2 <-> R10.2 <-> R16.1 <-> R17.2 <-> R2.2 <-> R20.1 <-> R23.2 <-> R25.2 <-> R4.1 <-> R7.2 <-> R9.2 <-> VR1.3
IC1.10 <-> R8.2 <-> R9.1
IC1.10 <-> R7.1
IC1.11 <-> IC2.11 <-> IC3.11 <-> IC4.4
IC1.12 <-> R7.1
IC1.12 <-> R8.2 <-> R9.1
IC1.13 <-> R10.1 <-> R11.2
IC1.14 <-> R11.1
IC1.2 <-> R2.1 <-> R3.1
IC1.3 <-> R1.1
IC1.4 <-> IC2.4 <-> IC3.4 <-> IC4.5
IC1.4 <-> IC2.4 <-> IC3.4 <-> IC4.8
IC1.6 <-> R4.2 <-> R5.2
IC1.8 <-> R11.1
IC1.9 <-> R10.1 <-> R11.2
IC2.10 <-> R20.2
IC2.12 <-> R20.2
IC2.2 <-> R12.2 <-> R13.1 <-> R14.2
IC2.5 <-> R15.1 <-> R16.2
IC2.6 <-> R17.1 <-> R18.1
IC3.1 <-> R22.2 <-> R24.1
IC3.13 <-> R26.2 <-> R29.1
IC3.14 <-> R29.2 <-> R30.2
IC3.2 <-> IC3.5 <-> R24.2
IC3.6 <-> IC3.7 <-> R26.1 <-> R27.1
IC3.8 <-> R29.2 <-> R30.2
IC3.9 <-> R26.2 <-> R29.1
IC4.1 <-> R32.1 <-> VR1.1
IC4.2 <-> R30.1 <-> R32.2
IC4.6 <-> IC4.7
@@ -5,24 +5,24 @@ Switch setup: [ ALL ]
C1.1 <-> R2.2
C1.2 <-> R1.2 <-> S1.3
C10.- <-> C12.1 <-> C3.- <-> C5.- <-> C6.1 <-> C7.- <-> IC1.4 <-> IC2.4 <-> Level.1 <-> R1.1 <-> R10.2 <-> R11.2 <-> R12.1 <-> R15.1 <-> R4.1
C11.1 <-> IC2.8 <-> Q2.1 <-> R15.2
C11.1 <-> IC2.5 <-> Q2.1 <-> R15.2
C11.2 <-> R14.2
C12.2 <-> C6.2 <-> C7.+ <-> IC1.5 <-> IC2.5 <-> Q2.2 <-> Q2.3 <-> R3.2 <-> VR1.1 <-> VR1.2
C12.2 <-> C6.2 <-> C7.+ <-> IC1.8 <-> IC2.8 <-> Q2.2 <-> Q2.3 <-> R3.2 <-> VR1.1 <-> VR1.2
C2.1 <-> Q1.1 <-> R14.1 <-> R2.1
C2.2 <-> IC1.3 <-> R3.1 <-> R4.2
C3.+ <-> R5.1
C4.1 <-> C8.+ <-> IC1.1 <-> R6.2 <-> R7.1 <-> R8.1
C4.2 <-> Level.3
C5.+ <-> IC1.8 <-> R8.2
C5.+ <-> IC1.5 <-> R8.2
C8.- <-> D1.A <-> R10.1
C9.+ <-> IC1.6 <-> R9.2
C9.+ <-> IC1.7 <-> R9.2
C9.- <-> D2.A <-> R11.1
IC1.2 <-> R5.2 <-> R6.1
IC1.7 <-> R7.2 <-> R9.1
IC1.6 <-> R7.2 <-> R9.1
IC2.1 <-> IC2.2 <-> R13.1
IC2.3 <-> Ratio.2
IC2.6 <-> Q1.2 <-> Q1.3 <-> R16.2
IC2.7 <-> R13.2 <-> R16.1
IC2.6 <-> R13.2 <-> R16.1
IC2.7 <-> Q1.2 <-> Q1.3 <-> R16.2
Level.2 <-> S1.8
R12.2 <-> Ratio.1
S1.1 <-> S1.9
@@ -5,24 +5,24 @@ Switch setup: [ ALL ]
C1.1 <-> R2.2
C1.2 <-> R1.2
C10.- <-> C12.1 <-> C3.- <-> C5.- <-> C6.1 <-> C7.- <-> IC1.4 <-> IC2.4 <-> Level.1 <-> R1.1 <-> R10.2 <-> R11.2 <-> R12.1 <-> R15.1 <-> R4.1
C11.1 <-> IC2.8 <-> Q2.1 <-> R15.2
C11.1 <-> IC2.5 <-> Q2.1 <-> R15.2
C11.2 <-> R14.2
C12.2 <-> C6.2 <-> C7.+ <-> IC1.5 <-> IC2.5 <-> Q2.2 <-> Q2.3 <-> R3.2 <-> VR1.1 <-> VR1.2
C12.2 <-> C6.2 <-> C7.+ <-> IC1.8 <-> IC2.8 <-> Q2.2 <-> Q2.3 <-> R3.2 <-> VR1.1 <-> VR1.2
C2.1 <-> Q1.1 <-> R14.1 <-> R2.1
C2.2 <-> IC1.3 <-> R3.1 <-> R4.2
C3.+ <-> R5.1
C4.1 <-> C8.+ <-> IC1.1 <-> R6.2 <-> R7.1 <-> R8.1
C4.2 <-> Level.3
C5.+ <-> IC1.8 <-> R8.2
C5.+ <-> IC1.5 <-> R8.2
C8.- <-> D1.A <-> R10.1
C9.+ <-> IC1.6 <-> R9.2
C9.+ <-> IC1.7 <-> R9.2
C9.- <-> D2.A <-> R11.1
IC1.2 <-> R5.2 <-> R6.1
IC1.7 <-> R7.2 <-> R9.1
IC1.6 <-> R7.2 <-> R9.1
IC2.1 <-> IC2.2 <-> R13.1
IC2.3 <-> Ratio.2
IC2.6 <-> Q1.2 <-> Q1.3 <-> R16.2
IC2.7 <-> R13.2 <-> R16.1
IC2.6 <-> R13.2 <-> R16.1
IC2.7 <-> Q1.2 <-> Q1.3 <-> R16.2
R12.2 <-> Ratio.1
Thres.1 <-> Thres.2
Thres.3 <-> VR1.3
@@ -2,14 +2,14 @@ Switch setup: [ ALL ]
C1.+ <-> C3.1 <-> P1.1 <-> P2B.1 <-> P2B.2 <-> R1.1 <-> R2.2 <-> R3.2
C1.- <-> IC1.4 <-> R2.1
C2.1 <-> C3.2 <-> IC1.8 <-> R7.1
C2.1 <-> C3.2 <-> IC1.5 <-> R7.1
C2.2 <-> P2A.1 <-> P2A.2
D1.A <-> D2.K <-> R4.2 <-> R5.1
D1.K <-> D2.A <-> IC1.6 <-> P1.3 <-> R5.2 <-> R6.1
D1.K <-> D2.A <-> IC1.7 <-> P1.3 <-> R5.2 <-> R6.1
IC1.1 <-> IC1.2
IC1.3 <-> P1.2
IC1.5 <-> R1.2 <-> S1.1
IC1.7 <-> R3.1 <-> R4.1
IC1.6 <-> R3.1 <-> R4.1
IC1.8 <-> R1.2 <-> S1.1
P2A.3 <-> R6.2
P2B.3 <-> R7.2
@@ -2,14 +2,14 @@ Switch setup: [ ALL ]
C1.+ <-> C3.1 <-> P1.1 <-> P2B.1 <-> P2B.2 <-> R1.1 <-> R2.2 <-> R3.2
C1.- <-> IC1.4 <-> R2.1
C2.1 <-> C3.2 <-> IC1.8 <-> R7.1
C2.1 <-> C3.2 <-> IC1.5 <-> R7.1
C2.2 <-> P2A.1 <-> P2A.2
D1.A <-> D2.K <-> R4.2 <-> R5.1
D1.K <-> D2.A <-> IC1.6 <-> P1.3 <-> R5.2 <-> R6.1
D1.K <-> D2.A <-> IC1.7 <-> P1.3 <-> R5.2 <-> R6.1
IC1.1 <-> IC1.2
IC1.3 <-> P1.2
IC1.5 <-> R1.2
IC1.7 <-> R3.1 <-> R4.1
IC1.6 <-> R3.1 <-> R4.1
IC1.8 <-> R1.2
P2A.3 <-> R6.2
P2B.3 <-> R7.2
+183 -82
View File
@@ -1,5 +1,5 @@
{
"version" : "6.10.0",
"version" : "6.9.0",
"components" : [ {
"name" : "Blank Board",
"className" : "boards.BlankBoard",
@@ -12229,6 +12229,7 @@
"description" : "Axial electrolytic capacitor, similar to Sprague Atom, F&T, etc",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing", "It has no standing mode: keep the control points at least 'Length' apart, or the body is drawn past the holes" ],
"datasheetModels" : [ "F&T Typ A", "Illinois TTA", "JJ ANH", "Sprague TVA Atom" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -12343,6 +12344,7 @@
"description" : "Vertically mounted electrolytic capacitor, polarized or bipolar",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes; the body's own leads are 'Lead Spacing' apart and centred between them, with lead wire drawn out to each hole. Place the points exactly 'Lead Spacing' apart for straight leads", "'Diameter' is the can as seen from above; 'Height' is drawn only when 'Folded' lays the can on its side" ],
"datasheetModels" : [ "Elna Silmic II", "Nichicon FG", "Nichicon FW", "Nichicon KZ", "Panasonic FC", "Panasonic FM", "Panasonic NHG" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -12467,6 +12469,7 @@
"description" : "Axial film capacitor, similar to Mallory 150s",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing", "If the control points are closer together than 'Length' (0.5in by default) the part is drawn standing, its body over the first point, or over the second with 'Reverse (standing)'; to keep it lying on a tighter footprint, reduce 'Length'" ],
"datasheetModels" : [ "Mallory 150", "Solen Fast" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -12563,6 +12566,7 @@
"description" : "Radial film capacitor, similar to Sprague Orange Drop",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes; the body's own leads are 'Lead Spacing' apart and centred between them, with lead wire drawn out to each hole. Place the points exactly 'Lead Spacing' apart for straight leads", "'Length' and 'Width' are the body as seen from above: 'Length' runs along the line between the leads and 'Width' across it, i.e. the datasheet width and thickness; body height is not drawn" ],
"datasheetModels" : [ "Epcos MKT (5mm)", "Epcos MKT (7.5mm)", "Epcos MKT (10mm)", "Orange Drop 225P", "Orange Drop 715P", "Orange Drop 716P", "WIMA FKP-02 (2.5mm)", "WIMA FKP-2 (5mm)", "WIMA FKS-2 (5mm)", "WIMA MKP-2 (5mm)", "WIMA MKP-4", "WIMA MKS-02 (2.5mm)", "WIMA MKS-2 (5mm)", "WIMA MKS-4" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -12965,6 +12969,7 @@
"description" : "Standard radial mica capacitor",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes; the body's own leads are 'Lead Spacing' apart and centred between them, with lead wire drawn out to each hole. Place the points exactly 'Lead Spacing' apart for straight leads" ],
"datasheetModels" : [ "Cornell Dubilier CD10 (3.6mm)", "Cornell Dubilier CD15 (5.9mm)", "Cornell Dubilier CD19 (8.7mm)", "Cornell Dubilier CDV19 (8.7mm)", "Cornell Dubilier CD30 (11.1mm)", "Cornell Dubilier CDV30 (11.1mm)", "Cornell Dubilier CD42 (27mm)" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -13496,7 +13501,8 @@
"namePrefix" : "R",
"description" : "Resistor layout symbol",
"creationMethod" : "POINT_BY_POINT",
"notes" : [ "The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing", "If the control points are closer together than 'Length' (0.5in by default) the part is drawn standing, its body over the first point; to keep it lying on a tighter footprint, reduce 'Length'" ],
"notes" : [ "The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing", "If the control points are closer together than 'Length' (0.5in by default) the part is drawn standing, its body over the first point; to keep it lying on a tighter footprint, reduce 'Length'", "A datasheet model sets the body size, shape and colors of a resistor family at one power rating but never the resistance; set 'Power Rating' together with the model to pick the matching size" ],
"datasheetModels" : [ "Carbon Comp", "Carbon Film", "Metal Film", "Metal Oxide", "Ceramic", "Dale CMF55", "Dale CMF60" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -14004,58 +14010,6 @@
"label" : "-"
} ]
}
}, {
"name" : "Box (Schematic)",
"className" : "schematic.SchematicBox",
"category" : "Schematic Symbols",
"namePrefix" : "U",
"description" : "Generic schematic box with configurable terminals",
"creationMethod" : "SINGLE_CLICK",
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Body",
"type" : "Color",
"format" : "hex",
"defaultValue" : "#FFFFFF"
}, {
"name" : "Border",
"type" : "Color",
"format" : "hex",
"defaultValue" : "#000000"
}, {
"name" : "Bottom Nodes",
"type" : "String"
}, {
"name" : "Display",
"type" : "Display",
"possibleValues" : [ "NAME", "VALUE", "NONE", "BOTH" ],
"defaultValue" : "BOTH"
}, {
"name" : "Left Nodes",
"type" : "String",
"defaultValue" : "1,2"
}, {
"name" : "Name",
"type" : "String"
}, {
"name" : "Right Nodes",
"type" : "String",
"defaultValue" : "3,4"
}, {
"name" : "Top Nodes",
"type" : "String"
}, {
"name" : "Value",
"type" : "String"
} ],
"pinout" : {
"pinCount" : 4,
"labels" : "sequential"
}
}, {
"name" : "Bulb (Schematic)",
"className" : "misc.BulbSchematicSymbol",
@@ -16594,7 +16548,8 @@
"namePrefix" : "IC",
"description" : "Dual-in-line package IC",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "The first control point is the anchor; with 'Orientation' DEFAULT the first row of pins runs down from it 'Pin Spacing' apart, the second row sits 'Row Spacing' to the right and the notch is at the anchor's end. The other orientations rotate this around the anchor" ],
"notes" : [ "The first control point is the anchor; with 'Orientation' DEFAULT the first row of pins runs down from it 'Pin Spacing' apart, the second row sits 'Row Spacing' to the right and the notch is at the anchor's end. The other orientations rotate this around the anchor", "'Pin Names' is a comma-separated list of pin names in pin-number order, starting with pin 1 next to the notch; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package, e.g. GND_1 and GND_2, while 1IN- and 2IN- are inputs of two separate sections", "A datasheet model is a part number such as TL072 or NE555 and sets 'Pins', 'Row Spacing', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model" ],
"datasheetModels" : [ "TL061", "TL071", "TL081", "LM741", "LF356", "OPA134", "NE5534", "LF351", "UA741", "OP07", "LM308", "TL062", "TL072", "TL082", "NE5532", "JRC4558", "RC4580", "OPA2134", "OPA2604", "LM4562", "LM358", "LF353", "MC1458", "RC4558", "LM833", "TL022", "OP275", "LME49720", "OPA2132", "OPA2227", "TL074", "TL084", "OPA4134", "LM324", "TL064", "NE555", "ICM7555", "TLC555", "NE556", "ICM7556", "LM555", "LM13700", "CA3080", "SSM2164", "V2164", "LM13600", "AS3080", "AS2164", "LM386", "LM380", "TDA7052", "TDA2822", "MN3007", "MN3207", "BL3207", "BL3208A", "V3207", "MN3008", "MN3005", "BL3208B", "MN3101", "MN3102", "V3102", "PT2399", "NE570", "NE571", "SA571", "ICL7660", "TC1044", "MAX1044", "LT1054", "CD4049", "CD4069", "CD40106", "CD4011", "CD4013", "CD4017", "CD4066", "CD4051", "CD4053", "CD4016", "74HC14", "74HC4051", "PC817", "4N35", "6N138", "H11L1", "6N139", "ATtiny85", "ATmega328P", "ATtiny13A", "LM393" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -16615,11 +16570,6 @@
"type" : "Display",
"possibleValues" : [ "NAME", "VALUE", "NONE", "BOTH" ],
"defaultValue" : "BOTH"
}, {
"name" : "Display Pin #s",
"type" : "DisplayNumbers",
"possibleValues" : [ "NO", "DIP", "CONNECTOR", "DIP_MIRROR", "CONNECTOR_MIRROR" ],
"defaultValue" : "NO"
}, {
"name" : "Font Size Override",
"type" : "Integer"
@@ -16641,6 +16591,14 @@
"type" : "Orientation",
"possibleValues" : [ "DEFAULT", "_90", "_180", "_270" ],
"defaultValue" : "DEFAULT"
}, {
"name" : "Pin Labels",
"type" : "DisplayNumbers",
"possibleValues" : [ "NO", "NAMES", "DIP", "CONNECTOR", "DIP_MIRROR", "CONNECTOR_MIRROR" ],
"defaultValue" : "NAMES"
}, {
"name" : "Pin Names",
"type" : "String"
}, {
"name" : "Pin Spacing",
"type" : "Size",
@@ -16663,22 +16621,10 @@
"type" : "String"
} ],
"pinout" : {
"drivers" : [ "Display Pin #s", "Pins" ],
"drivers" : [ "Pin Labels", "Pins" ],
"variants" : [ {
"when" : {
"Display Pin #s" : "NO"
},
"pinCountExpression" : "Pins",
"labels" : "sequential",
"pinCountSamples" : {
"_4" : 4,
"_16" : 16,
"_28" : 28,
"_40" : 40
}
}, {
"when" : {
"Display Pin #s" : "DIP"
"Pin Labels" : "NO"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
@@ -16708,7 +16654,67 @@
}
}, {
"when" : {
"Display Pin #s" : "CONNECTOR"
"Pin Labels" : "NAMES"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
"pinCountSamples" : {
"_4" : 4,
"_16" : 16,
"_28" : 28,
"_40" : 40
},
"example" : {
"when" : {
"Pins" : "_4"
},
"pins" : [ {
"i" : 0,
"label" : "1"
}, {
"i" : 1,
"label" : "2"
}, {
"i" : 2,
"label" : "4"
}, {
"i" : 3,
"label" : "3"
} ]
}
}, {
"when" : {
"Pin Labels" : "DIP"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
"pinCountSamples" : {
"_4" : 4,
"_16" : 16,
"_28" : 28,
"_40" : 40
},
"example" : {
"when" : {
"Pins" : "_4"
},
"pins" : [ {
"i" : 0,
"label" : "1"
}, {
"i" : 1,
"label" : "2"
}, {
"i" : 2,
"label" : "4"
}, {
"i" : 3,
"label" : "3"
} ]
}
}, {
"when" : {
"Pin Labels" : "CONNECTOR"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
@@ -16738,7 +16744,7 @@
}
}, {
"when" : {
"Display Pin #s" : "DIP_MIRROR"
"Pin Labels" : "DIP_MIRROR"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
@@ -16768,7 +16774,7 @@
}
}, {
"when" : {
"Display Pin #s" : "CONNECTOR_MIRROR"
"Pin Labels" : "CONNECTOR_MIRROR"
},
"pinCountExpression" : "Pins",
"labels" : "custom",
@@ -17068,6 +17074,8 @@
"namePrefix" : "IC",
"description" : "Multiwatt package IC typically used for audio amplifiers of high power applications",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "The leads are staggered in two rows and numbered alternately along the package: pin 1 is the first control point, pins 1, 3, 5, ... form the row bent forward in front of the body, away from the heatsink tab, and pins 2, 4, ... the row that leaves the body from underneath. 'Type' picks the package: Multiwatt 11 for LM3886 or TDA7265, Multiwatt 15 for TDA7293 or TDA7294", "'Pin Names' is a comma-separated list of pin names in pin-number order; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package", "A datasheet model is a part number such as LM3886 and sets 'Type', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model" ],
"datasheetModels" : [ "LM3886", "TDA7265", "TDA7293", "TDA7294" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -17101,6 +17109,14 @@
"type" : "Orientation",
"possibleValues" : [ "DEFAULT", "_90", "_180", "_270" ],
"defaultValue" : "DEFAULT"
}, {
"name" : "Pin Labels",
"type" : "PinLabels",
"possibleValues" : [ "NONE", "NAMES", "NUMBERS" ],
"defaultValue" : "NAMES"
}, {
"name" : "Pin Names",
"type" : "String"
}, {
"name" : "Tab",
"type" : "Color",
@@ -17126,14 +17142,90 @@
"when" : {
"Type" : "MULTIWATT_11"
},
"pinCount" : 11,
"labels" : "sequential"
"pins" : [ {
"i" : 0,
"label" : "1"
}, {
"i" : 1,
"label" : "3"
}, {
"i" : 2,
"label" : "5"
}, {
"i" : 3,
"label" : "7"
}, {
"i" : 4,
"label" : "9"
}, {
"i" : 5,
"label" : "11"
}, {
"i" : 6,
"label" : "2"
}, {
"i" : 7,
"label" : "4"
}, {
"i" : 8,
"label" : "6"
}, {
"i" : 9,
"label" : "8"
}, {
"i" : 10,
"label" : "10"
} ]
}, {
"when" : {
"Type" : "MULTIWATT_15"
},
"pinCount" : 15,
"labels" : "sequential"
"pins" : [ {
"i" : 0,
"label" : "1"
}, {
"i" : 1,
"label" : "3"
}, {
"i" : 2,
"label" : "5"
}, {
"i" : 3,
"label" : "7"
}, {
"i" : 4,
"label" : "9"
}, {
"i" : 5,
"label" : "11"
}, {
"i" : 6,
"label" : "13"
}, {
"i" : 7,
"label" : "15"
}, {
"i" : 8,
"label" : "2"
}, {
"i" : 9,
"label" : "4"
}, {
"i" : 10,
"label" : "6"
}, {
"i" : 11,
"label" : "8"
}, {
"i" : 12,
"label" : "10"
}, {
"i" : 13,
"label" : "12"
}, {
"i" : 14,
"label" : "14"
} ]
} ]
}
}, {
@@ -17143,7 +17235,8 @@
"namePrefix" : "IC",
"description" : "Single-in-line package IC",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "The first control point is the anchor; with 'Orientation' DEFAULT the pins run straight down from it 'Pin Spacing' apart and the indent is at the anchor's end of the body. The other orientations rotate this around the anchor" ],
"notes" : [ "The first control point is the anchor; with 'Orientation' DEFAULT the pins run straight down from it 'Pin Spacing' apart and the indent is at the anchor's end of the body. The other orientations rotate this around the anchor", "The first control point is pin 1. 'Pin Names' is a comma-separated list of pin names in pin-number order; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package, while 1IN and 2IN are separate channels", "A datasheet model is a part number such as LA4440 and sets 'Pins', 'Pin Spacing', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model" ],
"datasheetModels" : [ "LA4440", "LA4485", "LA4425A", "TA7205AP", "TDA1013B", "TDA1010A", "TDA1521A", "TDA7056", "TDA7056A", "BA5406", "BA5417", "BA3121N" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -17182,6 +17275,14 @@
"type" : "Orientation",
"possibleValues" : [ "DEFAULT", "_90", "_180", "_270" ],
"defaultValue" : "DEFAULT"
}, {
"name" : "Pin Labels",
"type" : "PinLabels",
"possibleValues" : [ "NONE", "NAMES", "NUMBERS" ],
"defaultValue" : "NAMES"
}, {
"name" : "Pin Names",
"type" : "String"
}, {
"name" : "Pin Spacing",
"type" : "Size",
+1 -4
View File
@@ -1,5 +1,5 @@
{
"version" : "6.10.0",
"version" : "6.9.0",
"categories" : {
"Boards" : [ {
"name" : "Blank Board",
@@ -389,9 +389,6 @@
}, {
"name" : "Battery (Schematic)",
"className" : "misc.BatterySymbol"
}, {
"name" : "Box (Schematic)",
"className" : "schematic.SchematicBox"
}, {
"name" : "Bulb (Schematic)",
"className" : "misc.BulbSchematicSymbol"
@@ -25,7 +25,6 @@ import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.schematic.ISchematicFactory;
/**
* Entity class used to describe a component type.
@@ -57,7 +56,6 @@ public class ComponentType implements Serializable {
private boolean enableCache;
private List<String[]> datasheet;
private int datasheetCreationStepCount;
private Class<? extends ISchematicFactory> schematicFactoryClass;
public ComponentType() {
}
@@ -160,18 +158,6 @@ public class ComponentType implements Serializable {
return datasheetCreationStepCount;
}
/**
* @return the declared {@link ISchematicFactory} class for this type, or {@code null} when the
* type relies on the generic box fallback.
*/
public Class<? extends ISchematicFactory> getSchematicFactoryClass() {
return schematicFactoryClass;
}
public void setSchematicFactoryClass(Class<? extends ISchematicFactory> schematicFactoryClass) {
this.schematicFactoryClass = schematicFactoryClass;
}
@Override
public String toString() {
return name;
@@ -26,7 +26,6 @@ import java.util.List;
import org.diylc.netlist.INetlistParser;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistException;
import org.diylc.presenter.ContinuityArea;
/**
* Interface for {@link Netlist} related operations.
@@ -43,14 +42,7 @@ public interface INetlistProcessor {
* @throws NetlistException
*/
List<Netlist> extractNetlists(boolean includeSwitches) throws NetlistException;
/**
* @return the current continuity areas as computed by the drawing manager. Needed to build a
* netlist outside of the presenter, e.g. for schematic generation.
*/
List<ContinuityArea> getContinuityAreas();
/**
* Finds all available {@link INetlistAnalyzer} implementations.
*
@@ -68,7 +68,6 @@ public class Project implements Serializable, Cloneable {
private Set<IDIYComponent<?>> lockedComponents;
private Set<Integer> hiddenLayers;
private Font font = DEFAULT_FONT;
private SchematicView schematicView;
public Project() {
components = new ArrayList<IDIYComponent<?>>();
@@ -197,30 +196,6 @@ public class Project implements Serializable, Cloneable {
return hiddenLayers;
}
/**
* @return the schematic representation of this project, or {@code null} if the user has never
* opened the schematic view. Use {@link #getOrCreateSchematicView()} when a non-null
* instance is required.
*/
public SchematicView getSchematicView() {
return schematicView;
}
public void setSchematicView(SchematicView schematicView) {
this.schematicView = schematicView;
}
/**
* Lazily initializes and returns the {@link SchematicView}. Calling this method causes the
* schematic data to be persisted with the project from now on.
*/
public SchematicView getOrCreateSchematicView() {
if (schematicView == null) {
schematicView = new SchematicView();
}
return schematicView;
}
public VersionNumber getFileVersion() {
return fileVersion;
}
@@ -348,11 +323,6 @@ public class Project implements Serializable, Cloneable {
return false;
} else if (!hiddenLayers.equals(other.hiddenLayers))
return false;
if (schematicView == null) {
if (other.schematicView != null)
return false;
} else if (!schematicView.equals(other.schematicView))
return false;
return true;
}
@@ -388,10 +358,7 @@ public class Project implements Serializable, Cloneable {
project.setWidth(this.getWidth());
project.getLockedLayers().addAll(this.getLockedLayers());
project.getHiddenLayers().addAll(this.getHiddenLayers());
project.setFont(this.getFont());
if (this.schematicView != null) {
project.setSchematicView(this.schematicView.clone());
}
project.setFont(this.getFont());
Map<IDIYComponent<?>, IDIYComponent<?>> cloneMap = new HashMap<IDIYComponent<?>, IDIYComponent<?>>();
@@ -1,183 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.core;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
/**
* Schematic representation of a physical layout {@link Project}. It is derived from the layout via
* the netlist infrastructure and rendered using the regular DIYLC drawing pipeline. It is embedded
* in {@link Project} and serialized alongside the rest of the project data, but only after the user
* opens the schematic view for the first time (the field stays {@code null} until then, keeping
* files unchanged for users who never use the feature).
*
* @author Branislav Stojkovic
*/
public class SchematicView implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
public static Size DEFAULT_WIDTH = new Size(42d, SizeUnit.cm);
public static Size DEFAULT_HEIGHT = new Size(30d, SizeUnit.cm);
public static Size DEFAULT_GRID_SPACING = new Size(0.1d, SizeUnit.in);
/** Schematic components: symbols and auto-routed wires, sorted by z-order ascending. */
private List<IDIYComponent<?>> components;
/**
* Maps a physical component id ({@link IDIYComponent#getId()}) to the ids of the schematic
* symbol(s) that were spawned from it. The relation is 1:N to support multi-section components
* such as dual triodes or dual op-amps.
*/
private Map<UUID, List<UUID>> physicalToSchematicMap;
private Size width;
private Size height;
private Size gridSpacing;
public SchematicView() {
this.components = new ArrayList<IDIYComponent<?>>();
this.physicalToSchematicMap = new LinkedHashMap<UUID, List<UUID>>();
this.width = DEFAULT_WIDTH;
this.height = DEFAULT_HEIGHT;
this.gridSpacing = DEFAULT_GRID_SPACING;
}
public List<IDIYComponent<?>> getComponents() {
if (components == null) {
components = new ArrayList<IDIYComponent<?>>();
}
return components;
}
public void setComponents(List<IDIYComponent<?>> components) {
this.components = components;
}
public Map<UUID, List<UUID>> getPhysicalToSchematicMap() {
if (physicalToSchematicMap == null) {
physicalToSchematicMap = new LinkedHashMap<UUID, List<UUID>>();
}
return physicalToSchematicMap;
}
public void setPhysicalToSchematicMap(Map<UUID, List<UUID>> physicalToSchematicMap) {
this.physicalToSchematicMap = physicalToSchematicMap;
}
public Size getWidth() {
return width == null ? DEFAULT_WIDTH : width;
}
public void setWidth(Size width) {
this.width = width;
}
public Size getHeight() {
return height == null ? DEFAULT_HEIGHT : height;
}
public void setHeight(Size height) {
this.height = height;
}
public Size getGridSpacing() {
return gridSpacing == null ? DEFAULT_GRID_SPACING : gridSpacing;
}
public void setGridSpacing(Size gridSpacing) {
this.gridSpacing = gridSpacing;
}
/**
* @return {@code true} if the schematic has already been generated at least once.
*/
public boolean isGenerated() {
return components != null && !components.isEmpty();
}
@Override
public SchematicView clone() {
SchematicView clone = new SchematicView();
clone.setWidth(this.getWidth());
clone.setHeight(this.getHeight());
clone.setGridSpacing(this.getGridSpacing());
for (IDIYComponent<?> component : this.getComponents()) {
try {
clone.getComponents().add(component.clone());
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
for (Map.Entry<UUID, List<UUID>> entry : this.getPhysicalToSchematicMap().entrySet()) {
clone.getPhysicalToSchematicMap().put(entry.getKey(), new ArrayList<UUID>(entry.getValue()));
}
return clone;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((components == null) ? 0 : components.hashCode());
result = prime * result + ((width == null) ? 0 : width.hashCode());
result = prime * result + ((height == null) ? 0 : height.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
SchematicView other = (SchematicView) obj;
if (components == null) {
if (other.components != null)
return false;
} else if (other.components == null || components.size() != other.components.size()) {
return false;
} else {
for (int i = 0; i < components.size(); i++) {
if (!components.get(i).equalsTo(other.components.get(i)))
return false;
}
}
if (width == null ? other.width != null : !width.equals(other.width))
return false;
if (height == null ? other.height != null : !height.equals(other.height))
return false;
if (gridSpacing == null ? other.gridSpacing != null : !gridSpacing.equals(other.gridSpacing))
return false;
return new HashMap<UUID, List<UUID>>(getPhysicalToSchematicMap())
.equals(new HashMap<UUID, List<UUID>>(other.getPhysicalToSchematicMap()));
}
}
@@ -25,6 +25,7 @@ import java.awt.geom.Point2D;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class Template {
@@ -35,6 +36,8 @@ public class Template {
@Deprecated
private boolean defaultFlag;
private LocalDateTime createdOn;
// set only on a factory variant that has been withdrawn, which then carries nothing but its name
private LocalDateTime deletedOn;
public Template() {
}
@@ -82,11 +85,20 @@ public class Template {
this.createdOn = createdOn;
}
public LocalDateTime getDeletedOn() {
return deletedOn;
}
public void setDeletedOn(LocalDateTime deletedOn) {
this.deletedOn = deletedOn;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((createdOn == null) ? 0 : createdOn.hashCode());
result = prime * result + ((deletedOn == null) ? 0 : deletedOn.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((points == null) ? 0 : points.hashCode());
result = prime * result + ((values == null) ? 0 : values.hashCode());
@@ -107,6 +119,9 @@ public class Template {
return false;
} else if (!createdOn.equals(other.createdOn))
return false;
if (!Objects.equals(deletedOn, other.deletedOn)) {
return false;
}
if (name == null) {
if (other.name != null)
return false;
@@ -28,7 +28,6 @@ import org.diylc.common.DefaultComponentTransformer;
import org.diylc.common.IComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.schematic.ISchematicFactory;
/**
* Annotation that needs to be used for each {@link IDIYComponent} implementation. Describes
@@ -131,13 +130,12 @@ public @interface ComponentDescriptor {
int datasheetCreationStepCount() default 0;
/**
* Strategy that converts an instance of this component type into one or more schematic symbols
* when the schematic view is generated. When left at the default ({@link ISchematicFactory}
* itself, used here only as a "none" marker), a generic box symbol is produced instead.
* If datasheet is enabled, the column that names the part a row describes. It is what the AI
* refers to a model by, while the columns before it only group rows in the toolbox.
*
* @return the schematic factory class, or {@code ISchematicFactory.class} when none is declared
* @return index of the model name column
*/
Class<? extends ISchematicFactory> schematicFactory() default ISchematicFactory.class;
int datasheetModelColumn() default 0;
/**
* @return true if the component type should not be listed in the palette (toolbox and
@@ -52,6 +52,7 @@ import org.diylc.plugins.chatbot.model.AiCatalogIndexEntry;
import org.diylc.plugins.chatbot.model.pinout.AiPinout;
import org.diylc.presenter.ComparatorFactory;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.DatasheetService;
import org.diylc.presenter.ReleaseHistory;
/**
@@ -175,6 +176,18 @@ public class ComponentCatalogGenerator {
comp.put("notes", notes);
}
// a datasheet lists one row per part, so the model names are the distinct values of its model
// column
List<String[]> datasheet = ct.getDatasheet();
if (datasheet != null) {
int column = DatasheetService.getInstance().getModelColumn(ct.getInstanceClass());
List<String> models = datasheet.stream()
.map(row -> row[column].trim())
.distinct()
.collect(Collectors.toList());
comp.put("datasheetModels", models);
}
// Extract editable properties
try {
List<PropertyWrapper> properties = processor.extractProperties(ct.getInstanceClass());
@@ -133,11 +133,6 @@ public class ComponentProcessor {
new ComponentType(name, description, creationMethod, category, namePrefix, author, icon, clazz, zOrder,
flexibleZOrder, bomPolicy, autoEdit, transformer, keywordPolicy, keywordTag, cacheDrawing, datasheet,
datasheetCreationStepCount);
Class<? extends org.diylc.schematic.ISchematicFactory> schematicFactory =
clazz.getAnnotation(ComponentDescriptor.class).schematicFactory();
if (schematicFactory != null && schematicFactory != org.diylc.schematic.ISchematicFactory.class) {
componentType.setSchematicFactoryClass(schematicFactory);
}
componentTypeMap.put(clazz.getName(), componentType);
return componentType;
}
@@ -31,6 +31,7 @@ import org.apache.log4j.Logger;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDatasheetSupport;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.measures.AbstractMeasure;
import org.diylc.core.measures.Unit;
@@ -128,6 +129,16 @@ public class DatasheetService {
return null;
}
/**
* @param clazz
* @return the datasheet column that names the model a row describes, see
* {@link ComponentDescriptor#datasheetModelColumn()}.
*/
public int getModelColumn(Class<?> clazz) {
ComponentDescriptor descriptor = clazz.getAnnotation(ComponentDescriptor.class);
return descriptor == null ? 0 : descriptor.datasheetModelColumn();
}
/**
* Finds the rows of a datasheet series by its name, ignoring case. When no series has exactly
* that name, every series whose name contains it is returned instead.
@@ -141,14 +152,16 @@ public class DatasheetService {
if (datasheet == null || series == null || series.isBlank()) {
return new ArrayList<String[]>();
}
int column = getModelColumn(clazz);
String name = series.trim();
List<String[]> rows = datasheet.stream()
.filter(row -> row.length > 0 && row[0].trim().equalsIgnoreCase(name))
.filter(row -> row.length > column && row[column].trim().equalsIgnoreCase(name))
.collect(Collectors.toList());
if (rows.isEmpty()) {
String lowerName = name.toLowerCase();
rows = datasheet.stream()
.filter(row -> row.length > 0 && row[0].trim().toLowerCase().contains(lowerName))
.filter(row -> row.length > column
&& row[column].trim().toLowerCase().contains(lowerName))
.collect(Collectors.toList());
}
return rows;
@@ -170,7 +183,7 @@ public class DatasheetService {
return null;
}
Object value = component.getValue();
if (value == null) {
if (value == null || (value instanceof String text && text.isBlank())) {
// nothing to preserve, but nothing to choose by either
return rows.size() == 1 ? rows.get(0) : null;
}
@@ -3208,15 +3208,10 @@ public class Presenter implements IPlugInPort {
@Override
public List<Netlist> extractNetlists(boolean includeSwitches) throws NetlistException {
List<ContinuityArea> continuityAreas = drawingManager.getContinuityAreas();
return NetlistBuilder.extractNetlists(includeSwitches, currentProject, continuityAreas);
}
@Override
public List<ContinuityArea> getContinuityAreas() {
return drawingManager.getContinuityAreas();
}
@Override
public List<INetlistAnalyzer> getNetlistAnalyzers() {
try {
@@ -65,6 +65,7 @@ public class VariantManager {
.readObject(IPlugInPort.DEFAULT_TEMPLATES_IMPORT_DATE_KEY, LocalDateTime.MIN);
int importCount = 0;
int deleteCount = 0;
try {
InputStream inputStream = Presenter.class.getResourceAsStream("/" + VARIANTS_FILE_NAME);
@@ -89,7 +90,20 @@ public class VariantManager {
.collect(Collectors.toSet());
for (Template variant : entry.getValue()) {
if ((variant.getCreatedOn() == null || importDate.compareTo(variant.getCreatedOn()) < 0)
// a deletion stub keeps nothing but its name, so a missing createdOn only means an old
// entry when the variant has not been deleted
boolean created = variant.getCreatedOn() == null ? variant.getDeletedOn() == null
: importDate.compareTo(variant.getCreatedOn()) < 0;
boolean deleted =
variant.getDeletedOn() != null && importDate.compareTo(variant.getDeletedOn()) < 0;
// a variant created and deleted since the last import never reached the user, and a
// deletion older than the last import was applied then, so a variant the user has since
// created under the same name stays
if (deleted && !created) {
if (existingVariants.removeIf(x -> variant.getName().equals(x.getName()))) {
deleteCount++;
}
} else if (created && variant.getDeletedOn() == null
&& !existingNames.contains(variant.getName())) {
existingVariants.add(variant);
importCount++;
@@ -104,6 +118,9 @@ public class VariantManager {
if (importCount > 0) {
LOG.info(String.format("Imported default %d variants", importCount));
}
if (deleteCount > 0) {
LOG.info(String.format("Deleted %d withdrawn default variants", deleteCount));
}
} catch (Exception e) {
LOG.error("Could not load default variants", e);
}
@@ -1,56 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.util.List;
import org.diylc.core.IDIYComponent;
/**
* Strategy that converts a physical layout component into one or more schematic symbols.
*
* <p>
* Implementations are declared per component type through
* {@code @ComponentDescriptor(schematicFactory = ...)}. When a type declares no factory, the
* schematic generator falls back to a generic box symbol.
* </p>
*
* <p>
* Implementations must have a public no-argument constructor. They are instantiated once and reused,
* so they must be stateless.
* </p>
*
* @author Branislav Stojkovic
*/
public interface ISchematicFactory {
/**
* Creates fully instantiated and configured schematic symbol instances for the given physical
* component. The returned symbols only need to be positioned by the caller.
*
* @param physicalComponent the physical layout component
* @return list of {@link SchematicSymbolMapping} entries, one per schematic symbol (1:1 for simple
* components, 1:N for multi-section components). An empty list means the component produces
* no symbol and should be treated as excluded.
*/
List<SchematicSymbolMapping> createSchematicSymbols(IDIYComponent<?> physicalComponent);
}
@@ -1,103 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.util.HashMap;
import java.util.Map;
import org.diylc.core.IDIYComponent;
/**
* Describes a single schematic symbol that an {@link ISchematicFactory} spawned from a physical
* layout component. For multi-section physical components (dual triodes, dual op-amps, etc.) a
* factory returns more than one of these.
*
* @author Branislav Stojkovic
*/
public class SchematicSymbolMapping {
private IDIYComponent<?> schematicSymbol;
private Map<Integer, Integer> pinMapping;
private String sectionLabel;
public SchematicSymbolMapping() {
this.pinMapping = new HashMap<Integer, Integer>();
}
public SchematicSymbolMapping(IDIYComponent<?> schematicSymbol) {
this();
this.schematicSymbol = schematicSymbol;
}
public SchematicSymbolMapping(IDIYComponent<?> schematicSymbol, Map<Integer, Integer> pinMapping,
String sectionLabel) {
this.schematicSymbol = schematicSymbol;
this.pinMapping = pinMapping == null ? new HashMap<Integer, Integer>() : pinMapping;
this.sectionLabel = sectionLabel;
}
/**
* @return the fully configured schematic symbol instance. It only needs to be positioned.
*/
public IDIYComponent<?> getSchematicSymbol() {
return schematicSymbol;
}
public void setSchematicSymbol(IDIYComponent<?> schematicSymbol) {
this.schematicSymbol = schematicSymbol;
}
/**
* Maps a physical component control point index to the control point index on
* {@link #getSchematicSymbol()}. Physical control points that are not present in this map have no
* counterpart on the symbol and any net attached to them is dropped for this symbol.
*
* @return physical control point index -&gt; schematic control point index
*/
public Map<Integer, Integer> getPinMapping() {
if (pinMapping == null) {
pinMapping = new HashMap<Integer, Integer>();
}
return pinMapping;
}
public void setPinMapping(Map<Integer, Integer> pinMapping) {
this.pinMapping = pinMapping;
}
public SchematicSymbolMapping mapPin(int physicalIndex, int schematicIndex) {
getPinMapping().put(physicalIndex, schematicIndex);
return this;
}
/**
* @return an optional label identifying the section of a multi-section component, e.g.
* "Section A". May be {@code null}.
*/
public String getSectionLabel() {
return sectionLabel;
}
public void setSectionLabel(String sectionLabel) {
this.sectionLabel = sectionLabel;
}
}
File diff suppressed because it is too large Load Diff
+44 -248
View File
@@ -1,253 +1,5 @@
<java.util.Arrays_-ArrayList>
<a class="org.diylc.appframework.update.Version-array">
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>10</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-01-20 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'M' and 'N' potentiometer taper options (thank you mm-rnd)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added E-C-B BJT pinout to TO-92 transistor package</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the performance issue that happens when groups of components are copy-pasted and the project size grows uncontrollably</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>All building blocks of the same type get added to the same group</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Tactile Micro Switch' editor doesn't show up on double click</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>11</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-02-15 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added ability to export chat history from 'AI Assistant' to a HTML file</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Upgraded AI models for AI-powered tools and improved overall quality of the responses</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Windows installer prompts for installation folder instead of forcing one</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Shape components shouldn't appear in the BOM</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved rendering of 'Switch (Latching)' symbol to clearly make visual contact between the terminals</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Consolidate a few keyboard shortcuts for consistency</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Using "Duplicate Selection" results in duplicates of component ids, breaking Project Explorer panel and causing other issues downstream</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Calculated length disappeared from Hookup Wire and similar curved components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Resize dimensions do not show for 'Chassis Panel' component when dragging a corner point</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>12</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-03-26 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Auto-Unit Conversion' configuration which automatically converts between metric and imperial units based on the ruler unit preference</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added a new section in the output of 'AI Circuit Analyzer' which provides insights into the operation of the circuit and individual components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Better error handling for AI tools</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Pressing the 'Esc' key closes selection editor dialog</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Right click (or two finger tap on mac) after selecting a component with a regular click opens editor dialog in addition to the context menu</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Clicking on a component type in the left-hand component tree sometimes does not register and does not instantiate the component</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>13</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-04-07 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added new 'Heat Sink Resistor' component with a bunch of factory variants covering the most common types</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added new 'Dial Scale' variations - 'Ticks + Numeric (0/5.5/11)' a'la Friedman and another variation of 'Ticks' with more pronounced 9/12/3 o'clock indices</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Core' property to 'Radial Inductor' component that allows selection between Ferrite and Air core inductor models'</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Envelope' property to tube symbol components which can be used to display one side of the outer envelope dashed indicating section A or section B of a tube</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added more ceramic resistor variants to 'Resistor' component</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Uniformized rendering of components while being dragged</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Ability to blank-out previously populated values for unit-type measures, e.g. resistance, capacitance, dimensions, etc</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved data model for storing transparency alpha and other properties that are edited using a slider widget; as a result, the editing is more precise and does not randomly change to neighboring values</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved editing experience of integer properties (e.g. number of terminals on a tag strip) by using the spinner text field</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved logic when moving component(s) that have other components stuck with them; in case there are non-sticky control points within the included components, they will be moved along retaining component shape (e.g. wires or curved traces)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Decommission 'Loadline Editor' option from the File menu</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>14</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-06-03 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Potentiometer (Miniature)' component with two variations - right-angled and vertically mounted</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Thonk 3.5mm Jack' component with two variations - mono switched and stereo</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Enable all types of potentiometers and jacks for guitar diagram analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increase maximum number of switch combination for analysis to 1024</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'Line' component set to px used to render slightly thicker than 1px, appearing thicker than schematic symbols; changed to match the thickenss of schematic symbols</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>15</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-06-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Add Freeway Blade switches 3B3-01, 5B5-01, 5B5-02 (thank you Martin Morell)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>IEC variations of fuse and potentiometer symbols</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Detect the style of a treble bleed based on the topology and component values (e.g. Fender, DiMarzio, Kinman, etc)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved quality of AI Assistant responses in some cases</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved precision of rendering, in some cases there was a sub-pixel misalignment between the lines that should meet exactly</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Tone capacitor mistaken for treble bleed when connected to the input lug of the volume pot</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Dragging a component from the component tree to the canvas sometimes does not work as expected without clicking on it first and then dragging it</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Schaller Megaswitch' not rendered correctly when rotated for 90 or 270 degrees</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>6</major>
@@ -744,5 +496,49 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>6</major>
<minor>10</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-10-30 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Pin Names' property to 'DIP IC', 'SIP IC' and 'Multiwatt IC' to name each pin, e.g. 1OUT, 1IN-, VCC+; the names are drawn on the component and used in node tooltips, netlists and AI analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Pin Labels' property to 'SIP IC' and 'Multiwatt IC' to show pin names or pin numbers next to the pins</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Re-modeled factory variants of Resistor component type as datasheet in order to make them available to the AI; this will produce more accurate AI-driven edits</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added datasheets to 'DIP IC', 'SIP IC' and 'Multiwatt IC' - pick a part by its number from the component's submenu to set its pin count, spacing and pin names at once - more than 100 chips in total; the AI Assistant can place these parts by part number too</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'DIP IC' with 'Display Pin #s' set to 'No' numbered its pins in the wrong order in netlists and AI analysis, e.g. pin 8 of an 8-pin IC was reported as pin 5</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Multiwatt IC' numbered its pins row by row instead of alternating between the two rows as on the real package</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Multiwatt IC' in the Multiwatt 11 package had its leads 1.27mm apart instead of 1.7mm; existing Multiwatt 11 parts change shape when the project is opened</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'SIP IC' body was drawn too short when 'Pin Spacing' is not a multiple of 0.1 inch, leaving the last pin outside the body</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
</a>
</java.util.Arrays_-ArrayList>
@@ -5325,5 +5325,253 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>10</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-01-20 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'M' and 'N' potentiometer taper options (thank you mm-rnd)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added E-C-B BJT pinout to TO-92 transistor package</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the performance issue that happens when groups of components are copy-pasted and the project size grows uncontrollably</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>All building blocks of the same type get added to the same group</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Tactile Micro Switch' editor doesn't show up on double click</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>11</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-02-15 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added ability to export chat history from 'AI Assistant' to a HTML file</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Upgraded AI models for AI-powered tools and improved overall quality of the responses</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Windows installer prompts for installation folder instead of forcing one</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Shape components shouldn't appear in the BOM</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved rendering of 'Switch (Latching)' symbol to clearly make visual contact between the terminals</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Consolidate a few keyboard shortcuts for consistency</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Using "Duplicate Selection" results in duplicates of component ids, breaking Project Explorer panel and causing other issues downstream</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Calculated length disappeared from Hookup Wire and similar curved components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Resize dimensions do not show for 'Chassis Panel' component when dragging a corner point</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>12</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-03-26 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Auto-Unit Conversion' configuration which automatically converts between metric and imperial units based on the ruler unit preference</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added a new section in the output of 'AI Circuit Analyzer' which provides insights into the operation of the circuit and individual components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Better error handling for AI tools</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Pressing the 'Esc' key closes selection editor dialog</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Right click (or two finger tap on mac) after selecting a component with a regular click opens editor dialog in addition to the context menu</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Clicking on a component type in the left-hand component tree sometimes does not register and does not instantiate the component</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>13</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-04-07 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added new 'Heat Sink Resistor' component with a bunch of factory variants covering the most common types</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added new 'Dial Scale' variations - 'Ticks + Numeric (0/5.5/11)' a'la Friedman and another variation of 'Ticks' with more pronounced 9/12/3 o'clock indices</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Core' property to 'Radial Inductor' component that allows selection between Ferrite and Air core inductor models'</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Envelope' property to tube symbol components which can be used to display one side of the outer envelope dashed indicating section A or section B of a tube</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added more ceramic resistor variants to 'Resistor' component</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Uniformized rendering of components while being dragged</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Ability to blank-out previously populated values for unit-type measures, e.g. resistance, capacitance, dimensions, etc</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved data model for storing transparency alpha and other properties that are edited using a slider widget; as a result, the editing is more precise and does not randomly change to neighboring values</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved editing experience of integer properties (e.g. number of terminals on a tag strip) by using the spinner text field</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved logic when moving component(s) that have other components stuck with them; in case there are non-sticky control points within the included components, they will be moved along retaining component shape (e.g. wires or curved traces)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Decommission 'Loadline Editor' option from the File menu</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>14</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-06-03 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Potentiometer (Miniature)' component with two variations - right-angled and vertically mounted</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Thonk 3.5mm Jack' component with two variations - mono switched and stereo</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Enable all types of potentiometers and jacks for guitar diagram analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increase maximum number of switch combination for analysis to 1024</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'Line' component set to px used to render slightly thicker than 1px, appearing thicker than schematic symbols; changed to match the thickenss of schematic symbols</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>15</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-06-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Add Freeway Blade switches 3B3-01, 5B5-01, 5B5-02 (thank you Martin Morell)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>IEC variations of fuse and potentiometer symbols</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Detect the style of a treble bleed based on the topology and component values (e.g. Fender, DiMarzio, Kinman, etc)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved quality of AI Assistant responses in some cases</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved precision of rendering, in some cases there was a sub-pixel misalignment between the lines that should meet exactly</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Tone capacitor mistaken for treble bleed when connected to the input lug of the volume pot</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Dragging a component from the component tree to the canvas sometimes does not work as expected without clicking on it first and then dragging it</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Schaller Megaswitch' not rendered correctly when rotated for 90 or 270 degrees</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
</a>
</java.util.Arrays_-ArrayList>
@@ -26,7 +26,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -49,8 +48,7 @@ import org.diylc.utils.Constants;
@AiNote("The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing")
@AiNote("It has no standing mode: keep the control points at least 'Length' apart, or the body is drawn past the holes")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Electrolytic Capacitor (Axial)", author = "Branislav Stojkovic",
@ComponentDescriptor(name = "Electrolytic Capacitor (Axial)", author = "Branislav Stojkovic",
category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Axial electrolytic capacitor, similar to Sprague Atom, F&T, etc",
zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class,
@@ -24,7 +24,6 @@ import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import org.diylc.components.passive.CapacitorDatasheetService.CapacitorDatasheet;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
@@ -35,8 +34,7 @@ import org.diylc.core.annotations.EditableProperty;
@AiNote("The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing")
@AiNote("If the control points are closer together than 'Length' (0.5in by default) the part is drawn standing, its body over the first point, or over the second with 'Reverse (standing)'; to keep it lying on a tighter footprint, reduce 'Length'")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Film Capacitor (Axial)", author = "Branislav Stojkovic",
@ComponentDescriptor(name = "Film Capacitor (Axial)", author = "Branislav Stojkovic",
category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Axial film capacitor, similar to Mallory 150s", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class, enableDatasheet = true,
@@ -12,7 +12,6 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.PotentiometerSchematicFactory;
import org.diylc.common.Display;
import org.diylc.common.IPlugInPort;
@@ -31,8 +30,7 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(schematicFactory = PotentiometerSchematicFactory.class,
name = "Potentiometer (Miniature)", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Potentiometer (Miniature)", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.SINGLE_CLICK, instanceNamePrefix = "VR",
description = "9mm Alpha style miniature potentiometers", zOrder = IDIYComponent.COMPONENT,
transformer = MiniaturePotentiometerTransformer.class)
@@ -33,7 +33,6 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.PotentiometerSchematicFactory;
import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment;
@@ -60,8 +59,7 @@ import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the other two lugs run to the right of it 'Spacing' apart, and the other orientations rotate this around the anchor")
@AiNote("Orient the pot so its lugs face the rest of the circuit: they face down in DEFAULT, left in _90, up in _180 and right in _270")
@AiNote("Unless told otherwise, use one of two standard sizes, picked by the type of circuit and the space available: the 25mm pot, which the defaults already describe ('Diameter' 1in, 'Spacing' 0.3in), or the 16mm pot ('Diameter' 16mm, 'Spacing' 0.2in)")
@ComponentDescriptor(schematicFactory = PotentiometerSchematicFactory.class,
name = "Potentiometer", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Potentiometer", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.SINGLE_CLICK, instanceNamePrefix = "VR",
description = "Metal body panel mount potentiometer with solder lugs or PCB terminals (Alpha, CTS, Bourns)", zOrder = IDIYComponent.COMPONENT,
transformer = PotentiometerTransformer.class, enableCache = true)
@@ -27,7 +27,6 @@ import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.core.annotations.AiNote;
@@ -39,8 +38,7 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
@AiNote("'Length' and 'Width' are the disc as seen from above: 'Length' is its diameter and 'Width' its thickness")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Ceramic Capacitor (Radial)", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Ceramic Capacitor (Radial)", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Standard radial ceramic capacitor", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class)
@@ -29,7 +29,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -49,8 +48,7 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("'Diameter' is the can as seen from above; 'Height' is drawn only when 'Folded' lays the can on its side")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Electrolytic Capacitor (Radial)", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Electrolytic Capacitor (Radial)", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Vertically mounted electrolytic capacitor, polarized or bipolar", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class, enableDatasheet = true, datasheetCreationStepCount = 3)
@@ -26,7 +26,6 @@ import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.components.passive.CapacitorDatasheetService.CapacitorDatasheet;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
@@ -39,8 +38,7 @@ import org.diylc.core.measures.SizeUnit;
@AiNote("The two control points are the holes; the body's own leads are 'Lead Spacing' apart and centred between them, with lead wire drawn out to each hole. Place the points exactly 'Lead Spacing' apart for straight leads")
@AiNote("'Length' and 'Width' are the body as seen from above: 'Length' runs along the line between the leads and 'Width' across it, i.e. the datasheet width and thickness; body height is not drawn")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Film Capacitor (Radial)", author = "Branislav Stojkovic",
@ComponentDescriptor(name = "Film Capacitor (Radial)", author = "Branislav Stojkovic",
category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Radial film capacitor, similar to Sprague Orange Drop",
zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class,
@@ -33,7 +33,6 @@ import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.common.ObjectCache;
import org.diylc.schematic.InductorSchematicFactory;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
@@ -47,8 +46,7 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("'Diameter' is the body as seen from above; 'Height' is drawn only when 'Folded' lays the part on its side")
@ComponentDescriptor(schematicFactory = InductorSchematicFactory.class,
name = "Inductor (Radial)", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Inductor (Radial)", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "L",
description = "Vertically mounted ferrite core inductor", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class)
@@ -26,7 +26,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import org.diylc.components.passive.CapacitorDatasheetService.CapacitorDatasheet;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
@@ -39,8 +38,7 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.core.measures.Voltage;
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Mica Capacitor (Radial)", author = "Branislav Stojkovic",
@ComponentDescriptor(name = "Mica Capacitor (Radial)", author = "Branislav Stojkovic",
category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Standard radial mica capacitor", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class, enableDatasheet = true,
@@ -32,12 +32,12 @@ import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.common.ObjectCache;
import org.diylc.schematic.ResistorSchematicFactory;
import org.diylc.common.ResistorColorCode;
import org.diylc.components.AbstractLeadedComponent;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDatasheetSupport;
import org.diylc.core.annotations.AiNote;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
@@ -49,11 +49,13 @@ import org.diylc.core.measures.SizeUnit;
@AiNote("The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing")
@AiNote("If the control points are closer together than 'Length' (0.5in by default) the part is drawn standing, its body over the first point; to keep it lying on a tighter footprint, reduce 'Length'")
@ComponentDescriptor(schematicFactory = ResistorSchematicFactory.class,
name = "Resistor", author = "Branislav Stojkovic", category = "Passive",
@AiNote("A datasheet model sets the body size, shape and colors of a resistor family at one power rating but never the resistance; set 'Power Rating' together with the model to pick the matching size")
@ComponentDescriptor(name = "Resistor", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "R", description = "Resistor layout symbol",
zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true)
public class Resistor extends AbstractLeadedComponent<Resistance> implements IGerberComponentSimple {
zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true,
enableDatasheet = true, datasheetCreationStepCount = 2)
public class Resistor extends AbstractLeadedComponent<Resistance>
implements IGerberComponentSimple, IDatasheetSupport {
private static final long serialVersionUID = 1L;
@@ -77,6 +79,41 @@ public class Resistor extends AbstractLeadedComponent<Resistance> implements IGe
this.borderColor = BORDER_COLOR;
}
public Resistor(String[] model) {
this();
applyModel(model);
}
// columns: family, power, body length, body width, shape, color code, body color, border color;
// the resistance is not part of a row because the body size depends only on family and power
@Override
public void applyModel(String[] model) {
setPowerNew(org.diylc.core.measures.Power.parseCapacitance(model[1]));
setLength(parseDatasheetSize(model[2]));
setWidth(parseDatasheetSize(model[3]));
setShape(parseDatasheetEnum(ResistorShape.values(), model[4]));
setColorCode(parseDatasheetEnum(ResistorColorCode.values(), model[5]));
setBodyColor(Color.decode(model[6]));
setBorderColor(Color.decode(model[7]));
}
// sizes are in mm unless a unit follows the number, as some families are specified in inches
private static Size parseDatasheetSize(String value) {
String[] parts = value.split(" ");
SizeUnit unit = parts.length > 1 ? SizeUnit.valueOf(parts[1]) : SizeUnit.mm;
return new Size(Double.parseDouble(parts[0]), unit);
}
private static <E extends Enum<E>> E parseDatasheetEnum(E[] values, String name) {
for (E value : values) {
if (value.toString().equalsIgnoreCase(name)) {
return value;
}
}
throw new IllegalArgumentException("Unknown datasheet value: " + name);
}
@Override
protected boolean supportsStandingMode() {
return true;
@@ -29,7 +29,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.CapacitorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -47,8 +46,7 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("'Diameter' is the body as seen from above; 'Height' is drawn only when 'Folded' lays the part on its side")
@ComponentDescriptor(schematicFactory = CapacitorSchematicFactory.class,
name = "Tantalum Capacitor", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Tantalum Capacitor", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C",
description = "Vertically mounted tantalum capacitor", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class)
@@ -28,7 +28,6 @@ import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.common.ObjectCache;
import org.diylc.schematic.InductorSchematicFactory;
import org.diylc.components.AbstractLeadedComponent;
import org.diylc.components.transform.SimpleComponentTransformer;
import org.diylc.core.CreationMethod;
@@ -42,8 +41,7 @@ import org.diylc.core.measures.Resistance;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(schematicFactory = InductorSchematicFactory.class,
name = "Inductor (Toroidal)", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Inductor (Toroidal)", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "L",
description = "Ferrite core toroidal inductor mounted vertically", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class)
@@ -30,7 +30,6 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.PotentiometerSchematicFactory;
import org.diylc.common.Display;
import org.diylc.common.IPlugInPort;
@@ -51,8 +50,7 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the third lug is 0.2in below it (0.4in for FLAT_XLARGE) and the wiper sits halfway between them, offset to the right by 0.2in for FLAT_SMALL and FLAT_XSMALL, 0.4in for FLAT_SMALL2 and FLAT_LARGE, 0.5in for FLAT_XLARGE, 0.1in for VERTICAL_OFFSET, 0.2in for VERTICAL_OFFSET_BIG_GAP and not at all for VERTICAL_INLINE. The other orientations rotate this around the anchor")
@ComponentDescriptor(schematicFactory = PotentiometerSchematicFactory.class,
name = "Trimmer Potentiometer", author = "Branislav Stojkovic", category = "Passive",
@ComponentDescriptor(name = "Trimmer Potentiometer", author = "Branislav Stojkovic", category = "Passive",
creationMethod = CreationMethod.SINGLE_CLICK, instanceNamePrefix = "VR",
description = "Various types of board mounted trimmer potentiometers", zOrder = IDIYComponent.COMPONENT,
transformer = TrimmerTransformer.class)
@@ -1,433 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.components.schematic;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.diylc.awt.StringUtils;
import org.diylc.common.Display;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.ObjectCache;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractTransparentComponent;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
/**
* Generic rectangular IC-style schematic symbol with configurable terminals on each of the four
* sides, similar to KiCad's generic symbol. Used as the default fallback representation for physical
* components that do not declare a dedicated schematic factory.
*
* @author Branislav Stojkovic
*/
@ComponentDescriptor(name = "Box", author = "DIYLC", category = "Schematic Symbols",
instanceNamePrefix = "U", description = "Generic schematic box with configurable terminals",
zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.SHOW_ALL_NAMES,
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Schematic")
public class SchematicBox extends AbstractTransparentComponent<String> {
private static final long serialVersionUID = 1L;
public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
public static Color BODY_COLOR = Color.white;
public static Color BORDER_COLOR = Color.black;
public static Color PIN_COLOR = Color.black;
private String leftNodes = "1,2";
private String rightNodes = "3,4";
private String topNodes = "";
private String bottomNodes = "";
private String value = "";
private Display display = Display.BOTH;
private Color bodyColor = BODY_COLOR;
private Color borderColor = BORDER_COLOR;
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
transient private List<String> nodeLabels;
public SchematicBox() {
super();
updateControlPoints();
}
private static List<String> parseNodes(String csv) {
List<String> result = new ArrayList<String>();
if (csv == null)
return result;
for (String token : csv.split(",")) {
String trimmed = token.trim();
if (!trimmed.isEmpty())
result.add(trimmed);
}
return result;
}
private int leftCount() {
return parseNodes(leftNodes).size();
}
private int rightCount() {
return parseNodes(rightNodes).size();
}
private int topCount() {
return parseNodes(topNodes).size();
}
private int bottomCount() {
return parseNodes(bottomNodes).size();
}
private double pinSpacing() {
return PIN_SPACING.convertToPixels();
}
private double bodyWidth() {
double spacing = pinSpacing();
int horizontal = Math.max(topCount(), bottomCount());
return Math.max((horizontal + 1) * spacing, 4 * spacing);
}
private double bodyHeight() {
double spacing = pinSpacing();
int vertical = Math.max(leftCount(), rightCount());
return Math.max((vertical + 1) * spacing, 2 * spacing);
}
/**
* Recomputes the pin geometry from the anchor (control point 0) and the configured node counts.
* Ordering: left side (top to bottom), right side (top to bottom), top side (left to right),
* bottom side (left to right).
*
* <p>
* This is called on construction and whenever the node lists change — <b>not</b> from
* {@link #setControlPoint}. During a drag the presenter translates every control point by the
* same delta (because {@link #canPointMoveFreely} is {@code false}), which already keeps the pins
* on the box edge, so recomputing there would double-apply the delta. When the pin count is
* unchanged the existing {@link Point2D} objects are mutated in place so references held by the
* drawing/selection code stay valid.
* </p>
*/
private void updateControlPoints() {
nodeLabels = null;
double spacing = pinSpacing();
double leadLength = spacing;
double firstPinX = controlPoints[0].getX();
double firstPinY = controlPoints[0].getY();
double bodyX = firstPinX + leadLength;
double bodyY = firstPinY - spacing;
double bodyW = bodyWidth();
double bodyH = bodyHeight();
List<Point2D> points = new ArrayList<Point2D>();
// left
for (int i = 0; i < leftCount(); i++) {
points.add(new Point2D.Double(firstPinX, bodyY + spacing * (i + 1)));
}
// right
for (int i = 0; i < rightCount(); i++) {
points.add(new Point2D.Double(bodyX + bodyW + leadLength, bodyY + spacing * (i + 1)));
}
// top
for (int i = 0; i < topCount(); i++) {
points.add(new Point2D.Double(bodyX + spacing * (i + 1), bodyY - leadLength));
}
// bottom
for (int i = 0; i < bottomCount(); i++) {
points.add(new Point2D.Double(bodyX + spacing * (i + 1), bodyY + bodyH + leadLength));
}
if (points.isEmpty()) {
points.add(new Point2D.Double(firstPinX, firstPinY));
}
// keep index 0 exactly where it was
points.set(0, new Point2D.Double(firstPinX, firstPinY));
if (controlPoints.length == points.size()) {
for (int i = 0; i < points.size(); i++) {
controlPoints[i].setLocation(points.get(i));
}
} else {
controlPoints = points.toArray(new Point2D[0]);
}
}
private List<String> getNodeLabels() {
if (nodeLabels == null) {
nodeLabels = new ArrayList<String>();
nodeLabels.addAll(parseNodes(leftNodes));
nodeLabels.addAll(parseNodes(rightNodes));
nodeLabels.addAll(parseNodes(topNodes));
nodeLabels.addAll(parseNodes(bottomNodes));
}
return nodeLabels;
}
private Rectangle2D getBodyRectangle() {
double spacing = pinSpacing();
double leadLength = spacing;
double bodyX = controlPoints[0].getX() + leadLength;
double bodyY = controlPoints[0].getY() - spacing;
return new Rectangle2D.Double(bodyX, bodyY, bodyWidth(), bodyHeight());
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
if (checkPointsClipped(g2d.getClip()))
return;
Composite oldComposite = applyAlpha(g2d, componentState);
Rectangle2D body = getBodyRectangle();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(body);
g2d.setComposite(oldComposite);
Color finalBorder =
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
: borderColor;
g2d.setColor(finalBorder);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.draw(body);
// pins
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
? SELECTION_COLOR
: PIN_COLOR);
for (int i = 0; i < controlPoints.length; i++) {
Point2D p = controlPoints[i];
Point2D edge = nearestEdgePoint(p, body);
g2d.drawLine((int) Math.round(p.getX()), (int) Math.round(p.getY()), (int) Math.round(edge.getX()),
(int) Math.round(edge.getY()));
}
// pin labels
Font smallFont = project.getFont().deriveFont((float) (project.getFont().getSize() * 0.8));
g2d.setFont(smallFont);
List<String> labels = getNodeLabels();
for (int i = 0; i < controlPoints.length && i < labels.size(); i++) {
Point2D edge = nearestEdgePoint(controlPoints[i], body);
double lx = edge.getX();
double ly = edge.getY();
// nudge label inward
if (Math.abs(edge.getX() - body.getMinX()) < 1)
lx += smallFont.getSize();
else if (Math.abs(edge.getX() - body.getMaxX()) < 1)
lx -= smallFont.getSize();
if (Math.abs(edge.getY() - body.getMinY()) < 1)
ly += smallFont.getSize() * 0.7;
else if (Math.abs(edge.getY() - body.getMaxY()) < 1)
ly -= smallFont.getSize() * 0.7;
StringUtils.drawCenteredText(g2d, labels.get(i), lx, ly, HorizontalAlignment.CENTER,
VerticalAlignment.CENTER);
}
// body label
g2d.setColor(componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
? LABEL_COLOR_SELECTED
: LABEL_COLOR);
g2d.setFont(project.getFont());
String label = getLabelForDisplay();
if (label != null && !label.isEmpty()) {
StringUtils.drawCenteredText(g2d, label, body.getCenterX(), body.getCenterY(),
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
}
private String getLabelForDisplay() {
switch (display) {
case NAME:
return getName();
case VALUE:
return value;
case NONE:
return "";
case BOTH:
default:
return (getName() == null ? "" : getName()) + (value == null || value.isEmpty() ? "" : " " + value);
}
}
private static Point2D nearestEdgePoint(Point2D p, Rectangle2D body) {
double x = p.getX();
double y = p.getY();
if (x <= body.getMinX())
return new Point2D.Double(body.getMinX(), clamp(y, body.getMinY(), body.getMaxY()));
if (x >= body.getMaxX())
return new Point2D.Double(body.getMaxX(), clamp(y, body.getMinY(), body.getMaxY()));
if (y <= body.getMinY())
return new Point2D.Double(clamp(x, body.getMinX(), body.getMaxX()), body.getMinY());
return new Point2D.Double(clamp(x, body.getMinX(), body.getMaxX()), body.getMaxY());
}
private static double clamp(double v, double min, double max) {
return Math.max(min, Math.min(max, v));
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
int margin = 4 * width / 32;
g2d.setColor(BODY_COLOR);
g2d.fillRect(margin * 2, margin, width - margin * 4, height - margin * 2);
g2d.setColor(BORDER_COLOR);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.drawRect(margin * 2, margin, width - margin * 4, height - margin * 2);
g2d.drawLine(0, height / 3, margin * 2, height / 3);
g2d.drawLine(0, height * 2 / 3, margin * 2, height * 2 / 3);
g2d.drawLine(width - margin * 2, height / 2, width, height / 2);
}
@Override
public int getControlPointCount() {
return controlPoints.length;
}
@Override
public Point2D getControlPoint(int index) {
return controlPoints[index];
}
@Override
public void setControlPoint(Point2D point, int index) {
// Move only the requested point. The presenter drags every control point of the box together
// (canPointMoveFreely == false), so the pins stay on the edge without a recompute here.
controlPoints[index].setLocation(point);
}
@Override
public boolean isControlPointSticky(int index) {
return true;
}
@Override
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
return VisibilityPolicy.WHEN_SELECTED;
}
@Override
public boolean canPointMoveFreely(int pointIndex) {
return false;
}
@Override
public String getControlPointNodeName(int index) {
List<String> labels = getNodeLabels();
return index < labels.size() ? labels.get(index) : Integer.toString(index + 1);
}
@EditableProperty(name = "Left Nodes")
public String getLeftNodes() {
return leftNodes;
}
public void setLeftNodes(String leftNodes) {
this.leftNodes = leftNodes;
updateControlPoints();
}
@EditableProperty(name = "Right Nodes")
public String getRightNodes() {
return rightNodes;
}
public void setRightNodes(String rightNodes) {
this.rightNodes = rightNodes;
updateControlPoints();
}
@EditableProperty(name = "Top Nodes")
public String getTopNodes() {
return topNodes;
}
public void setTopNodes(String topNodes) {
this.topNodes = topNodes;
updateControlPoints();
}
@EditableProperty(name = "Bottom Nodes")
public String getBottomNodes() {
return bottomNodes;
}
public void setBottomNodes(String bottomNodes) {
this.bottomNodes = bottomNodes;
updateControlPoints();
}
@EditableProperty
public Display getDisplay() {
return display;
}
public void setDisplay(Display display) {
this.display = display;
}
@EditableProperty(name = "Body")
public Color getBodyColor() {
return bodyColor;
}
public void setBodyColor(Color bodyColor) {
this.bodyColor = bodyColor;
}
@EditableProperty(name = "Border")
public Color getBorderColor() {
return borderColor;
}
public void setBorderColor(Color borderColor) {
this.borderColor = borderColor;
}
@EditableProperty
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
}
@@ -30,16 +30,22 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.Arrays;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.awt.StringUtils;
import org.diylc.common.Display;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractLabeledComponent;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.components.transform.DIL_ICTransformer;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDatasheetSupport;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.Theme;
@@ -55,11 +61,15 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the first row of pins runs down from it 'Pin Spacing' apart, the second row sits 'Row Spacing' to the right and the notch is at the anchor's end. The other orientations rotate this around the anchor")
@AiNote("'Pin Names' is a comma-separated list of pin names in pin-number order, starting with pin 1 next to the notch; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package, e.g. GND_1 and GND_2, while 1IN- and 2IN- are inputs of two separate sections")
@AiNote("A datasheet model is a part number such as TL072 or NE555 and sets 'Pins', 'Row Spacing', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model")
@ComponentDescriptor(name = "DIP IC", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "IC", description = "Dual-in-line package IC",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE,
transformer = DIL_ICTransformer.class, enableCache = true)
public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberComponentSimple {
transformer = DIL_ICTransformer.class, enableCache = true, enableDatasheet = true,
datasheetCreationStepCount = 2, datasheetModelColumn = 1)
public class DIL_IC extends AbstractLabeledComponent<String>
implements IGerberComponentSimple, IDatasheetSupport {
private static final long serialVersionUID = 1L;
@@ -72,7 +82,9 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
public static int EDGE_RADIUS = 6;
public static Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
public static Size INDENT_SIZE = new Size(0.07d, SizeUnit.in);
public static DisplayNumbers DISPLAY_NUMBERS = DisplayNumbers.NO;
public static DisplayNumbers DISPLAY_NUMBERS = DisplayNumbers.NAMES;
// the smaller font lets names sit closer to their pins than on a maker board
public static Size PIN_NAME_OFFSET = new Size(1.35d, SizeUnit.mm);
private String value = "";
private Orientation orientation = Orientation.DEFAULT;
@@ -86,6 +98,7 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
private Color labelColor = LABEL_COLOR;
private Color indentColor = INDENT_COLOR;
private DisplayNumbers displayNumbers = DISPLAY_NUMBERS;
private String pinNames = null;
// new Point(0, pinSpacing.convertToPixels()),
// new Point(0, 2 * pinSpacing.convertToPixels()),
// new Point(0, 3 * pinSpacing.convertToPixels()),
@@ -97,12 +110,29 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
// new Point(3 * pinSpacing.convertToPixels(), 3 *
// pinSpacing.convertToPixels()) };
transient private Area[] body;
transient private String[] parsedPinNames;
public DIL_IC() {
super();
updateControlPoints();
}
public DIL_IC(String[] model) {
this();
applyModel(model);
}
// columns: group, part number, row spacing, then one name per pin in pin-number order, so the
// pin count is the number of names
@Override
public void applyModel(String[] model) {
setValue(model[1]);
setRowSpacing(Size.parseSize(model[2]));
setPinCount(PinCount.valueOf("_" + (model.length - 3)));
setPinNames(String.join(",", Arrays.copyOfRange(model, 3, model.length)));
}
@EditableProperty
public String getValue() {
return value;
@@ -425,9 +455,14 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
}
}
if (getDisplayNumbers() == DisplayNumbers.NAMES) {
drawPinNames(g2d);
}
// draw pin numbers
if (getDisplayNumbers() != DisplayNumbers.NO) {
if (getDisplayNumbers() != DisplayNumbers.NO && getDisplayNumbers() != DisplayNumbers.NAMES) {
int pinSize = (int) PIN_SIZE.convertToPixels();
g2d.setFont(project.getFont().deriveFont((float) (project.getFont().getSize2D() * 0.66)));
for (int i = 0; i < controlPoints.length; i++) {
Point2D point = controlPoints[i];
@@ -454,16 +489,68 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
textY2 = textY2 - pinSize;
}
g2d.setFont(project.getFont().deriveFont((float) (project.getFont().getSize2D() * 0.66)));
String pinNumber = Integer.toString(getPinNumber(i));
if (i >= pinCount.getValue() / 2) {
g2d.drawString(getControlPointNodeName(i), textX1, textY1);
g2d.drawString(pinNumber, textX1, textY1);
} else {
g2d.drawString(getControlPointNodeName(i), textX2, textY2);
g2d.drawString(pinNumber, textX2, textY2);
}
}
}
}
// Names are printed the way maker boards print theirs: centred just inside the body from their
// pin and running along the row, which leaves the middle of even a 0.3 in body to the name/value
// label. Each name has one pin pitch along the row, so a name that would touch the previous one
// in its row or run past the end of the body is left out rather than overprinted.
private void drawPinNames(Graphics2D g2d) {
g2d.setFont(ICPinNames.PIN_NAME_FONT);
FontMetrics fontMetrics = g2d.getFontMetrics(ICPinNames.PIN_NAME_FONT);
double labelOffset = PIN_NAME_OFFSET.convertToPixels();
double pitch = pinSpacing.convertToPixels();
int half = pinCount.getValue() / 2;
Point2D firstRow = controlPoints[0];
Point2D secondRow = controlPoints[half];
double rowDistance = firstRow.distance(secondRow);
double acrossX = (secondRow.getX() - firstRow.getX()) / rowDistance;
double acrossY = (secondRow.getY() - firstRow.getY()) / rowDistance;
boolean rowsVertical = Math.abs(acrossX) > Math.abs(acrossY);
// along the row the body reaches half a pitch past the first and the last pin
double bodyStart = -pitch / 2 + 1;
double bodyEnd = (half - 0.5) * pitch - 1;
double previousEnd = bodyStart - ICPinNames.PIN_NAME_GAP;
for (int i = 0; i < controlPoints.length; i++) {
if (i == half) {
previousEnd = bodyStart - ICPinNames.PIN_NAME_GAP;
}
String name = getPinName(getPinNumber(i));
if (name == null) {
continue;
}
String label = AbstractMakerBoard.getDisplayPinLabel(name);
double alongRow = (i % half) * pitch;
double halfWidth = fontMetrics.stringWidth(label) / 2d;
if (alongRow - halfWidth < previousEnd + ICPinNames.PIN_NAME_GAP || alongRow + halfWidth > bodyEnd) {
continue;
}
previousEnd = alongRow + halfWidth;
double dirX = i < half ? acrossX : -acrossX;
double dirY = i < half ? acrossY : -acrossY;
double x = controlPoints[i].getX() + dirX * labelOffset;
double y = controlPoints[i].getY() + dirY * labelOffset;
AffineTransform oldTransform = g2d.getTransform();
// the two rows are mirrored so each name reads away from its pin, as on a maker board; rows
// that run horizontally keep their names level so that neither reads upside down
if (rowsVertical) {
g2d.rotate(dirX > 0 ? Math.PI / 2 : -Math.PI / 2, x, y);
}
StringUtils.drawCenteredText(g2d, label, x, y, HorizontalAlignment.CENTER,
VerticalAlignment.CENTER);
g2d.setTransform(oldTransform);
}
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
int radius = 6 * width / 32;
@@ -527,7 +614,7 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
this.indentColor = indentColor;
}
@EditableProperty(name = "Display Pin #s")
@EditableProperty(name = "Pin Labels")
public DisplayNumbers getDisplayNumbers() {
if (displayNumbers == null) {
displayNumbers = DisplayNumbers.NO;
@@ -539,6 +626,42 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
this.displayNumbers = numbers;
}
@EditableProperty(name = "Pin Names")
public String getPinNames() {
return pinNames;
}
public void setPinNames(String pinNames) {
this.pinNames = pinNames;
parsedPinNames = null;
}
// the physical pin number of a control point; control points run down the first row and then
// down the second, whereas DIP numbering comes back up the second row
private int getPinNumber(int index) {
int pinCount = this.pinCount.getValue();
int half = pinCount / 2;
int position = index + 1;
boolean secondRow = position > half;
switch (getDisplayNumbers()) {
case CONNECTOR:
return secondRow ? 2 * (position - half) : 2 * position - 1;
case DIP_MIRROR:
return secondRow ? position - half : pinCount - position + 1;
case CONNECTOR_MIRROR:
return secondRow ? 2 * (position - half) - 1 : 2 * position;
default:
return secondRow ? pinCount + half - position + 1 : position;
}
}
private String getPinName(int pinNumber) {
if (parsedPinNames == null) {
parsedPinNames = ICPinNames.parse(getPinNames());
}
return ICPinNames.get(parsedPinNames, pinNumber);
}
@Override
public boolean canPointMoveFreely(int pointIndex) {
return false;
@@ -569,35 +692,9 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
@Override
public String getControlPointNodeName(int index) {
index++;
if (displayNumbers == DisplayNumbers.DIP) {
if (index > pinCount.getValue() / 2) {
return Integer.toString(pinCount.getValue() * 3 / 2 - index + 1);
} else {
return Integer.toString(index);
}
} else if (displayNumbers == DisplayNumbers.CONNECTOR) {
if (index > pinCount.getValue() / 2) {
return Integer.toString(index - pinCount.getValue() + index);
} else {
return Integer.toString(index * 2 - 1);
}
} else if (displayNumbers == DisplayNumbers.DIP_MIRROR) {
if (index > pinCount.getValue() / 2) {
return Integer.toString((index - pinCount.getValue() / 2));
} else {
return Integer.toString(pinCount.getValue() - index + 1);
}
} else if (displayNumbers == DisplayNumbers.CONNECTOR_MIRROR) {
if (index > pinCount.getValue() / 2) {
return Integer.toString(index - pinCount.getValue() + index - 1);
} else {
return Integer.toString(2 * index);
}
}
return Integer.toString(index);
int pinNumber = getPinNumber(index);
String name = getPinName(pinNumber);
return name == null ? Integer.toString(pinNumber) : name;
}
public static enum PinCount {
@@ -616,8 +713,8 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
public enum DisplayNumbers {
NO("No"), DIP("DIP"), CONNECTOR("Connector"), DIP_MIRROR("DIP (Mirrored)"), CONNECTOR_MIRROR(
"Connector (Mirrored)");
NO("None"), NAMES("Names"), DIP("DIP"), CONNECTOR("Connector"), DIP_MIRROR("DIP (Mirrored)"),
CONNECTOR_MIRROR("Connector (Mirrored)");
private String label;
@@ -26,7 +26,6 @@ import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.DiodeSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.components.AbstractLeadedComponent;
@@ -44,8 +43,7 @@ import org.diylc.utils.Constants;
@AiNote("The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing")
@AiNote("If the control points are closer together than 'Length' (0.2in by default) the part is drawn standing, its body over the first point, or over the second with 'Reverse (standing)'; to keep it lying on a tighter footprint, reduce 'Length'")
@ComponentDescriptor(schematicFactory = DiodeSchematicFactory.class,
name = "Diode (glass)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Diode (glass)", author = "Branislav Stojkovic", category = "Semiconductors",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "D",
description = "Glass diode, like most small signal diodes.", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class, enableCache = true)
@@ -26,7 +26,6 @@ import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.DiodeSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.components.AbstractLeadedComponent;
@@ -44,8 +43,7 @@ import org.diylc.utils.Constants;
@AiNote("The two control points are the holes the leads go into and the body is centred between them; 'Length' is the body length, not the hole spacing")
@AiNote("If the control points are closer together than 'Length' (0.25in by default) the part is drawn standing, its body over the first point; to keep it lying on a tighter footprint, reduce 'Length'")
@ComponentDescriptor(schematicFactory = DiodeSchematicFactory.class,
name = "Diode (plastic)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Diode (plastic)", author = "Branislav Stojkovic", category = "Semiconductors",
creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "D",
description = "Plastic diode, like most rectifier, zener, schottky, etc.", zOrder = IDIYComponent.COMPONENT,
transformer = SimpleComponentTransformer.class, enableCache = true)
@@ -0,0 +1,100 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.components.semiconductors;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.components.AbstractMakerBoard;
/**
* Pin names shared by the IC packages. A package keeps its names as one comma-separated string in
* pin-number order, pin 1 first, so the names survive a change of pin count and read back as typed.
*/
public final class ICPinNames {
// a size below the maker boards' pin font, as IC pin names are longer than a board's silkscreen
// and get only one pin pitch each
public static Font PIN_NAME_FONT = new Font(AbstractMakerBoard.SILK_FONT_FAMILY, Font.PLAIN, 6);
public static int PIN_NAME_GAP = 2;
private ICPinNames() {}
public static String[] parse(String pinNames) {
return pinNames == null ? new String[0] : pinNames.split(",", -1);
}
/**
* @param names names as returned by {@link #parse(String)}
* @param pinNumber 1-based pin number
* @return the trimmed name of the pin, or null if it has none
*/
public static String get(String[] names, int pinNumber) {
if (pinNumber < 1 || pinNumber > names.length) {
return null;
}
String name = names[pinNumber - 1].trim();
return name.isEmpty() ? null : name;
}
/**
* Draws a pin label outside the package, starting just beyond whichever reaches further in the
* given direction, the body or the pin itself, and reading away from the package. The direction
* is one of the four axis directions; text pointing left or down is right-aligned so that it never
* reads upside down.
*
* @param pin the pin the label belongs to
* @param dirX x component of the unit direction away from the package
* @param dirY y component of the unit direction away from the package
* @param bodyBounds bounds of the package body
* @param pinRadius half the drawn pin size
*/
public static void drawOutside(Graphics2D g2d, String text, Point2D pin, double dirX, double dirY,
Rectangle2D bodyBounds, double pinRadius) {
boolean horizontal = Math.abs(dirX) > Math.abs(dirY);
double pinReach = (horizontal ? pin.getX() * dirX : pin.getY() * dirY) + pinRadius;
double bodyReach = horizontal ? (dirX > 0 ? bodyBounds.getMaxX() : -bodyBounds.getMinX())
: (dirY > 0 ? bodyBounds.getMaxY() : -bodyBounds.getMinY());
double start = Math.max(pinReach, bodyReach) + PIN_NAME_GAP;
double x = horizontal ? start * dirX : pin.getX();
double y = horizontal ? pin.getY() : start * dirY;
FontMetrics fontMetrics = g2d.getFontMetrics();
int width = fontMetrics.stringWidth(text);
float baseline = (fontMetrics.getAscent() - fontMetrics.getDescent()) / 2f;
AffineTransform oldTransform = g2d.getTransform();
g2d.translate(x, y);
double forward;
if (horizontal) {
forward = dirX;
} else {
g2d.rotate(-Math.PI / 2);
forward = -dirY;
}
g2d.drawString(text, forward > 0 ? 0 : -width, baseline);
g2d.setTransform(oldTransform);
}
}
@@ -30,16 +30,20 @@ import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.*;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.components.AbstractTransparentComponent;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDatasheetSupport;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.AiNote;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.KeywordPolicy;
@@ -48,11 +52,16 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("The leads are staggered in two rows and numbered alternately along the package: pin 1 is the first control point, pins 1, 3, 5, ... form the row bent forward in front of the body, away from the heatsink tab, and pins 2, 4, ... the row that leaves the body from underneath. 'Type' picks the package: Multiwatt 11 for LM3886 or TDA7265, Multiwatt 15 for TDA7293 or TDA7294")
@AiNote("'Pin Names' is a comma-separated list of pin names in pin-number order; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package")
@AiNote("A datasheet model is a part number such as LM3886 and sets 'Type', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model")
@ComponentDescriptor(name = "Multiwatt IC", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "IC", description = "Multiwatt package IC typically used for audio amplifiers of high power applications",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE,
enableCache = true)
public class MultiwattIC extends AbstractTransparentComponent<String> implements IGerberComponentSimple {
enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2,
datasheetModelColumn = 1)
public class MultiwattIC extends AbstractTransparentComponent<String>
implements IGerberComponentSimple, IDatasheetSupport {
private static final long serialVersionUID = 1L;
@@ -67,12 +76,11 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
public static Size PIN_SIZE = new Size(0.7d, SizeUnit.mm);
public static Size THICKNESS = new Size(5d, SizeUnit.mm);
public static Size TAB_THICKNESS = new Size(1.6d, SizeUnit.mm);
// Multiwatt packages use 0.1 inch (2.54mm) spacing between pins in the same row
public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
// from the back of the tab to the lead row that leaves the body from underneath; the other row is
// bent forward clear of the body
public static Size BACK_TO_NEAR_ROW = new Size(4.55d, SizeUnit.mm);
// Multiwatt packages have staggered pins with 0.2 inch (5.08mm) row spacing
public static Size ROW_SPACING = new Size(0.2d, SizeUnit.in);
// Lead offset due to bent leads
public static Size LEAD_OFFSET = new Size(4d, SizeUnit.mm);
private String value = "";
private Orientation orientation = Orientation.DEFAULT;
@@ -84,7 +92,10 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
private Color labelColor = LABEL_COLOR;
private Color tabColor = TAB_COLOR;
private Color tabBorderColor = TAB_BORDER_COLOR;
private String pinNames = null;
private PinLabels pinLabels = null;
transient private Area[] body;
transient private String[] parsedPinNames;
public MultiwattIC() {
super();
@@ -92,6 +103,27 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
this.alphaPercent = new Percentage(100);
}
public MultiwattIC(String[] model) {
this();
applyModel(model);
}
// columns: group, part number, then one name per pin in pin-number order; the pin count picks
// the package type
@Override
public void applyModel(String[] model) {
int pinCount = model.length - 2;
MultiwattType modelType = Arrays.stream(MultiwattType.values())
.filter(t -> t.getPinCount() == pinCount)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No Multiwatt package with " + pinCount
+ " pins"));
setValue(model[1]);
setType(modelType);
setPinNames(String.join(",", Arrays.copyOfRange(model, 2, model.length)));
}
@EditableProperty
public String getValue() {
return value;
@@ -175,7 +207,7 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
int pinsRight = type.getPinsRight();
controlPoints = new Point2D.Double[pinCount];
controlPoints[0] = firstPoint;
double pinSpacing = PIN_SPACING.convertToPixels();
double pinSpacing = type.getRowPitch().convertToPixels();
double rowSpacing = ROW_SPACING.convertToPixels();
// Update control points for staggered layout
double dx1, dy1, dx2, dy2;
@@ -234,91 +266,68 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
public Area[] getBody() {
if (body == null) {
body = new Area[2];
double x = controlPoints[0].getX();
double y = controlPoints[0].getY();
double pinSize = PIN_SIZE.convertToPixels();
double pinSpacing = PIN_SPACING.convertToPixels();
double rowSpacing = ROW_SPACING.convertToPixels();
double tabThickness = TAB_THICKNESS.convertToPixels();
double leadOffset = LEAD_OFFSET.convertToPixels();
double pinSpacing = type.getRowPitch().convertToPixels();
int pinsLeft = type.getPinsLeft();
int pinsRight = type.getPinsRight();
// Calculate height accounting for staggered pins (right row offset by half spacing)
double leftRowHeight = (pinsLeft - 1) * pinSpacing;
double rightRowHeight = (pinsRight - 1 + 0.5) * pinSpacing; // Account for half-spacing offset
double maxHeight = Math.max(leftRowHeight, rightRowHeight) + pinSpacing; // Add spacing for last pin
double bodyX, bodyY, bodyWidth, bodyHeight;
double tabX, tabY, tabWidth, tabHeight;
switch (orientation) {
case DEFAULT:
bodyWidth = rowSpacing - pinSize;
bodyHeight = maxHeight;
// Offset body to the left by lead offset (bent leads)
bodyX = x + pinSize / 2 - leadOffset;
bodyY = y - pinSpacing / 2;
// Tab on the left side (where there are more pins) inside the body bounds
tabX = bodyX;
tabY = bodyY;
tabWidth = tabThickness;
tabHeight = bodyHeight;
break;
case _90:
bodyWidth = maxHeight;
bodyHeight = rowSpacing - pinSize;
// Body positioned between the two pin rows
// Left pins extend to negative X, body starts from leftmost pin
// For _90: left pins go left (negative X), so leftmost is at x - (pinsLeft-1)*pinSpacing
// Offset body upward by lead offset (bent leads)
bodyX = x - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
bodyY = y + pinSize / 2 - leadOffset;
// Tab on the top side (where there are more pins - left row at y) inside the body bounds
tabX = bodyX;
tabY = bodyY;
tabWidth = bodyWidth;
tabHeight = tabThickness;
break;
case _180:
bodyWidth = rowSpacing - pinSize;
bodyHeight = maxHeight;
// Body positioned between the two pin rows
// For _180: left pins go up (negative Y), right pins at x - rowSpacing
// Left row is at x, right row is at x - rowSpacing
// Pins extend upward from y, so body should start from top of pin array
// Offset body to the right by lead offset (bent leads)
bodyX = x - rowSpacing + pinSize / 2 + leadOffset;
bodyY = y - (pinsLeft - 1) * pinSpacing - pinSpacing / 2;
// Tab on the right side (where there are more pins - left row at x) inside the body bounds
tabX = bodyX + bodyWidth - tabThickness;
tabY = bodyY;
tabWidth = tabThickness;
tabHeight = bodyHeight;
break;
case _270:
bodyWidth = maxHeight;
bodyHeight = rowSpacing - pinSize;
// Body positioned between the two pin rows
// For _270: left pins go right (positive X), so leftmost is at x
// Left row is at y, right row is at y - rowSpacing
// Similar to _90 but mirrored
// Offset body downward by lead offset (bent leads)
bodyX = x - pinSpacing / 2;
bodyY = y - rowSpacing + pinSize / 2 + leadOffset;
// Tab on the bottom side (where there are more pins - left row at y) inside the body bounds
tabX = bodyX;
tabY = bodyY + bodyHeight - tabThickness;
tabWidth = bodyWidth;
tabHeight = tabThickness;
break;
default:
throw new RuntimeException("Unexpected orientation: " + orientation);
}
body[0] = new Area(new Rectangle2D.Double(bodyX, bodyY, bodyWidth, bodyHeight));
body[1] = new Area(new Rectangle2D.Double(tabX, tabY, tabWidth, tabHeight));
double rightRowHeight = (pinsRight - 1 + 0.5) * pinSpacing;
double length = Math.max(leftRowHeight, rightRowHeight) + pinSpacing;
double start = -pinSpacing / 2;
double back = ROW_SPACING.convertToPixels() + BACK_TO_NEAR_ROW.convertToPixels();
body[0] = new Area(
toDrawing(start, start + length, back - THICKNESS.convertToPixels(), back));
body[1] = new Area(
toDrawing(start, start + length, back - TAB_THICKNESS.convertToPixels(), back));
}
return body;
}
// The rectangle spanning the given distances along the rows and across them, measured from the
// first pin with the second row on the positive side, turned to the orientation.
private Rectangle2D toDrawing(double alongFrom, double alongTo, double acrossFrom,
double acrossTo) {
double alongX;
double alongY;
double acrossX;
double acrossY;
switch (orientation) {
case DEFAULT:
alongX = 0;
alongY = 1;
acrossX = 1;
acrossY = 0;
break;
case _90:
alongX = -1;
alongY = 0;
acrossX = 0;
acrossY = 1;
break;
case _180:
alongX = 0;
alongY = -1;
acrossX = -1;
acrossY = 0;
break;
case _270:
alongX = 1;
alongY = 0;
acrossX = 0;
acrossY = -1;
break;
default:
throw new RuntimeException("Unexpected orientation: " + orientation);
}
Point2D first = controlPoints[0];
double x1 = first.getX() + alongX * alongFrom + acrossX * acrossFrom;
double y1 = first.getY() + alongY * alongFrom + acrossY * acrossFrom;
double x2 = first.getX() + alongX * alongTo + acrossX * acrossTo;
double y2 = first.getY() + alongY * alongTo + acrossY * acrossTo;
return new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1),
Math.abs(y2 - y1));
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
@@ -337,20 +346,8 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
double pinX = point.getX();
double pinY = point.getY();
// Check if pin is inside the body
boolean insideBody = mainArea.contains(pinX, pinY);
if (insideBody) {
// Draw pin as square inside body
Rectangle2D rect = new Rectangle2D.Double(pinX - pinSize / 2,
pinY - pinSize / 2, pinSize, pinSize);
g2d.setColor(PIN_COLOR);
drawingObserver.startTracking();
g2d.fill(rect);
drawingObserver.stopTracking();
g2d.setColor(PIN_BORDER_COLOR);
g2d.draw(rect);
} else {
// pins under the body are drawn on top of it further down
if (!mainArea.contains(pinX, pinY)) {
// Draw rectangular lead from body edge to pin position
double bodyX = bodyBounds.getX();
double bodyY = bodyBounds.getY();
@@ -460,6 +457,25 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
g2d.setColor(finalBorderColor);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.draw(mainArea);
// the near row leaves through the bottom of the body, so its pins are drawn over the body to
// stay visible whatever its alpha
if (!outlineMode) {
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
for (Point2D point : controlPoints) {
if (mainArea.contains(point.getX(), point.getY())) {
Rectangle2D rect = new Rectangle2D.Double(point.getX() - pinSize / 2,
point.getY() - pinSize / 2, pinSize, pinSize);
g2d.setColor(PIN_COLOR);
drawingObserver.startTracking();
g2d.fill(rect);
drawingObserver.stopTracking();
g2d.setColor(PIN_BORDER_COLOR);
g2d.draw(rect);
}
}
}
// Draw label.
g2d.setFont(project.getFont());
Color finalLabelColor;
@@ -492,6 +508,44 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
int x = (int) (bounds.getX() + (bounds.width - textWidth) / 2);
int y = (int) (bounds.getY() + (bounds.height - textHeight) / 2 + fontMetrics.getAscent());
g2d.drawString(label, x, y);
if (getPinLabels() != PinLabels.NONE) {
g2d.setColor(finalBorderColor);
drawPinLabels(g2d, mainArea.getBounds2D());
}
}
// Labels read forward, square to the rows, away from the tab. A pin under the body is labelled
// from the front edge of the body, in the gap between the two forward leads either side of it;
// a forward pin past the end of its lead. The stagger keeps the two sets apart.
private void drawPinLabels(Graphics2D g2d, Rectangle2D bodyBounds) {
g2d.setFont(ICPinNames.PIN_NAME_FONT);
int pinsLeft = type.getPinsLeft();
double acrossX = controlPoints[pinsLeft].getX() - controlPoints[0].getX();
double acrossY = controlPoints[pinsLeft].getY() - controlPoints[0].getY();
// the stagger adds half a pitch along the row, so the row offset is the larger component
if (Math.abs(acrossX) > Math.abs(acrossY)) {
acrossX = Math.signum(acrossX);
acrossY = 0;
} else {
acrossX = 0;
acrossY = Math.signum(acrossY);
}
double pinRadius = PIN_SIZE.convertToPixels() / 2;
for (int i = 0; i < controlPoints.length; i++) {
String label;
if (getPinLabels() == PinLabels.NUMBERS) {
label = Integer.toString(getPinNumber(i));
} else {
String name = getPinName(getPinNumber(i));
if (name == null) {
continue;
}
label = AbstractMakerBoard.getDisplayPinLabel(name);
}
ICPinNames.drawOutside(g2d, label, controlPoints[i], -acrossX, -acrossY, bodyBounds,
pinRadius);
}
}
@Override
@@ -500,7 +554,7 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
int pinSize = 2 * width / 32;
int leftPinX = width / 6 + 9; // Move left pins 9px to the right (4px + 3px + 2px)
int rightPinX = 5 * width / 6;
int leadOffset = (int) (LEAD_OFFSET.convertToPixels() * width / 100); // Scale lead offset for icon
int leadOffset = (int) (BACK_TO_NEAR_ROW.convertToPixels() * width / 100); // Scale lead offset for icon
int bodyX = leftPinX + pinSize - leadOffset + 2; // Offset body to the left, then move 2px to the right
int bodyWidth = (rightPinX - bodyX - pinSize) / 2 + 2; // Thinner body, make 2px thicker
int bodyHeight = height - 2 * margin;
@@ -508,13 +562,13 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
// Calculate pin spacing to use all vertical space
// Use same spacing for both rows, with front row offset by half spacing
int backPins = 4;
int frontPins = 3;
// Use the spacing that fits the back row (more pins)
int pinSpacing = (bodyHeight - pinSize) / (backPins - 1);
int backStartY = margin + pinSize / 2 + 1; // Move pins 1px down
// Front pins start with half spacing offset for staggered effect
int frontStartY = margin + pinSize / 2 + pinSpacing / 2 + 1; // Move pins 1px down
// the forward row, bent clear of the body, is the one with more pins
int backPins = 3;
int frontPins = 4;
int pinSpacing = (bodyHeight - pinSize) / (frontPins - 1);
int frontStartY = margin + pinSize / 2 + 1; // Move pins 1px down
// Back pins start with half spacing offset for staggered effect
int backStartY = margin + pinSize / 2 + pinSpacing / 2 + 1; // Move pins 1px down
// Draw thin body between pin rows
g2d.setColor(BODY_COLOR);
@@ -522,14 +576,14 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
g2d.setColor(BORDER_COLOR);
g2d.drawRect(bodyX, bodyY, bodyWidth, bodyHeight);
// Draw heat sink tab on left side (where there are more pins)
// Draw heat sink tab on left side, away from the forward row
int tabWidth = pinSize * 2;
g2d.setColor(TAB_COLOR);
g2d.fillRect(bodyX, bodyY, tabWidth, bodyHeight);
g2d.setColor(TAB_BORDER_COLOR);
g2d.drawRect(bodyX, bodyY, tabWidth, bodyHeight);
// Draw 4 pins in the back (left row, more pins) as squares - inside body
// Draw 3 pins in the back as squares - inside body
g2d.setColor(PIN_COLOR);
for (int i = 0; i < backPins; i++) {
int pinY = backStartY + i * pinSpacing - pinSize / 2;
@@ -539,7 +593,7 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
g2d.setColor(PIN_COLOR);
}
// Draw 3 pins in the front (right row, fewer pins) with leads going left to body
// Draw 4 pins in the front with leads going left to body
// Use same spacing as back row, just offset by half spacing
g2d.setColor(PIN_COLOR);
for (int i = 0; i < frontPins; i++) {
@@ -628,6 +682,49 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
this.tabBorderColor = tabBorderColor;
}
@EditableProperty(name = "Pin Names")
public String getPinNames() {
return pinNames;
}
public void setPinNames(String pinNames) {
this.pinNames = pinNames;
parsedPinNames = null;
}
@EditableProperty(name = "Pin Labels")
public PinLabels getPinLabels() {
if (pinLabels == null) {
pinLabels = PinLabels.NAMES;
}
return pinLabels;
}
public void setPinLabels(PinLabels pinLabels) {
this.pinLabels = pinLabels;
}
// the leads alternate between the rows along the package, so the forward row holds the odd pin
// numbers and the row under the body the even ones
private int getPinNumber(int index) {
int pinsLeft = type.getPinsLeft();
return index < pinsLeft ? 2 * index + 1 : 2 * (index - pinsLeft) + 2;
}
private String getPinName(int pinNumber) {
if (parsedPinNames == null) {
parsedPinNames = ICPinNames.parse(getPinNames());
}
return ICPinNames.get(parsedPinNames, pinNumber);
}
@Override
public String getControlPointNodeName(int index) {
int pinNumber = getPinNumber(index);
String name = getPinName(pinNumber);
return name == null ? Integer.toString(pinNumber) : name;
}
@Override
public Rectangle2D getCachingBounds() {
double minX = Integer.MAX_VALUE;
@@ -652,18 +749,28 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
public static enum MultiwattType {
// Multiwatt-11: 6 pins on left, 5 pins on right (staggered)
MULTIWATT_11(11, 6, 5),
MULTIWATT_11(11, 6, 5, new Size(1.7d, SizeUnit.mm), "Multiwatt 11"),
// Multiwatt-15: 8 pins on left, 7 pins on right (staggered)
MULTIWATT_15(15, 8, 7);
MULTIWATT_15(15, 8, 7, new Size(1.27d, SizeUnit.mm), "Multiwatt 15");
private final int pinCount;
private final int pinsLeft;
private final int pinsRight;
// distance between consecutive leads along the package, i.e. from one row to the other
private final Size leadPitch;
private final String title;
MultiwattType(int pinCount, int pinsLeft, int pinsRight) {
MultiwattType(int pinCount, int pinsLeft, int pinsRight, Size leadPitch, String title) {
this.pinCount = pinCount;
this.pinsLeft = pinsLeft;
this.pinsRight = pinsRight;
this.leadPitch = leadPitch;
this.title = title;
}
// neighbouring leads of the same row are two lead pitches apart
public Size getRowPitch() {
return leadPitch.scale(2);
}
public int getPinCount() {
@@ -680,7 +787,7 @@ public class MultiwattIC extends AbstractTransparentComponent<String> implements
@Override
public String toString() {
return "Multiwatt " + pinCount;
return title;
}
}
}
@@ -19,16 +19,19 @@
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
package org.diylc.components.semiconductors;
import org.diylc.components.semiconductors.DiodeSymbol;
import org.diylc.core.IDIYComponent;
public enum PinLabels {
NONE("None"), NAMES("Names"), NUMBERS("Numbers");
/** Maps physical diodes to a {@link DiodeSymbol}. */
public class DiodeSchematicFactory extends AbstractSimpleSchematicFactory {
private String title;
private PinLabels(String title) {
this.title = title;
}
@Override
protected IDIYComponent<?> createSymbol() {
return new DiodeSymbol();
public String toString() {
return title;
}
}
@@ -32,13 +32,16 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.Arrays;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.*;
import org.diylc.components.AbstractMakerBoard;
import org.diylc.components.AbstractTransparentComponent;
import org.diylc.components.transform.SIL_ICTransformer;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDatasheetSupport;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.Theme;
@@ -54,11 +57,15 @@ import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the pins run straight down from it 'Pin Spacing' apart and the indent is at the anchor's end of the body. The other orientations rotate this around the anchor")
@AiNote("The first control point is pin 1. 'Pin Names' is a comma-separated list of pin names in pin-number order; a pin without a name is called by its number. Pins named alike up to a trailing '_1', '_2' are one net inside the package, while 1IN and 2IN are separate channels")
@AiNote("A datasheet model is a part number such as LA4440 and sets 'Pins', 'Pin Spacing', 'Pin Names' and 'Value'. Set 'Value' to the same part number together with the model")
@ComponentDescriptor(name = "SIP IC", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "IC", description = "Single-in-line package IC",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = SIL_ICTransformer.class,
enableCache = true)
public class SIL_IC extends AbstractTransparentComponent<String> implements IGerberComponentSimple {
enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2,
datasheetModelColumn = 1)
public class SIL_IC extends AbstractTransparentComponent<String>
implements IGerberComponentSimple, IDatasheetSupport {
private static final long serialVersionUID = 1L;
@@ -83,6 +90,8 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
private Color borderColor = BORDER_COLOR;
private Color labelColor = LABEL_COLOR;
private Color indentColor = INDENT_COLOR;
private String pinNames = null;
private PinLabels pinLabels = null;
// new Point(0, pinSpacing.convertToPixels()),
// new Point(0, 2 * pinSpacing.convertToPixels()),
// new Point(0, 3 * pinSpacing.convertToPixels()),
@@ -94,6 +103,7 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
// new Point(3 * pinSpacing.convertToPixels(), 3 *
// pinSpacing.convertToPixels()) };
transient private Area[] body;
transient private String[] parsedPinNames;
public SIL_IC() {
super();
@@ -101,6 +111,22 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
this.alphaPercent = new Percentage(100);
}
public SIL_IC(String[] model) {
this();
applyModel(model);
}
// columns: group, part number, pin spacing, then one name per pin in pin-number order, so the
// pin count is the number of names
@Override
public void applyModel(String[] model) {
setValue(model[1]);
setPinSpacing(Size.parseSize(model[2]));
setPinCount(PinCount.valueOf("_" + (model.length - 3)));
setPinNames(String.join(",", Arrays.copyOfRange(model, 3, model.length)));
}
@EditableProperty
public String getValue() {
return value;
@@ -230,7 +256,7 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
int thickness = getClosestOdd(THICKNESS.convertToPixels());
double width;
double height;
double pinSpacing = (int) this.pinSpacing.convertToPixels();
double pinSpacing = this.pinSpacing.convertToPixels();
Area indentation = null;
int indentationSize = getClosestOdd(INDENT_SIZE.convertToPixels());
switch (orientation) {
@@ -364,6 +390,40 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
int x = (int) (bounds.getX() + (bounds.width - textWidth) / 2);
int y = (int) (bounds.getY() + (bounds.height - textHeight) / 2 + fontMetrics.getAscent());
g2d.drawString(label, x, y);
if (getPinLabels() != PinLabels.NONE) {
g2d.setColor(finalBorderColor);
drawPinLabels(g2d, mainArea.getBounds2D());
}
}
// Labels sit beside the body, square to the row and reading away from it, on the side the row
// turns to clockwise: right of a row running down, below one running left.
private void drawPinLabels(Graphics2D g2d, Rectangle2D bodyBounds) {
if (controlPoints.length < 2) {
return;
}
g2d.setFont(ICPinNames.PIN_NAME_FONT);
if (g2d.getFontMetrics().getAscent() > pinSpacing.convertToPixels()) {
return;
}
double rowDistance = controlPoints[0].distance(controlPoints[1]);
double alongX = (controlPoints[1].getX() - controlPoints[0].getX()) / rowDistance;
double alongY = (controlPoints[1].getY() - controlPoints[0].getY()) / rowDistance;
double pinRadius = PIN_SIZE.convertToPixels() / 2;
for (int i = 0; i < controlPoints.length; i++) {
String label;
if (getPinLabels() == PinLabels.NUMBERS) {
label = Integer.toString(i + 1);
} else {
String name = getPinName(i + 1);
if (name == null) {
continue;
}
label = AbstractMakerBoard.getDisplayPinLabel(name);
}
ICPinNames.drawOutside(g2d, label, controlPoints[i], alongY, -alongX, bodyBounds, pinRadius);
}
}
@Override
@@ -429,6 +489,41 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
public void setIndentColor(Color indentColor) {
this.indentColor = indentColor;
}
@EditableProperty(name = "Pin Names")
public String getPinNames() {
return pinNames;
}
public void setPinNames(String pinNames) {
this.pinNames = pinNames;
parsedPinNames = null;
}
@EditableProperty(name = "Pin Labels")
public PinLabels getPinLabels() {
if (pinLabels == null) {
pinLabels = PinLabels.NAMES;
}
return pinLabels;
}
public void setPinLabels(PinLabels pinLabels) {
this.pinLabels = pinLabels;
}
private String getPinName(int pinNumber) {
if (parsedPinNames == null) {
parsedPinNames = ICPinNames.parse(getPinNames());
}
return ICPinNames.get(parsedPinNames, pinNumber);
}
@Override
public String getControlPointNodeName(int index) {
String name = getPinName(index + 1);
return name == null ? Integer.toString(index + 1) : name;
}
@Override
public Rectangle2D getCachingBounds() {
@@ -34,7 +34,6 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.TransistorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -55,8 +54,7 @@ import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the third pin is 2 x 'Pin spacing' below it and the middle pin 'Pin spacing' below and 'Pin spacing' to the left, a triangle under the round can. 'Folded' puts the three pins in a straight line down from the anchor and lays the body flat on the right of them. The other orientations rotate this around the anchor")
@AiNote("On a 0.1in grid board the default 0.05in 'Pin spacing' puts the middle pin between holes: set 'Pin spacing' to 0.1in to land all three pins on holes")
@ComponentDescriptor(schematicFactory = TransistorSchematicFactory.class,
name = "Transistor (TO-1)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Transistor (TO-1)", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "Q", description = "Transistor with small metal body",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO1Transformer.class,
enableCache = true)
@@ -34,7 +34,6 @@ import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.TransistorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -52,8 +51,7 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(schematicFactory = TransistorSchematicFactory.class,
name = "Transistor (TO-126)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Transistor (TO-126)", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "Q", description = "Transistors with a hole for heat sink mounting",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO220Transformer.class,
enableCache = true)
@@ -34,7 +34,6 @@ import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.TransistorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -56,8 +55,7 @@ import org.diylc.utils.Constants;
@AiNote("The first control point is the anchor; with 'Orientation' DEFAULT the three pins run straight down from it 0.1in apart, and the other orientations rotate this around the anchor")
@AiNote("Seen from above, the metal tab is on the right of the pin row in DEFAULT orientation and the printed front faces left; 'Folded' lays the body flat on the right of the pin row, 'Lead Length' away from it, with the tab and its mounting hole at the far end")
@AiNote("'Pinout' values list the pins as a datasheet does, facing the printed front with the legs down, left to right; pick the value that matches the part's datasheet as written")
@ComponentDescriptor(schematicFactory = TransistorSchematicFactory.class,
name = "Transistor (TO-220)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Transistor (TO-220)", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "Q", description = "Transistors with metal tab for heat sink mounting",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO220Transformer.class,
enableCache = true)
@@ -35,7 +35,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.TransistorSchematicFactory;
import org.diylc.awt.TwoCircleTangent;
import org.diylc.common.*;
@@ -53,8 +52,7 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
@ComponentDescriptor(schematicFactory = TransistorSchematicFactory.class,
name = "Transistor (TO-3)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Transistor (TO-3)", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "Q", description = "Transistor with large metal body",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, enableCache = true)
public class TransistorTO3 extends AbstractLabeledComponent<String> {
@@ -34,7 +34,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.schematic.TransistorSchematicFactory;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
@@ -57,8 +56,7 @@ import org.diylc.utils.Constants;
@AiNote("Seen from above, the flat face is on the left of the pin row in DEFAULT orientation; 'Folded' lays the body flat on the right of the pin row instead")
@AiNote("'Pinout' values list the pins as a datasheet does, facing the flat side with the legs down, left to right; pick the value that matches the part's datasheet as written")
@AiNote("On a 0.1in grid board the default 0.05in 'Pin spacing' puts the middle pin between holes: set 'Pin spacing' to 0.1in, or use two or three jumpers acting as flexible leads and connecting TO-92 terminals to the holes on the board")
@ComponentDescriptor(schematicFactory = TransistorSchematicFactory.class,
name = "Transistor (TO-92)", author = "Branislav Stojkovic", category = "Semiconductors",
@ComponentDescriptor(name = "Transistor (TO-92)", author = "Branislav Stojkovic", category = "Semiconductors",
instanceNamePrefix = "Q", description = "Transistor with small plastic or epoxy body",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO92Transformer.class,
enableCache = true)
@@ -1,88 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.diylc.core.IDIYComponent;
/**
* Base class for the common case of a single-symbol, straight pin-for-pin mapping between a physical
* component and its schematic symbol (resistors, capacitors, inductors, diodes, two- and
* three-terminal parts, ...). The n-th sticky control point of the physical component is mapped to
* the n-th electrical control point of the symbol.
*
* @author Branislav Stojkovic
*/
public abstract class AbstractSimpleSchematicFactory implements ISchematicFactory {
/** @return a fresh, unconfigured schematic symbol instance. */
protected abstract IDIYComponent<?> createSymbol();
/** @return number of electrical (net-carrying) control points to map, in index order. */
protected int electricalPinCount() {
return 2;
}
@Override
public List<SchematicSymbolMapping> createSchematicSymbols(IDIYComponent<?> physicalComponent) {
IDIYComponent<?> symbol = createSymbol();
symbol.setId(UUID.randomUUID());
symbol.setName(physicalComponent.getName());
copyValue(physicalComponent, symbol);
// leaded symbols are born with a one-inch lead span, which leaves far more bare lead than body
// and runs into whatever sits in the neighbouring cell; pull them in to the schematic pitch
SchematicBuilder.applyPitch(symbol, false, false);
List<Integer> physicalPins = new ArrayList<Integer>();
for (int i = 0; i < physicalComponent.getControlPointCount()
&& physicalPins.size() < electricalPinCount(); i++) {
if (physicalComponent.isControlPointSticky(i)
&& physicalComponent.getControlPointNodeName(i) != null) {
physicalPins.add(i);
}
}
Map<Integer, Integer> pinMapping = new HashMap<Integer, Integer>();
for (int schematicIndex = 0; schematicIndex < physicalPins.size()
&& schematicIndex < symbol.getControlPointCount(); schematicIndex++) {
pinMapping.put(physicalPins.get(schematicIndex), schematicIndex);
}
List<SchematicSymbolMapping> result = new ArrayList<SchematicSymbolMapping>();
result.add(new SchematicSymbolMapping(symbol, pinMapping, null));
return result;
}
@SuppressWarnings("unchecked")
protected void copyValue(IDIYComponent<?> physical, IDIYComponent<?> symbol) {
try {
((IDIYComponent<Object>) symbol).setValue(physical.getValue());
} catch (Throwable t) {
// symbol value type differs from the physical component's; skip
}
}
}
@@ -1,34 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import org.diylc.components.passive.CapacitorSymbol;
import org.diylc.core.IDIYComponent;
/** Maps physical capacitors to a {@link CapacitorSymbol}. */
public class CapacitorSchematicFactory extends AbstractSimpleSchematicFactory {
@Override
protected IDIYComponent<?> createSymbol() {
return new CapacitorSymbol();
}
}
@@ -1,88 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.diylc.common.Display;
import org.diylc.components.schematic.SchematicBox;
import org.diylc.core.IDIYComponent;
/**
* Default {@link ISchematicFactory} used for every physical component type that does not declare a
* dedicated factory. It inspects the sticky control points of the component, creates a
* {@link SchematicBox} with one labeled terminal per node and copies the component's name and value.
*
* @author Branislav Stojkovic
*/
public class GenericBoxSchematicFactory implements ISchematicFactory {
@Override
public List<SchematicSymbolMapping> createSchematicSymbols(IDIYComponent<?> physicalComponent) {
List<Integer> pinIndices = new ArrayList<Integer>();
List<String> labels = new ArrayList<String>();
for (int i = 0; i < physicalComponent.getControlPointCount(); i++) {
if (!physicalComponent.isControlPointSticky(i)) {
continue;
}
String nodeName = physicalComponent.getControlPointNodeName(i);
if (nodeName == null) {
continue;
}
pinIndices.add(i);
labels.add(nodeName);
}
if (pinIndices.isEmpty()) {
return Collections.emptyList();
}
SchematicBox box = new SchematicBox();
box.setName(physicalComponent.getName());
String value = physicalComponent.getValueForDisplay();
box.setValue(value == null ? "" : value);
box.setDisplay(Display.BOTH);
box.setTopNodes("");
box.setBottomNodes("");
int half = (labels.size() + 1) / 2;
List<String> left = labels.subList(0, half);
List<String> right = labels.subList(half, labels.size());
box.setLeftNodes(String.join(",", left));
box.setRightNodes(String.join(",", right));
// SchematicBox control point order is left (top to bottom) then right (top to bottom), which is
// exactly the order we appended the labels in, so the k-th physical pin maps to schematic
// control point k.
Map<Integer, Integer> pinMapping = new HashMap<Integer, Integer>();
for (int k = 0; k < pinIndices.size(); k++) {
pinMapping.put(pinIndices.get(k), k);
}
List<SchematicSymbolMapping> result = new ArrayList<SchematicSymbolMapping>();
result.add(new SchematicSymbolMapping(box, pinMapping, null));
return result;
}
}
@@ -1,34 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import org.diylc.components.passive.InductorSymbol;
import org.diylc.core.IDIYComponent;
/** Maps physical inductors to a {@link InductorSymbol}. */
public class InductorSchematicFactory extends AbstractSimpleSchematicFactory {
@Override
protected IDIYComponent<?> createSymbol() {
return new InductorSymbol();
}
}
@@ -1,510 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.diylc.core.IDIYComponent;
import org.diylc.netlist.Group;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.Node;
import org.diylc.schematic.SchematicBuilder.SymbolEntry;
import org.diylc.schematic.flow.FlowHints;
import org.diylc.schematic.flow.FlowRole;
import org.diylc.schematic.flow.SignalFlowAnalyzer;
/**
* Arranges schematic symbols in columns that follow the direction the signal travels, so that the
* drawing reads from input on the left to output on the right.
*
* <p>
* The circuit becomes a graph of components joined by its nets. Two kinds of net are kept out of that
* graph: the ones carrying a declared rail, and the single busiest net, which in practice is ground.
* Ground reaches nearly every part of a circuit, so leaving it in makes every component adjacent to
* every other and flattens the whole drawing into one column - and most projects never place a ground
* symbol, so it cannot be recognised by its label alone.
* </p>
*
* <p>
* Where {@link SignalFlowAnalyzer} can tell that one component drives another the edge is directed,
* and columns come from the longest path through those edges. Real projects rarely carry that
* information - a transistor only reports its legs once someone has set its pin-out - so when
* direction is absent the columns come from distance across the graph instead, measured from the far
* end of its longest chain. That always spreads a connected circuit out, which is the one thing the
* layout must never fail to do.
* </p>
*
* <p>
* Every collection here keeps insertion order. The same project has to produce the same drawing every
* time, and hash ordering would quietly make the layout depend on object identity.
* </p>
*
* @author Branislav Stojkovic
*/
public class LayeredPlacement {
/** Gap between one column and the next, in pixels. */
public static final double COLUMN_GAP = 130d;
/** Gap between symbols stacked within a column, in pixels. */
public static final double ROW_GAP = 60d;
public static final double MARGIN = 120d;
/** Grid step the finished positions are snapped to. */
public static final double GRID = 10d;
private static final int ORDERING_SWEEPS = 6;
private final SignalFlowAnalyzer flowAnalyzer = new SignalFlowAnalyzer();
/** One placed component: the physical part, the symbols it produced, and where it ended up. */
private static class Vertex {
final UUID id;
final IDIYComponent<?> physical;
final List<SymbolEntry> entries;
final Set<UUID> drives = new LinkedHashSet<UUID>();
final Set<UUID> drivenBy = new LinkedHashSet<UUID>();
final Set<UUID> neighbours = new LinkedHashSet<UUID>();
int layer;
double barycentre;
Vertex(IDIYComponent<?> physical, List<SymbolEntry> entries) {
this.id = physical.getId();
this.physical = physical;
this.entries = entries;
}
boolean isDirected() {
return !drives.isEmpty() || !drivenBy.isEmpty();
}
/** @return where the part sits on the board, used to break ties so the schematic echoes it. */
double physicalY() {
return physical.getControlPointCount() == 0 ? 0 : physical.getControlPoint(0).getY();
}
}
private final Map<UUID, Vertex> vertices = new LinkedHashMap<UUID, Vertex>();
/**
* Places every symbol of every eligible component. The symbols must already have been given their
* attitude, because the column widths are measured from their current geometry.
*/
public void place(List<IDIYComponent<?>> eligible,
Map<UUID, List<SymbolEntry>> entriesByPhysicalId, Netlist netlist) {
buildVertices(eligible, entriesByPhysicalId);
if (vertices.isEmpty()) {
return;
}
buildEdges(netlist);
breakFeedbackLoops();
assignLayers();
spreadUndirectedVertices();
if (distinctLayerCount() < 2) {
// nothing in the circuit said which way the signal runs, so fall back to plain distance
layerByDistance();
}
assignCoordinates(orderWithinLayers());
}
private void buildVertices(List<IDIYComponent<?>> eligible,
Map<UUID, List<SymbolEntry>> entriesByPhysicalId) {
for (IDIYComponent<?> physical : eligible) {
List<SymbolEntry> entries = entriesByPhysicalId.get(physical.getId());
if (entries != null && !entries.isEmpty()) {
vertices.put(physical.getId(), new Vertex(physical, entries));
}
}
}
/**
* Turns each ordinary net into edges.
*
* <p>
* Direction has to carry across the passive parts that sit between active devices: a collector
* reaches the next stage's base through a coupling capacitor, so the two are never on one net and a
* rule that only looked for an output and an input on the same net would find nothing at all.
* Anything driving a net therefore drives every other member of it, and every member drives
* whatever takes the net as an input.
* </p>
*/
private void buildEdges(Netlist netlist) {
if (netlist == null) {
return;
}
Group backbone = SchematicBuilder.backboneNet(netlist);
Map<UUID, FlowHints> hintCache = new LinkedHashMap<UUID, FlowHints>();
for (Group group : netlist.getSortedGroups()) {
if (group == backbone || SchematicBuilder.railLabelOf(group) != null) {
continue;
}
List<UUID> members = new ArrayList<UUID>();
List<UUID> drivers = new ArrayList<UUID>();
List<UUID> receivers = new ArrayList<UUID>();
for (Node node : group.getSortedNodes()) {
UUID id = node.getComponent().getId();
if (!vertices.containsKey(id)) {
continue;
}
if (!members.contains(id)) {
members.add(id);
}
FlowRole role = roleOf(node, hintCache);
if (role == FlowRole.OUTPUT && !drivers.contains(id)) {
drivers.add(id);
} else if (role == FlowRole.INPUT && !receivers.contains(id)) {
receivers.add(id);
}
}
for (int i = 0; i < members.size(); i++) {
for (int j = i + 1; j < members.size(); j++) {
vertices.get(members.get(i)).neighbours.add(members.get(j));
vertices.get(members.get(j)).neighbours.add(members.get(i));
}
}
for (UUID member : members) {
for (UUID driver : drivers) {
link(driver, member);
}
for (UUID receiver : receivers) {
link(member, receiver);
}
}
}
}
private void link(UUID from, UUID to) {
if (from.equals(to)) {
return;
}
vertices.get(from).drives.add(to);
vertices.get(to).drivenBy.add(from);
}
private FlowRole roleOf(Node node, Map<UUID, FlowHints> hintCache) {
UUID id = node.getComponent().getId();
FlowHints hints;
if (hintCache.containsKey(id)) {
hints = hintCache.get(id);
} else {
hints = flowAnalyzer.analyze(node.getComponent());
hintCache.put(id, hints);
}
return hints == null ? FlowRole.NEUTRAL : hints.getRole(node.getPointIndex());
}
/**
* Drops the edges that close a loop, so that the graph can be layered at all.
*
* <p>
* Feedback is not an anomaly here: an op-amp with a feedback network, or a transistor with a
* bootstrapped or degenerated bias, produces a cycle in every real circuit of interest. Removing
* the edge that closes the loop keeps the forward path intact, which is the one that determines how
* the drawing reads.
* </p>
*/
private void breakFeedbackLoops() {
Set<UUID> visited = new LinkedHashSet<UUID>();
Set<UUID> onPath = new LinkedHashSet<UUID>();
for (UUID id : new ArrayList<UUID>(vertices.keySet())) {
if (!visited.contains(id)) {
removeBackEdges(id, visited, onPath);
}
}
}
private void removeBackEdges(UUID id, Set<UUID> visited, Set<UUID> onPath) {
visited.add(id);
onPath.add(id);
Vertex vertex = vertices.get(id);
for (UUID next : new ArrayList<UUID>(vertex.drives)) {
if (onPath.contains(next)) {
vertex.drives.remove(next);
vertices.get(next).drivenBy.remove(id);
} else if (!visited.contains(next)) {
removeBackEdges(next, visited, onPath);
}
}
onPath.remove(id);
}
/** Longest path layering over the acyclic graph: a component sits one column after its drivers. */
private void assignLayers() {
Map<UUID, Integer> remaining = new LinkedHashMap<UUID, Integer>();
List<UUID> ready = new ArrayList<UUID>();
for (Vertex vertex : vertices.values()) {
vertex.layer = 0;
remaining.put(vertex.id, vertex.drivenBy.size());
if (vertex.drivenBy.isEmpty()) {
ready.add(vertex.id);
}
}
for (int i = 0; i < ready.size(); i++) {
Vertex vertex = vertices.get(ready.get(i));
for (UUID next : vertex.drives) {
Vertex target = vertices.get(next);
target.layer = Math.max(target.layer, vertex.layer + 1);
int left = remaining.get(next) - 1;
remaining.put(next, left);
if (left == 0) {
ready.add(next);
}
}
}
}
/**
* Pulls the parts that no rule could give a direction to - the resistors and capacitors that make
* up most of a circuit - towards the components they sit between, so they do not all pile up in the
* first column.
*/
private void spreadUndirectedVertices() {
for (int sweep = 0; sweep < ORDERING_SWEEPS; sweep++) {
boolean changed = false;
for (Vertex vertex : vertices.values()) {
if (vertex.isDirected() || vertex.neighbours.isEmpty()) {
continue;
}
double sum = 0;
int count = 0;
for (UUID neighbour : vertex.neighbours) {
sum += vertices.get(neighbour).layer;
count++;
}
int target = (int) Math.round(sum / count);
if (target != vertex.layer) {
vertex.layer = target;
changed = true;
}
}
if (!changed) {
break;
}
}
}
private int distinctLayerCount() {
Set<Integer> layers = new LinkedHashSet<Integer>();
for (Vertex vertex : vertices.values()) {
layers.add(vertex.layer);
}
return layers.size();
}
/**
* Spreads the circuit out by distance when nothing declared a direction.
*
* <p>
* Each connected island is measured from one end of its longest chain - found by walking to the
* furthest vertex and then measuring from there - and every component takes its column from how far
* along it sits. The result is not electrically meaningful the way a driven chain is, but it does
* lay the circuit out along its own longest path, which is far closer to a schematic than a single
* stack of parts.
* </p>
*/
private void layerByDistance() {
Set<UUID> remaining = new LinkedHashSet<UUID>(vertices.keySet());
while (!remaining.isEmpty()) {
UUID seed = remaining.iterator().next();
Map<UUID, Integer> reach = distancesFrom(seed);
UUID far = furthest(reach);
Map<UUID, Integer> distances = distancesFrom(far);
for (Map.Entry<UUID, Integer> entry : distances.entrySet()) {
vertices.get(entry.getKey()).layer = entry.getValue();
remaining.remove(entry.getKey());
}
// a vertex with no connections at all is its own island
remaining.remove(seed);
}
}
private Map<UUID, Integer> distancesFrom(UUID start) {
Map<UUID, Integer> distances = new LinkedHashMap<UUID, Integer>();
List<UUID> queue = new ArrayList<UUID>();
distances.put(start, 0);
queue.add(start);
for (int i = 0; i < queue.size(); i++) {
UUID current = queue.get(i);
int next = distances.get(current) + 1;
for (UUID neighbour : vertices.get(current).neighbours) {
if (!distances.containsKey(neighbour)) {
distances.put(neighbour, next);
queue.add(neighbour);
}
}
}
return distances;
}
private static UUID furthest(Map<UUID, Integer> distances) {
UUID best = null;
int bestDistance = -1;
for (Map.Entry<UUID, Integer> entry : distances.entrySet()) {
if (entry.getValue() > bestDistance) {
bestDistance = entry.getValue();
best = entry.getKey();
}
}
return best;
}
/**
* Orders each column so that symbols sit near the ones they connect to, which is what keeps the
* wires between columns from crossing. Ties fall back to the part's position on the board, so the
* schematic keeps a family resemblance to the layout it came from.
*/
private Map<Integer, List<Vertex>> orderWithinLayers() {
Map<Integer, List<Vertex>> byLayer = new LinkedHashMap<Integer, List<Vertex>>();
List<Vertex> sorted = new ArrayList<Vertex>(vertices.values());
sorted.sort(Comparator.comparingInt((Vertex v) -> v.layer)
.thenComparingDouble(Vertex::physicalY));
for (Vertex vertex : sorted) {
List<Vertex> column = byLayer.get(vertex.layer);
if (column == null) {
column = new ArrayList<Vertex>();
byLayer.put(vertex.layer, column);
}
column.add(vertex);
}
Map<UUID, Integer> position = new LinkedHashMap<UUID, Integer>();
for (List<Vertex> column : byLayer.values()) {
for (int i = 0; i < column.size(); i++) {
position.put(column.get(i).id, i);
}
}
for (int sweep = 0; sweep < ORDERING_SWEEPS; sweep++) {
for (List<Vertex> column : byLayer.values()) {
for (Vertex vertex : column) {
vertex.barycentre = barycentreOf(vertex, position);
}
column.sort(Comparator.comparingDouble((Vertex v) -> v.barycentre)
.thenComparingDouble(Vertex::physicalY));
for (int i = 0; i < column.size(); i++) {
position.put(column.get(i).id, i);
}
}
}
return byLayer;
}
private double barycentreOf(Vertex vertex, Map<UUID, Integer> position) {
double sum = 0;
int count = 0;
for (UUID neighbour : vertex.neighbours) {
Integer at = position.get(neighbour);
if (at != null) {
sum += at;
count++;
}
}
return count == 0 ? position.get(vertex.id) : sum / count;
}
/**
* Turns columns and orders into pixel positions. Each column is as wide as its widest symbol, and a
* symbol is placed by its bounding box rather than by a control point, so that a part standing
* upright cannot hang out of its cell.
*/
private void assignCoordinates(Map<Integer, List<Vertex>> byLayer) {
List<Integer> layers = new ArrayList<Integer>(byLayer.keySet());
Collections.sort(layers);
double x = MARGIN;
for (Integer layer : layers) {
List<Vertex> column = byLayer.get(layer);
double columnWidth = 0;
for (Vertex vertex : column) {
columnWidth = Math.max(columnWidth, boundsOf(vertex.entries).getWidth());
}
double y = MARGIN;
for (Vertex vertex : column) {
Rectangle2D bounds = boundsOf(vertex.entries);
// centre the symbol in its column so that a narrow part sits under a wide one
double offsetX = x + (columnWidth - bounds.getWidth()) / 2;
moveBoundsTo(vertex.entries, bounds, snap(offsetX), snap(y));
y += bounds.getHeight() + ROW_GAP;
}
x += columnWidth + COLUMN_GAP;
}
}
/** @return the box spanned by every control point of every symbol the component produced. */
private static Rectangle2D boundsOf(List<SymbolEntry> entries) {
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
for (SymbolEntry entry : entries) {
for (int i = 0; i < entry.symbol.getControlPointCount(); i++) {
Point2D p = entry.symbol.getControlPoint(i);
minX = Math.min(minX, p.getX());
minY = Math.min(minY, p.getY());
maxX = Math.max(maxX, p.getX());
maxY = Math.max(maxY, p.getY());
}
}
if (minX > maxX) {
return new Rectangle2D.Double(0, 0, 0, 0);
}
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
}
private static void moveBoundsTo(List<SymbolEntry> entries, Rectangle2D bounds, double x,
double y) {
double dx = x - bounds.getX();
double dy = y - bounds.getY();
for (SymbolEntry entry : entries) {
translate(entry.symbol, dx, dy);
}
}
/**
* Moves every control point by the same offset. The targets are worked out before anything is
* written, because a rigid component such as SchematicBox rebuilds its remaining points when one of
* them is set and would otherwise apply the offset twice.
*/
private static void translate(IDIYComponent<?> component, double dx, double dy) {
int count = component.getControlPointCount();
Point2D[] targets = new Point2D[count];
for (int i = 0; i < count; i++) {
Point2D p = component.getControlPoint(i);
targets[i] = new Point2D.Double(p.getX() + dx, p.getY() + dy);
}
for (int i = 0; i < count; i++) {
component.setControlPoint(targets[i], i);
}
}
private static double snap(double value) {
return Math.round(value / GRID) * GRID;
}
}
@@ -1,43 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import org.diylc.components.passive.PotentiometerSymbol;
import org.diylc.core.IDIYComponent;
/**
* Maps physical potentiometers to a {@link PotentiometerSymbol}. The
* three package legs are mapped to the symbol legs in index order; if a particular package uses a
* different pin-out the user can rotate/relabel the resulting symbol.
*/
public class PotentiometerSchematicFactory extends AbstractSimpleSchematicFactory {
@Override
protected IDIYComponent<?> createSymbol() {
return new PotentiometerSymbol();
}
@Override
protected int electricalPinCount() {
return 3;
}
}
@@ -1,34 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import org.diylc.components.passive.ResistorSymbol;
import org.diylc.core.IDIYComponent;
/** Maps physical resistors to a {@link ResistorSymbol}. */
public class ResistorSchematicFactory extends AbstractSimpleSchematicFactory {
@Override
protected IDIYComponent<?> createSymbol() {
return new ResistorSymbol();
}
}
@@ -1,707 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.diylc.common.ComponentType;
import org.diylc.common.OrientationHV;
import org.diylc.components.AbstractLeadedComponent;
import org.diylc.components.connectivity.OrthogonalLine;
import org.diylc.components.misc.CommonNode;
import org.diylc.components.misc.GroundSymbol;
import org.diylc.core.IContinuity;
import org.diylc.core.ICommonNode;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ISwitch;
import org.diylc.core.Project;
import org.diylc.core.SchematicView;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.netlist.Group;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistBuilder;
import org.diylc.netlist.NetlistException;
import org.diylc.netlist.Node;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.ContinuityArea;
/**
* Generates the initial {@link SchematicView} for a layout {@link Project}. Every eligible physical
* component is turned into one or more schematic symbols via its {@link ISchematicFactory} (or the
* {@link GenericBoxSchematicFactory} fallback), symbols are placed on a connection-density aware
* grid and the nets from the layout netlist are drawn as {@link OrthogonalLine}s.
*
* <p>
* Nets that carry a common node (ground, supply rails) are not wired up at all. Each pin on such a
* net gets its own ground or common-node stub drawn right next to it, which is how schematics are
* conventionally drawn and keeps the densest net in the circuit from crossing the whole drawing.
* </p>
*
* @author Branislav Stojkovic
*/
public class SchematicBuilder {
private static final Logger LOG = Logger.getLogger(SchematicBuilder.class);
/** Grid step, in pixels, used for symbol placement. */
public static final double GRID = 90d;
/** Horizontal/vertical spacing between grid cells, in pixels. */
public static final double CELL_SIZE = 220d;
public static final double MARGIN = 120d;
/**
* Pin-to-pin span given to every two-terminal symbol. {@link AbstractLeadedComponent} defaults to
* a one-inch span (200px) while the schematic symbol bodies are only 0.05in to 0.3in long, so the
* default leaves 70 to 95 pixels of bare lead on each side — more lead than body, running well
* into the neighbouring cell. Half an inch clears the longest body with a short stub either side
* and lands on the 0.1in schematic grid.
*/
public static final double SYMBOL_PITCH = new Size(0.5d, SizeUnit.in).convertToPixels();
/** The label {@link GroundSymbol} reports; every other common-node label is treated as a supply. */
private static final String GROUND_LABEL = "GND";
/** Smallest net that may be taken for the ground backbone. */
private static final int MIN_BACKBONE_SIZE = 4;
private final Map<Class<? extends ISchematicFactory>, ISchematicFactory> factoryCache =
new HashMap<Class<? extends ISchematicFactory>, ISchematicFactory>();
private final GenericBoxSchematicFactory genericFactory = new GenericBoxSchematicFactory();
private final WireRouter router = new WireRouter();
/**
* One schematic symbol produced from a physical component together with the physical-&gt;schematic
* pin mapping that produced it.
*/
static class SymbolEntry {
final IDIYComponent<?> symbol;
final Map<Integer, Integer> pinMapping;
SymbolEntry(IDIYComponent<?> symbol, Map<Integer, Integer> pinMapping) {
this.symbol = symbol;
this.pinMapping = pinMapping;
}
}
/**
* Everything the wiring pass produces: the routed wires for ordinary signal nets, and the ground
* and supply stubs that stand in for the rail nets.
*/
static class Wiring {
private final List<OrthogonalLine> wires = new ArrayList<OrthogonalLine>();
private final List<IDIYComponent<?>> stubs = new ArrayList<IDIYComponent<?>>();
List<OrthogonalLine> getWires() {
return wires;
}
List<IDIYComponent<?>> getStubs() {
return stubs;
}
}
/**
* Builds the schematic and stores it on {@code project.getOrCreateSchematicView()}, replacing any
* previous content.
*
* @param project the layout project
* @param continuityAreas continuity areas from the drawing manager (may be empty)
*/
public void build(Project project, List<ContinuityArea> continuityAreas) {
Netlist netlist = extractNetlist(project, continuityAreas);
List<IDIYComponent<?>> schematicComponents = new ArrayList<IDIYComponent<?>>();
Map<UUID, List<UUID>> physicalToSchematicMap = new LinkedHashMap<UUID, List<UUID>>();
Map<UUID, List<SymbolEntry>> entriesByPhysicalId = new LinkedHashMap<UUID, List<SymbolEntry>>();
List<IDIYComponent<?>> eligible = new ArrayList<IDIYComponent<?>>();
for (IDIYComponent<?> physical : project.getComponents()) {
if (!isEligible(physical)) {
continue;
}
eligible.add(physical);
List<SymbolEntry> entries = createSymbols(physical);
if (entries.isEmpty()) {
continue;
}
List<UUID> symbolIds = new ArrayList<UUID>();
for (SymbolEntry entry : entries) {
schematicComponents.add(entry.symbol);
symbolIds.add(entry.symbol.getId());
}
physicalToSchematicMap.put(physical.getId(), symbolIds);
entriesByPhysicalId.put(physical.getId(), entries);
}
placeSymbols(eligible, entriesByPhysicalId, netlist);
Wiring wiring = createWires(netlist, entriesByPhysicalId);
schematicComponents.addAll(wiring.getStubs());
schematicComponents.addAll(wiring.getWires());
schematicComponents.sort(SCHEMATIC_ORDER);
SchematicView view = project.getOrCreateSchematicView();
view.getComponents().clear();
view.getComponents().addAll(schematicComponents);
view.setPhysicalToSchematicMap(physicalToSchematicMap);
resizeCanvasToFit(view);
}
Netlist extractNetlist(Project project, List<ContinuityArea> continuityAreas) {
try {
List<Netlist> netlists = NetlistBuilder.extractNetlists(false, project,
continuityAreas == null ? new ArrayList<ContinuityArea>() : continuityAreas);
if (netlists == null || netlists.isEmpty()) {
return null;
}
return netlists.get(0);
} catch (NetlistException e) {
LOG.warn("Could not extract netlist for schematic generation: " + e.getMessage());
return null;
}
}
/* ------------------------------------------------------------------ eligibility */
static boolean isEligible(IDIYComponent<?> component) {
if (component instanceof ICommonNode) {
// ground and supply nodes are not placed as symbols of their own; every pin on their net gets
// a freshly generated stub during wiring instead
return false;
}
if (component instanceof ISwitch) {
return true; // switches are drawn as symbols (netlist is built with includeSwitches=false)
}
if (component instanceof IContinuity) {
return false; // wires, traces, solder bridges, board strips
}
for (int i = 0; i < component.getControlPointCount(); i++) {
if (component.isControlPointSticky(i)) {
return true;
}
}
return false;
}
/* ------------------------------------------------------------------ symbol creation */
List<SymbolEntry> createSymbols(IDIYComponent<?> physical) {
ISchematicFactory factory = resolveFactory(physical);
List<SchematicSymbolMapping> mappings;
try {
mappings = factory.createSchematicSymbols(physical);
} catch (Exception e) {
LOG.warn("Schematic factory " + factory.getClass().getSimpleName() + " failed for "
+ physical.getName() + ", falling back to generic box", e);
mappings = genericFactory.createSchematicSymbols(physical);
}
List<SymbolEntry> entries = new ArrayList<SymbolEntry>();
if (mappings != null) {
for (SchematicSymbolMapping mapping : mappings) {
if (mapping.getSchematicSymbol() == null) {
continue;
}
if (mapping.getSchematicSymbol().getId() == null) {
mapping.getSchematicSymbol().setId(UUID.randomUUID());
}
entries.add(new SymbolEntry(mapping.getSchematicSymbol(), mapping.getPinMapping()));
}
}
return entries;
}
@SuppressWarnings("unchecked")
private ISchematicFactory resolveFactory(IDIYComponent<?> physical) {
ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) physical.getClass());
Class<? extends ISchematicFactory> factoryClass =
type == null ? null : type.getSchematicFactoryClass();
if (factoryClass == null) {
return genericFactory;
}
ISchematicFactory cached = factoryCache.get(factoryClass);
if (cached != null) {
return cached;
}
try {
ISchematicFactory factory = factoryClass.getDeclaredConstructor().newInstance();
factoryCache.put(factoryClass, factory);
return factory;
} catch (Exception e) {
LOG.warn("Could not instantiate schematic factory " + factoryClass.getName()
+ ", using generic box", e);
return genericFactory;
}
}
/* ------------------------------------------------------------------ placement */
private void placeSymbols(List<IDIYComponent<?>> eligible,
Map<UUID, List<SymbolEntry>> entriesByPhysicalId, Netlist netlist) {
Map<UUID, Map<Integer, String>> railPins = railPins(netlist);
// attitude first: the placement measures each symbol, so it has to see its final geometry
for (IDIYComponent<?> physical : eligible) {
List<SymbolEntry> entries = entriesByPhysicalId.get(physical.getId());
if (entries != null) {
orientSymbols(entries, railPins.get(physical.getId()));
}
}
new LayeredPlacement().place(eligible, entriesByPhysicalId, netlist);
}
/**
* Chooses the resting attitude of every two-terminal symbol of one component. A part with a leg on
* a rail is a shunt element and stands upright with that leg pointing at the rail it belongs to;
* everything else passes signal along the drawing and lies down.
*
* @param rails pin index -&gt; rail label for this component, or null when it touches no rail
*/
static void orientSymbols(List<SymbolEntry> entries, Map<Integer, String> rails) {
for (SymbolEntry entry : entries) {
String railLabel = null;
int railPin = -1;
if (rails != null) {
for (Map.Entry<Integer, Integer> mapped : entry.pinMapping.entrySet()) {
String label = rails.get(mapped.getKey());
if (label != null && mapped.getValue() < 2) {
railLabel = label;
railPin = mapped.getValue();
break;
}
}
}
if (railLabel == null) {
applyPitch(entry.symbol, false, false);
continue;
}
// a ground symbol is drawn downward from its point, so the grounded leg belongs at the bottom;
// supply labels sit above the part the way they are drawn on paper
boolean railAtBottom = GROUND_LABEL.equals(railLabel);
applyPitch(entry.symbol, true, (railPin == 0) == railAtBottom);
}
}
/**
* Rewrites the two terminals of a leaded symbol so that they sit {@link #SYMBOL_PITCH} apart,
* centred on wherever the symbol already is. Rigid multi-pin symbols are left untouched — their
* geometry comes from an anchor and a pin spacing, not from the terminals.
*
* <p>
* The midpoint is preserved, which is what keeps the label control point (index 2, positioned
* relative to the centre by the constructor) correct without having to move it as well.
* </p>
*
* @param vertical stand the symbol upright rather than lying it down
* @param flip put control point 0 at the far end (bottom when upright, right when lying down)
*/
static void applyPitch(IDIYComponent<?> symbol, boolean vertical, boolean flip) {
if (!(symbol instanceof AbstractLeadedComponent) || symbol.getControlPointCount() < 2) {
return;
}
Point2D first = symbol.getControlPoint(0);
Point2D second = symbol.getControlPoint(1);
double cx = (first.getX() + second.getX()) / 2;
double cy = (first.getY() + second.getY()) / 2;
double half = SYMBOL_PITCH / 2;
Point2D near =
vertical ? new Point2D.Double(cx, cy - half) : new Point2D.Double(cx - half, cy);
Point2D far = vertical ? new Point2D.Double(cx, cy + half) : new Point2D.Double(cx + half, cy);
symbol.setControlPoint(flip ? far : near, 0);
symbol.setControlPoint(flip ? near : far, 1);
}
/** Places every symbol of {@code entries} into grid cell {@code slot} of {@code totalSlots}. */
static void placeAtGridSlot(List<SymbolEntry> entries, int slot, int totalSlots) {
int columns = Math.max(1, (int) Math.ceil(Math.sqrt(Math.max(totalSlots, 1))));
int col = slot % columns;
int row = slot / columns;
double baseX = MARGIN + col * CELL_SIZE;
double baseY = MARGIN + row * CELL_SIZE;
int sub = 0;
for (SymbolEntry entry : entries) {
moveAnchorTo(entry.symbol, snap(baseX), snap(baseY + sub * (CELL_SIZE / 2)));
sub++;
}
}
/**
* Collects, per physical component, which of its control points sit on a rail net and under which
* label, so that placement can stand shunt parts upright.
*/
static Map<UUID, Map<Integer, String>> railPins(Netlist netlist) {
Map<UUID, Map<Integer, String>> result = new HashMap<UUID, Map<Integer, String>>();
if (netlist == null) {
return result;
}
for (Group group : netlist.getGroups()) {
String label = railLabelOf(group);
if (label == null) {
continue;
}
for (Node node : group.getNodes()) {
if (node.getComponent() instanceof ICommonNode) {
continue;
}
result.computeIfAbsent(node.getComponent().getId(), k -> new HashMap<Integer, String>())
.put(node.getPointIndex(), label);
}
}
return result;
}
/**
* {@link NetlistBuilder} emits a single node per common-node label, so a rail net is recognised by
* the one common-node member it carries rather than by its size.
*
* @return the rail label of the net, or null when it is an ordinary signal net
*/
static String railLabelOf(Group group) {
for (Node node : group.getNodes()) {
if (node.getComponent() instanceof ICommonNode) {
return ((ICommonNode) node.getComponent()).getCommonNodeLabel();
}
}
return null;
}
/**
* Picks out the net that behaves like ground: the busiest one that carries no declared rail.
*
* <p>
* Only about one project in eight places a ground symbol, so in most circuits ground arrives as an
* ordinary net that simply happens to touch a large share of the components. Left in the layout
* graph it makes every part adjacent to every other and flattens the drawing into a single column;
* left in the wiring it is chained from one side of the schematic to the other and crosses
* everything in between.
* </p>
*
* @return the backbone net, or null when no net is big enough to be taken for one
*/
static Group backboneNet(Netlist netlist) {
if (netlist == null) {
return null;
}
Group largest = null;
int largestSize = 0;
for (Group group : netlist.getSortedGroups()) {
if (railLabelOf(group) != null) {
continue;
}
int size = group.getNodes().size();
if (size > largestSize) {
largestSize = size;
largest = group;
}
}
return largestSize >= MIN_BACKBONE_SIZE ? largest : null;
}
/** Translates every control point of the component so that control point 0 lands on the target. */
static void moveAnchorTo(IDIYComponent<?> component, double targetX, double targetY) {
int count = component.getControlPointCount();
if (count == 0) {
return;
}
Point2D anchor = component.getControlPoint(0);
double dx = targetX - anchor.getX();
double dy = targetY - anchor.getY();
// Snapshot the target positions before mutating: some components (e.g. SchematicBox) recompute
// all of their control points when control point 0 is set, so reading getControlPoint(i) inside
// the loop after that would double-apply the offset.
Point2D[] targets = new Point2D[count];
for (int i = 0; i < count; i++) {
Point2D p = component.getControlPoint(i);
targets[i] = new Point2D.Double(p.getX() + dx, p.getY() + dy);
}
for (int i = 0; i < count; i++) {
component.setControlPoint(targets[i], i);
}
}
private static double snap(double value) {
return Math.round(value / GRID) * GRID;
}
/* ------------------------------------------------------------------ wiring */
Wiring createWires(Netlist netlist, Map<UUID, List<SymbolEntry>> entriesByPhysicalId) {
Wiring wiring = new Wiring();
if (netlist == null) {
return wiring;
}
Map<UUID, Rectangle2D> bodies = symbolBodies(entriesByPhysicalId);
// wires already routed become obstacles for the ones that follow, so a dense net does not end up
// drawing every connection on top of the last
List<Line2D> placed = new ArrayList<Line2D>();
Group backbone = backboneNet(netlist);
for (Group group : netlist.getSortedGroups()) {
List<Pin> pins = new ArrayList<Pin>();
for (Node node : group.getNodes()) {
UUID physicalId = node.getComponent().getId();
List<SymbolEntry> entries = entriesByPhysicalId.get(physicalId);
if (entries == null) {
continue;
}
for (SymbolEntry entry : entries) {
Integer schematicIndex = entry.pinMapping.get(node.getPointIndex());
if (schematicIndex == null || schematicIndex >= entry.symbol.getControlPointCount()) {
continue;
}
Point2D location = entry.symbol.getControlPoint(schematicIndex);
pins.add(new Pin(entry.symbol, schematicIndex,
new Point2D.Double(location.getX(), location.getY())));
}
}
String railLabel = railLabelOf(group);
if (railLabel == null && group == backbone) {
// an unlabelled ground net says the same thing drawn as stubs as it does chained across the
// whole drawing, and every stub ties back to the same net, so nothing is claimed that is not
// already true of the circuit
railLabel = GROUND_LABEL;
}
if (railLabel != null) {
// rails are never routed: a lone stub on each pin says the same thing without dragging the
// busiest net in the circuit across the drawing
for (Pin pin : pins) {
wiring.getStubs().add(createRailStub(railLabel, pin.location));
}
continue;
}
if (pins.size() < 2) {
continue;
}
// chain the pins after sorting left-to-right, top-to-bottom
pins.sort(Comparator.comparingDouble((Pin p) -> p.location.getX())
.thenComparingDouble(p -> p.location.getY()));
for (int i = 0; i < pins.size() - 1; i++) {
Pin a = pins.get(i);
Pin b = pins.get(i + 1);
OrthogonalLine wire = createWire(a, b, bodies, placed);
wiring.getWires().add(wire);
placed.addAll(segmentsOf(wire));
}
}
return wiring;
}
/** Creates the ground or supply symbol that stands in for one pin's connection to a rail. */
private static IDIYComponent<?> createRailStub(String label, Point2D location) {
IDIYComponent<?> stub;
if (GROUND_LABEL.equals(label)) {
stub = new GroundSymbol();
} else {
CommonNode node = new CommonNode();
node.setValue(label);
stub = node;
}
stub.setId(UUID.randomUUID());
stub.setControlPoint(copyOf(location), 0);
return stub;
}
private static class Pin {
final IDIYComponent<?> symbol;
final int pinIndex;
final Point2D location;
Pin(IDIYComponent<?> symbol, int pinIndex, Point2D location) {
this.symbol = symbol;
this.pinIndex = pinIndex;
this.location = location;
}
}
/**
* Connects two pins with a wire whose single bend point is placed so that the wire leaves the
* first pin along the axis that pin faces. {@link OrthogonalLine} derives the rest of the route
* from that one point, and keeps deriving it as the sticky endpoints follow the symbols around.
*/
private OrthogonalLine createWire(Pin a, Pin b, Map<UUID, Rectangle2D> bodies,
List<Line2D> placed) {
List<Rectangle2D> obstacles = new ArrayList<Rectangle2D>();
for (Map.Entry<UUID, Rectangle2D> entry : bodies.entrySet()) {
// the wire starts and ends on these two symbols, so their own boxes are not obstacles
if (!entry.getKey().equals(a.symbol.getId()) && !entry.getKey().equals(b.symbol.getId())) {
obstacles.add(entry.getValue());
}
}
WireRouter.Route route = router.route(a.location, exitDirection(a.symbol, a.location),
b.location, exitDirection(b.symbol, b.location), obstacles, placed);
OrthogonalLine wire = new OrthogonalLine();
wire.setId(UUID.randomUUID());
wire.setStartDirection(route.getStartDirection());
wire.setControlPoint(copyOf(a.location), 0);
wire.setControlPoint(route.getBendPoint(), 1);
wire.setControlPoint(copyOf(b.location), 2);
return wire;
}
/** @return the boxes spanned by each symbol's control points, keyed by symbol id. */
private static Map<UUID, Rectangle2D> symbolBodies(
Map<UUID, List<SymbolEntry>> entriesByPhysicalId) {
Map<UUID, Rectangle2D> bodies = new LinkedHashMap<UUID, Rectangle2D>();
for (List<SymbolEntry> entries : entriesByPhysicalId.values()) {
for (SymbolEntry entry : entries) {
Rectangle2D bounds = boundsOf(entry.symbol);
if (bounds != null) {
bodies.put(entry.symbol.getId(), bounds);
}
}
}
return bodies;
}
private static Rectangle2D boundsOf(IDIYComponent<?> symbol) {
int count = symbol.getControlPointCount();
if (count == 0) {
return null;
}
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
for (int i = 0; i < count; i++) {
Point2D p = symbol.getControlPoint(i);
minX = Math.min(minX, p.getX());
minY = Math.min(minY, p.getY());
maxX = Math.max(maxX, p.getX());
maxY = Math.max(maxY, p.getY());
}
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
}
private static List<Line2D> segmentsOf(OrthogonalLine wire) {
List<Point2D> vertices = WireRouter.vertices(wire.getControlPoint(0), wire.getControlPoint(1),
wire.getControlPoint(2), wire.getStartDirection());
List<Line2D> segments = new ArrayList<Line2D>();
for (int i = 1; i < vertices.size(); i++) {
segments.add(new Line2D.Double(vertices.get(i - 1), vertices.get(i)));
}
return segments;
}
/**
* Works out which way a wire should leave a pin.
*
* <p>
* A two terminal symbol has an axis of its own and its leads run along it, so a wire has to carry
* on in that direction or it will bend the moment it leaves the lead. Anything else - a box, a
* transistor - has its pins pointing away from the body, which the centroid finds well enough.
* </p>
*/
static OrientationHV exitDirection(IDIYComponent<?> symbol, Point2D pinLocation) {
int n = symbol.getControlPointCount();
if (n == 0) {
return OrientationHV.HORIZONTAL;
}
if (symbol instanceof AbstractLeadedComponent && n >= 2) {
Point2D first = symbol.getControlPoint(0);
Point2D second = symbol.getControlPoint(1);
return Math.abs(second.getX() - first.getX()) >= Math.abs(second.getY() - first.getY())
? OrientationHV.HORIZONTAL
: OrientationHV.VERTICAL;
}
double cx = 0;
double cy = 0;
for (int i = 0; i < n; i++) {
cx += symbol.getControlPoint(i).getX();
cy += symbol.getControlPoint(i).getY();
}
cx /= n;
cy /= n;
double dx = pinLocation.getX() - cx;
double dy = pinLocation.getY() - cy;
return Math.abs(dx) >= Math.abs(dy) ? OrientationHV.HORIZONTAL : OrientationHV.VERTICAL;
}
private static Point2D copyOf(Point2D p) {
return new Point2D.Double(p.getX(), p.getY());
}
/* ------------------------------------------------------------------ misc */
static void resizeCanvasToFit(SchematicView view) {
double maxX = 0;
double maxY = 0;
for (IDIYComponent<?> component : view.getComponents()) {
for (int i = 0; i < component.getControlPointCount(); i++) {
Point2D p = component.getControlPoint(i);
maxX = Math.max(maxX, p.getX());
maxY = Math.max(maxY, p.getY());
}
}
maxX += MARGIN;
maxY += MARGIN;
if (maxX > view.getWidth().convertToPixels()) {
view.setWidth(new Size(maxX, SizeUnit.px));
}
if (maxY > view.getHeight().convertToPixels()) {
view.setHeight(new Size(maxY, SizeUnit.px));
}
}
/**
* Paint and hit-test order for the schematic view. Wires go underneath the symbols, and not where
* their {@link IDIYComponent#WIRING} z-order would otherwise put them, because the canvas hands a
* click to the last matching component in this list. A wire runs right up to the pin it connects
* to and its hit area is widened to three pixels, so a wire drawn on top would swallow the clicks
* meant for the symbol underneath it and the user could not drag the symbol at all.
*/
static final Comparator<IDIYComponent<?>> SCHEMATIC_ORDER =
Comparator.comparingInt((IDIYComponent<?> c) -> c instanceof OrthogonalLine ? 0 : 1)
.thenComparingDouble(SchematicBuilder::zOrderOf);
@SuppressWarnings("unchecked")
static double zOrderOf(IDIYComponent<?> component) {
ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
return type == null ? IDIYComponent.COMPONENT : type.getZOrder();
}
/** Convenience overload used by callers that already have the drawing manager's areas. */
public static void generate(Project project, Collection<ContinuityArea> continuityAreas) {
new SchematicBuilder().build(project,
continuityAreas == null ? new ArrayList<ContinuityArea>()
: new ArrayList<ContinuityArea>(continuityAreas));
}
}
@@ -1,200 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.diylc.components.connectivity.OrthogonalLine;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.SchematicView;
import org.diylc.netlist.Netlist;
import org.diylc.presenter.ContinuityArea;
import org.diylc.schematic.SchematicBuilder.SymbolEntry;
/**
* Incrementally reconciles an already generated {@link SchematicView} with the current state of the
* layout {@link Project}. It is meant to run silently whenever the user switches to the schematic
* tab.
*
* <ul>
* <li>Components added to the layout get fresh symbols placed in a free grid cell.</li>
* <li>Components removed from the layout lose their symbols (and the wires attached to them).</li>
* <li>Symbols that survive keep their position, rotation and mirroring; only name and value are
* refreshed.</li>
* <li>All wires are regenerated from the current netlist.</li>
* </ul>
*
* @author Branislav Stojkovic
*/
public class SchematicSynchronizer {
private static final Logger LOG = Logger.getLogger(SchematicSynchronizer.class);
private final SchematicBuilder builder = new SchematicBuilder();
/**
* Synchronizes the schematic view. If the project has no schematic view yet, or it is empty, a
* full {@link SchematicBuilder#build} is performed instead.
*
* <p>
* Symbols the user has arranged keep their position for as long as the component behind them is
* still in the layout. Improvements to the placement algorithm deliberately do not reach an
* already generated schematic: re-laying one out has to be an explicit action, never a side
* effect of opening the view.
* </p>
*/
public void synchronize(Project project, List<ContinuityArea> continuityAreas) {
SchematicView view = project.getSchematicView();
if (view == null || !view.isGenerated()) {
builder.build(project, continuityAreas == null ? new ArrayList<ContinuityArea>() : continuityAreas);
return;
}
Map<UUID, List<UUID>> map = view.getPhysicalToSchematicMap();
Map<UUID, IDIYComponent<?>> symbolsById = new HashMap<UUID, IDIYComponent<?>>();
for (IDIYComponent<?> component : view.getComponents()) {
if (!(component instanceof OrthogonalLine)) {
symbolsById.put(component.getId(), component);
}
}
Map<UUID, IDIYComponent<?>> layoutById = new LinkedHashMap<UUID, IDIYComponent<?>>();
for (IDIYComponent<?> component : project.getComponents()) {
layoutById.put(component.getId(), component);
}
List<IDIYComponent<?>> keptSymbols = new ArrayList<IDIYComponent<?>>();
Map<UUID, List<SymbolEntry>> entriesByPhysicalId = new LinkedHashMap<UUID, List<SymbolEntry>>();
Map<UUID, List<UUID>> newMap = new LinkedHashMap<UUID, List<UUID>>();
// the netlist is needed up front so that newly added components are stood upright against their
// rails exactly the way a full build would place them
Netlist netlist = builder.extractNetlist(project,
continuityAreas == null ? new ArrayList<ContinuityArea>() : continuityAreas);
Map<UUID, Map<Integer, String>> railPins = SchematicBuilder.railPins(netlist);
int occupiedSlots = map.size();
for (Map.Entry<UUID, IDIYComponent<?>> layoutEntry : layoutById.entrySet()) {
UUID physicalId = layoutEntry.getKey();
IDIYComponent<?> physical = layoutEntry.getValue();
if (!SchematicBuilder.isEligible(physical)) {
continue;
}
List<SymbolEntry> freshEntries = builder.createSymbols(physical);
if (freshEntries.isEmpty()) {
continue;
}
List<UUID> existingIds = map.get(physicalId);
List<SymbolEntry> resolvedEntries = new ArrayList<SymbolEntry>();
List<UUID> resolvedIds = new ArrayList<UUID>();
if (existingIds != null) {
// component already present: keep positioned symbols, refresh labels, reuse fresh pin maps
for (int i = 0; i < freshEntries.size(); i++) {
SymbolEntry fresh = freshEntries.get(i);
IDIYComponent<?> existing =
i < existingIds.size() ? symbolsById.get(existingIds.get(i)) : null;
if (existing != null) {
copyLabels(fresh.symbol, existing);
resolvedEntries.add(new SymbolEntry(existing, fresh.pinMapping));
resolvedIds.add(existing.getId());
} else {
resolvedEntries.add(fresh);
resolvedIds.add(fresh.symbol.getId());
}
}
// place any brand new sub-symbols (e.g. section count grew) that had no counterpart
if (resolvedEntries.size() > existingIds.size()) {
List<SymbolEntry> added =
resolvedEntries.subList(existingIds.size(), resolvedEntries.size());
SchematicBuilder.orientSymbols(added, railPins.get(physicalId));
SchematicBuilder.placeAtGridSlot(added, occupiedSlots++, occupiedSlots + 1);
}
} else {
// new component
resolvedEntries.addAll(freshEntries);
for (SymbolEntry e : freshEntries) {
resolvedIds.add(e.symbol.getId());
}
SchematicBuilder.orientSymbols(freshEntries, railPins.get(physicalId));
SchematicBuilder.placeAtGridSlot(freshEntries, occupiedSlots++, occupiedSlots + 1);
}
entriesByPhysicalId.put(physicalId, resolvedEntries);
newMap.put(physicalId, resolvedIds);
for (SymbolEntry e : resolvedEntries) {
keptSymbols.add(e.symbol);
}
}
// anything left in the old map that we did not resolve corresponds to a deleted component
Set<UUID> removed = new HashSet<UUID>(map.keySet());
removed.removeAll(newMap.keySet());
if (!removed.isEmpty()) {
LOG.debug("Removing schematic symbols for " + removed.size() + " deleted component(s)");
}
SchematicBuilder.Wiring wiring = builder.createWires(netlist, entriesByPhysicalId);
List<IDIYComponent<?>> result = new ArrayList<IDIYComponent<?>>(keptSymbols);
result.addAll(wiring.getStubs());
result.addAll(wiring.getWires());
result.sort(SchematicBuilder.SCHEMATIC_ORDER);
view.getComponents().clear();
view.getComponents().addAll(result);
view.setPhysicalToSchematicMap(newMap);
SchematicBuilder.resizeCanvasToFit(view);
}
private static void copyLabels(IDIYComponent<?> from, IDIYComponent<?> to) {
to.setName(from.getName());
try {
Object value = from.getValue();
@SuppressWarnings("unchecked")
IDIYComponent<Object> target = (IDIYComponent<Object>) to;
target.setValue(value);
} catch (Exception e) {
// value types differ between fresh and existing symbol; leave the existing value untouched
}
}
public static void synchronize(Project project, Collection<ContinuityArea> continuityAreas,
boolean unused) {
new SchematicSynchronizer().synchronize(project,
continuityAreas == null ? new ArrayList<ContinuityArea>()
: new ArrayList<ContinuityArea>(continuityAreas));
}
}
@@ -1,43 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import org.diylc.components.semiconductors.BJTSymbol;
import org.diylc.core.IDIYComponent;
/**
* Maps physical transistor packages (TO-92, TO-220, TO-3, TO-126, TO-1) to a {@link BJTSymbol}. The
* three package legs are mapped to the symbol legs in index order; if a particular package uses a
* different pin-out the user can rotate/relabel the resulting symbol.
*/
public class TransistorSchematicFactory extends AbstractSimpleSchematicFactory {
@Override
protected IDIYComponent<?> createSymbol() {
return new BJTSymbol();
}
@Override
protected int electricalPinCount() {
return 3;
}
}
@@ -1,270 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.diylc.common.OrientationHV;
/**
* Chooses the bend point of a generated schematic wire.
*
* <p>
* A {@code Right Angle Line} derives its whole route from one interior handle plus the axis it leaves
* the first pin on, so routing here means picking those two rather than emitting a polyline. The
* router proposes a handful of handles - the two L shapes, the mid and quarter points, and a few
* positions outside the box spanned by the two pins that let a wire detour - and keeps the one that
* scores best.
* </p>
*
* <p>
* Scoring is what the previous bend-point rule lacked: it had no idea what else was on the drawing,
* so wires ran through symbols and along each other. Here a route is charged for its length, for
* every corner, for crossing a wire already placed, for running on top of one, and heavily for
* passing through the body of a symbol.
* </p>
*
* @author Branislav Stojkovic
*/
public class WireRouter {
/** Grid the handle is snapped to. */
public static final double GRID = 10d;
/** How far outside the endpoints a detour candidate is offered. */
private static final double DETOUR = 40d;
private static final double LENGTH_COST = 0.01;
private static final double BEND_COST = 6;
private static final double CROSSING_COST = 15;
private static final double OVERLAP_COST = 0.5;
private static final double BODY_COST = 40;
private static final double WRONG_AXIS_COST = 8;
private static final double EPS = 0.5;
/** The chosen route: the axis the wire leaves the first pin on, and its single bend point. */
public static class Route {
private final OrientationHV startDirection;
private final Point2D bendPoint;
Route(OrientationHV startDirection, Point2D bendPoint) {
this.startDirection = startDirection;
this.bendPoint = bendPoint;
}
public OrientationHV getStartDirection() {
return startDirection;
}
public Point2D getBendPoint() {
return bendPoint;
}
}
/**
* @param start the first pin
* @param startAxis the axis the wire ought to leave the first pin on
* @param end the second pin
* @param endAxis the axis the wire ought to arrive at the second pin on
* @param bodies boxes the route should stay out of, the two pins' own symbols excluded
* @param placed segments of the wires already routed
*/
public Route route(Point2D start, OrientationHV startAxis, Point2D end, OrientationHV endAxis,
List<Rectangle2D> bodies, List<Line2D> placed) {
// pins that already share a row or a column want a straight run, which a handle sitting on the
// first pin produces whichever axis the template starts on
if (Math.abs(start.getX() - end.getX()) < 1) {
return new Route(OrientationHV.VERTICAL, copyOf(start));
}
if (Math.abs(start.getY() - end.getY()) < 1) {
return new Route(OrientationHV.HORIZONTAL, copyOf(start));
}
Route best = null;
double bestCost = Double.MAX_VALUE;
for (OrientationHV direction : OrientationHV.values()) {
for (Point2D handle : candidates(start, end)) {
double cost = cost(start, handle, end, direction, startAxis, endAxis, bodies, placed);
if (cost < bestCost) {
bestCost = cost;
best = new Route(direction, handle);
}
}
}
return best;
}
private static List<Point2D> candidates(Point2D start, Point2D end) {
double midX = snap((start.getX() + end.getX()) / 2);
double midY = snap((start.getY() + end.getY()) / 2);
double leftX = snap(Math.min(start.getX(), end.getX()) - DETOUR);
double rightX = snap(Math.max(start.getX(), end.getX()) + DETOUR);
double topY = snap(Math.min(start.getY(), end.getY()) - DETOUR);
double bottomY = snap(Math.max(start.getY(), end.getY()) + DETOUR);
List<Point2D> result = new ArrayList<Point2D>();
// the two L shapes
result.add(new Point2D.Double(snap(end.getX()), snap(start.getY())));
result.add(new Point2D.Double(snap(start.getX()), snap(end.getY())));
// a leg down the middle, which is the shape most schematics use
result.add(new Point2D.Double(midX, snap(start.getY())));
result.add(new Point2D.Double(midX, snap(end.getY())));
result.add(new Point2D.Double(snap(start.getX()), midY));
result.add(new Point2D.Double(snap(end.getX()), midY));
result.add(new Point2D.Double(midX, midY));
// detours around whatever sits between the two pins
result.add(new Point2D.Double(leftX, midY));
result.add(new Point2D.Double(rightX, midY));
result.add(new Point2D.Double(midX, topY));
result.add(new Point2D.Double(midX, bottomY));
return result;
}
private double cost(Point2D start, Point2D handle, Point2D end, OrientationHV direction,
OrientationHV startAxis, OrientationHV endAxis, List<Rectangle2D> bodies,
List<Line2D> placed) {
List<Point2D> vertices = vertices(start, handle, end, direction);
List<Line2D> segments = segments(vertices);
if (segments.isEmpty()) {
return Double.MAX_VALUE;
}
double cost = BEND_COST * Math.max(0, vertices.size() - 2);
for (Line2D segment : segments) {
cost += LENGTH_COST * segment.getP1().distance(segment.getP2());
for (Rectangle2D body : bodies) {
if (body.intersectsLine(segment)) {
cost += BODY_COST;
}
}
for (Line2D other : placed) {
if (crosses(segment, other)) {
cost += CROSSING_COST;
}
cost += OVERLAP_COST * overlapLength(segment, other);
}
}
if (axisOf(segments.get(0)) != startAxis) {
cost += WRONG_AXIS_COST;
}
if (axisOf(segments.get(segments.size() - 1)) != endAxis) {
cost += WRONG_AXIS_COST;
}
return cost;
}
/**
* Expands a start, a bend point and an end into the vertices of the route, the same way
* {@code AbstractOrthogonalComponent} does: every control point is reached by a move along one axis
* followed by a move along the other. Zero length steps are dropped as they are produced.
*/
public static List<Point2D> vertices(Point2D start, Point2D handle, Point2D end,
OrientationHV startDirection) {
boolean horizontalFirst = startDirection == OrientationHV.HORIZONTAL;
List<Point2D> vertices = new ArrayList<Point2D>();
add(vertices, start);
Point2D previous = start;
for (Point2D next : new Point2D[] {handle, end}) {
if (horizontalFirst) {
add(vertices, new Point2D.Double(next.getX(), previous.getY()));
} else {
add(vertices, new Point2D.Double(previous.getX(), next.getY()));
}
add(vertices, next);
previous = next;
}
return vertices;
}
private static void add(List<Point2D> vertices, Point2D p) {
if (!vertices.isEmpty() && vertices.get(vertices.size() - 1).distance(p) < EPS) {
return;
}
vertices.add(new Point2D.Double(p.getX(), p.getY()));
}
private static List<Line2D> segments(List<Point2D> vertices) {
List<Line2D> segments = new ArrayList<Line2D>();
for (int i = 1; i < vertices.size(); i++) {
segments.add(new Line2D.Double(vertices.get(i - 1), vertices.get(i)));
}
return segments;
}
private static OrientationHV axisOf(Line2D segment) {
return Math.abs(segment.getX2() - segment.getX1()) >= Math.abs(segment.getY2() - segment.getY1())
? OrientationHV.HORIZONTAL
: OrientationHV.VERTICAL;
}
/** @return true when the two segments genuinely cross, rather than merely touching end to end. */
private static boolean crosses(Line2D a, Line2D b) {
if (axisOf(a) == axisOf(b)) {
return false;
}
if (!a.intersectsLine(b)) {
return false;
}
// a shared endpoint is a junction, not a crossing
return !(near(a.getP1(), b.getP1()) || near(a.getP1(), b.getP2()) || near(a.getP2(), b.getP1())
|| near(a.getP2(), b.getP2()));
}
/** @return the length the two segments run along one another, which reads as a single thick line. */
private static double overlapLength(Line2D a, Line2D b) {
if (axisOf(a) != axisOf(b)) {
return 0;
}
if (axisOf(a) == OrientationHV.HORIZONTAL) {
if (Math.abs(a.getY1() - b.getY1()) > EPS) {
return 0;
}
return span(a.getX1(), a.getX2(), b.getX1(), b.getX2());
}
if (Math.abs(a.getX1() - b.getX1()) > EPS) {
return 0;
}
return span(a.getY1(), a.getY2(), b.getY1(), b.getY2());
}
private static double span(double a1, double a2, double b1, double b2) {
double low = Math.max(Math.min(a1, a2), Math.min(b1, b2));
double high = Math.min(Math.max(a1, a2), Math.max(b1, b2));
return Math.max(0, high - low);
}
private static boolean near(Point2D a, Point2D b) {
return a.distance(b) < EPS;
}
private static Point2D copyOf(Point2D p) {
return new Point2D.Double(p.getX(), p.getY());
}
private static double snap(double value) {
return Math.round(value / GRID) * GRID;
}
}
@@ -1,54 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import org.diylc.common.ComponentType;
import org.diylc.components.semiconductors.BJTSymbol;
import org.diylc.core.IDIYComponent;
/**
* Derives the direction of a drawn transistor symbol from its fixed geometry.
*
* <p>
* A {@code BJTSymbol} names its pins {@code Q1.0}, {@code Q1.1} and so on, so there is nothing to
* read. Its three legs are however always built in the same order: control point 0 is the lone pin on
* the left, which is the base, and of the two on the right the upper one is the collector.
* </p>
*
* <p>
* This rule is deliberately restricted to the transistor symbol rather than to every three legged
* symbol, because a potentiometer shares that geometry while being entirely passive and would be
* given a direction it does not have.
* </p>
*
* @author Branislav Stojkovic
*/
public class BjtSymbolFlowRule implements ISignalFlowRule {
@Override
public FlowHints evaluate(IDIYComponent<?> component, ComponentType type) {
if (!(component instanceof BJTSymbol) || component.getControlPointCount() < 3) {
return null;
}
return FlowHints.builder().input(0).output(1).neutral(2).build();
}
}
@@ -1,98 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* What an {@link ISignalFlowRule} worked out about one component: the {@link FlowRole} of each of its
* control points. Points the rule said nothing about read back as {@link FlowRole#NEUTRAL}.
*
* @author Branislav Stojkovic
*/
public class FlowHints {
private final Map<Integer, FlowRole> roles;
private FlowHints(Map<Integer, FlowRole> roles) {
this.roles = Collections.unmodifiableMap(roles);
}
public static Builder builder() {
return new Builder();
}
public FlowRole getRole(int pointIndex) {
FlowRole role = roles.get(pointIndex);
return role == null ? FlowRole.NEUTRAL : role;
}
public Map<Integer, FlowRole> getRoles() {
return roles;
}
/**
* @return true if at least one point is an input or an output. A set of hints that is entirely
* neutral says nothing about direction, so the analyzer treats it as no answer at all.
*/
public boolean hasDirection() {
for (FlowRole role : roles.values()) {
if (role == FlowRole.INPUT || role == FlowRole.OUTPUT) {
return true;
}
}
return false;
}
@Override
public String toString() {
return roles.toString();
}
public static class Builder {
private final Map<Integer, FlowRole> roles = new HashMap<Integer, FlowRole>();
public Builder role(int pointIndex, FlowRole role) {
roles.put(pointIndex, role);
return this;
}
public Builder input(int pointIndex) {
return role(pointIndex, FlowRole.INPUT);
}
public Builder output(int pointIndex) {
return role(pointIndex, FlowRole.OUTPUT);
}
public Builder neutral(int pointIndex) {
return role(pointIndex, FlowRole.NEUTRAL);
}
public FlowHints build() {
return new FlowHints(roles);
}
}
}
@@ -1,43 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
/**
* Direction in which signal travels through one control point of a component. Used to orient the
* generated schematic so that it reads from input on the left to output on the right.
*
* @author Branislav Stojkovic
*/
public enum FlowRole {
/** Signal enters the component here: a base, a gate, a control grid. */
INPUT,
/** Signal leaves the component here: a collector, a drain, a plate, a pickup terminal. */
OUTPUT,
/**
* The point carries no direction of its own. Emitters, cathodes, heaters, supply and ground legs
* and both ends of a passive part all fall here.
*/
NEUTRAL
}
@@ -1,53 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import org.diylc.common.ComponentType;
import org.diylc.core.IDIYComponent;
/**
* Works out which control points of a component signal enters and leaves by, so that the schematic
* generator can lay the circuit out in the direction it actually runs.
*
* <p>
* A rule speaks only about components it recognises and returns {@code null} for everything else,
* which lets the next rule try. {@link SignalFlowAnalyzer} holds the rules in priority order, most
* specific first, and takes the first answer that carries a direction.
* </p>
*
* <p>
* Implementations must be stateless; one instance is created and reused for the life of the
* application.
* </p>
*
* @author Branislav Stojkovic
*/
public interface ISignalFlowRule {
/**
* @param component the physical layout component
* @param type the component's type, or null when it could not be resolved
* @return roles of the component's control points, or null if this rule has nothing to say about
* this component
*/
FlowHints evaluate(IDIYComponent<?> component, ComponentType type);
}
@@ -1,69 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import org.diylc.common.ComponentType;
import org.diylc.core.IDIYComponent;
import org.diylc.netlist.Node;
/**
* Last resort that reads the direction off pin names, for the many modules and sensors that label
* their terminals {@code IN+}, {@code OUT-} and the like.
*
* <p>
* It runs after every type aware rule, so a component that one of those recognises is never subject
* to a name that happens to look directional. Names that carry no direction, such as {@code VCC},
* {@code GND} or the battery terminals of a charger module, are left neutral, and a component whose
* pins are merely numbered produces no answer at all.
* </p>
*
* @author Branislav Stojkovic
*/
public class NamedPinFlowRule implements ISignalFlowRule {
@Override
public FlowHints evaluate(IDIYComponent<?> component, ComponentType type) {
FlowHints.Builder builder = FlowHints.builder();
for (int i = 0; i < component.getControlPointCount(); i++) {
builder.role(i, roleOf(component.getControlPointNodeName(i)));
}
FlowHints hints = builder.build();
return hints.hasDirection() ? hints : null;
}
private static FlowRole roleOf(String pinName) {
String name = Node.sanitizeNodeName(pinName);
if (name == null) {
return FlowRole.NEUTRAL;
}
String label = name.trim().toUpperCase();
if (label.equals("IN") || label.startsWith("IN+") || label.startsWith("IN-")
|| label.equals("INPUT")) {
return FlowRole.INPUT;
}
if (label.equals("OUT") || label.startsWith("OUT+") || label.startsWith("OUT-")
|| label.equals("OUTPUT")) {
return FlowRole.OUTPUT;
}
return FlowRole.NEUTRAL;
}
}
@@ -1,59 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import org.diylc.common.ComponentType;
import org.diylc.components.guitar.AbstractGuitarPickup;
import org.diylc.core.IDIYComponent;
/**
* Treats a guitar pickup as the origin of the signal, so that everything it reaches is downstream of
* it.
*
* <p>
* This is what gives a guitar wiring diagram a direction at all: those circuits are entirely passive,
* so no active device is there to say which way the signal runs. Pickups name no pins, so the rule
* keys on the type and marks every live terminal as an output.
* </p>
*
* @author Branislav Stojkovic
*/
public class PickupFlowRule implements ISignalFlowRule {
@Override
public FlowHints evaluate(IDIYComponent<?> component, ComponentType type) {
if (!(component instanceof AbstractGuitarPickup)) {
return null;
}
FlowHints.Builder builder = FlowHints.builder();
boolean any = false;
for (int i = 0; i < component.getControlPointCount(); i++) {
if (component.isControlPointSticky(i)) {
builder.output(i);
any = true;
} else {
builder.neutral(i);
}
}
return any ? builder.build() : null;
}
}
@@ -1,80 +0,0 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.schematic.flow;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.diylc.common.ComponentType;
import org.diylc.core.IDIYComponent;
import org.diylc.presenter.ComponentProcessor;
/**
* Works out which way signal runs through a component by asking each {@link ISignalFlowRule} in turn
* and taking the first answer that carries a direction.
*
* <p>
* The rules are held in an explicit list rather than discovered by scanning, because their order is
* their priority: the ones that read real per instance data come before the ones that infer from
* geometry, and the rule that merely reads pin names comes last. Adding a rule means writing the
* class and placing it in this list at the specificity it deserves.
* </p>
*
* @author Branislav Stojkovic
*/
public class SignalFlowAnalyzer {
private static final List<ISignalFlowRule> RULES = Collections.unmodifiableList(Arrays.asList(
new TransistorPinoutFlowRule(),
new TubeElectrodeFlowRule(),
new TubeSymbolFlowRule(),
new BjtSymbolFlowRule(),
new PickupFlowRule(),
new NamedPinFlowRule()));
/**
* @param component the physical layout component
* @return the first directional answer any rule had about the component, or null when none of them
* recognised it, which is the normal outcome for passive parts
*/
@SuppressWarnings("unchecked")
public FlowHints analyze(IDIYComponent<?> component) {
if (component == null) {
return null;
}
ComponentType type = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
for (ISignalFlowRule rule : RULES) {
FlowHints hints = rule.evaluate(component, type);
if (hints != null && hints.hasDirection()) {
return hints;
}
}
return null;
}
/** @return the rules in priority order. */
public static List<ISignalFlowRule> getRules() {
return RULES;
}
}

Some files were not shown because too many files have changed in this diff Show More