Compare commits

...

35 Commits

Author SHA1 Message Date
Bane Stojkovic 5944f4d36f Update update.xml 2025-05-19 22:55:21 +02:00
Bane Stojkovic 4fb17d343d hack trimmer to show terminals in the correct order 2025-05-19 22:54:23 +02:00
Bane Stojkovic 144f6e1c9f merge 2025-05-19 22:25:09 +02:00
Bane Stojkovic c9252f7c98 add xsmall2 2025-05-18 12:55:51 +02:00
Bane Stojkovic 7913df0767 improve analysis reports 2025-05-18 11:41:30 +02:00
Bane Stojkovic 48bb11955d improve styling of summarize reports 2025-05-18 11:31:35 +02:00
Bane Stojkovic be43ef559c Improved trace proximity calculation to avoid showing false positives 2025-05-18 11:14:17 +02:00
Bane Stojkovic b757952533 If the internet connection is present, but unstable, the application starts but nothing can be clicked until the connection restores 2025-05-17 21:28:26 +02:00
Bane Stojkovic fe0dadd64b Increased tolerance of sticky points on guitar pickup terminals in order to make it easier for leads to make proper contact and for the circuit analyzer to work 2025-05-16 12:10:35 +02:00
Bane Stojkovic f1d886865d release 2025-05-13 10:15:06 +02:00
Bane Stojkovic 17c8840dfc Merge pull request #925 from bancika/ai-analyzer
Ai analyzer
2025-05-13 10:03:56 +02:00
Bane Stojkovic d9384d8304 release 2025-05-13 10:02:40 +02:00
Bane Stojkovic a69584411e Fixed the issue with embedding image files into DIY projects 2025-05-09 19:53:28 +02:00
Bane Stojkovic e4f7fb40d7 rendering performance optimization 2025-05-08 22:55:52 +02:00
Bane Stojkovic b2dcf354e6 add icon 2025-05-08 22:20:26 +02:00
Bane Stojkovic a6a9cd1bdf Fixed the issue with some components (e.g. DIP IC) jumping when showing underside of a board 2025-05-03 23:13:02 +02:00
Bane Stojkovic a21ef1962c Update update.xml 2025-05-03 22:08:07 +02:00
Bane Stojkovic c90b722fb6 optimize rendering of guitar switches 2025-05-03 22:05:23 +02:00
Bane Stojkovic 3332b727e0 optimize guitar pickup rendering 2025-05-03 21:56:38 +02:00
Bane Stojkovic 24ee6b3a5f optimize drawing of semiconductors 2025-05-03 21:49:01 +02:00
Bane Stojkovic aa98f27de8 Optimize rendering of electro-mechanical components 2025-05-03 21:35:11 +02:00
Bane Stojkovic b4bcc25f66 'Open Jack 1/4"' and 'Potentiometer Panel' now tolerate connections anywhere on the lugs instead of dead center only 2025-04-30 22:34:23 +02:00
Bane Stojkovic 355c9f2b49 netlist fixes 2025-04-25 22:50:07 +02:00
Bane Stojkovic 5c09ebf7f8 knowledge 2025-04-25 22:11:11 +02:00
Bane Stojkovic f7cd33c80f analyzer added 2025-04-24 21:38:04 +02:00
Bane Stojkovic c914749413 Ai improvements 1 (#914)
* improve netlist

* structure improvement

* chatbot enhancements

* chatbot improvements

* charbot enhancements

* Update user_manual.sql

* chatbot enhancements

* release
2025-04-21 20:46:41 +02:00
Bane Stojkovic b42fe2c34f disable chatbot by default 2025-04-15 20:46:45 +02:00
Bane Stojkovic 3d659e4e75 Update update.xml 2025-04-15 18:34:27 +02:00
Bane Stojkovic 7a7e3982f3 Language selection doesn't work on some OSs 2025-04-15 18:21:37 +02:00
Bane Stojkovic 11242dcb10 retain chat history when saving to a new file name 2025-04-14 22:31:56 +02:00
Bane Stojkovic 6a0ee802eb Output current subscription statistics at the end of the chat for transparency 2025-04-14 22:08:02 +02:00
Bane Stojkovic 2e13cc6a49 Confirmation dialog before deleting AI Assistant chat history 2025-04-14 21:31:33 +02:00
Bane Stojkovic aae514afcb 'Connect to Patreon' option doesn't work if the user name contains a space or another non-alphanumerical character 2025-04-14 21:04:32 +02:00
Bane Stojkovic 9dda441375 tier message update 2025-04-12 21:52:01 +02:00
Bane Stojkovic 01c8df99b3 Merge pull request #910 from bancika/feature/chatbot
Feature/chatbot
2025-04-12 21:40:14 +02:00
93 changed files with 3144 additions and 590 deletions
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" ?>
<project>
<fileVersion>
<major>5</major>
<minor>5</minor>
<build>0</build>
</fileVersion>
<title>New Project</title>
<author>branislavstojkovic</author>
<width value="29.0" unit="cm"/>
<height value="21.0" unit="cm"/>
<gridSpacing value="0.1" unit="in"/>
<dotSpacing>1</dotSpacing>
<components>
<diylc.connectivity.CopperTrace>
<name>Trace1</name>
<alpha>127</alpha>
<points>
<point x="5.9" y="3.4"/>
<point x="6.4" y="3.4"/>
<point x="6.15" y="3.4"/>
</points>
<bodyColor hex="ffffff"/>
<borderColor hex="000000"/>
<labelColor hex="000000"/>
<leadColor hex="000000"/>
<display>NAME</display>
<flipStanding>false</flipStanding>
<labelOriantation>Directional</labelOriantation>
<moveLabel>false</moveLabel>
<hideShortLeads>false</hideShortLeads>
<thickness value="1.0" unit="mm"/>
<layer>_1</layer>
</diylc.connectivity.CopperTrace>
<diylc.connectivity.CopperTrace>
<name>Trace2</name>
<alpha>127</alpha>
<points>
<point x="6.005" y="3.45"/>
<point x="6.305" y="3.45"/>
<point x="6.155" y="3.45"/>
</points>
<bodyColor hex="ffffff"/>
<borderColor hex="000000"/>
<labelColor hex="000000"/>
<leadColor hex="000000"/>
<display>NAME</display>
<flipStanding>false</flipStanding>
<labelOriantation>Directional</labelOriantation>
<moveLabel>false</moveLabel>
<hideShortLeads>false</hideShortLeads>
<thickness value="1.0" unit="mm"/>
<layer>_1</layer>
</diylc.connectivity.CopperTrace>
<diylc.connectivity.SolderPad>
<name>Pad1</name>
<alpha>127</alpha>
<size value="0.09" unit="in"/>
<color hex="000000"/>
<point x="5.9" y="3.4"/>
<type>ROUND</type>
<holeSize value="0.8" unit="mm"/>
<layer>_1</layer>
<holeType>NTPH</holeType>
</diylc.connectivity.SolderPad>
<diylc.connectivity.SolderPad>
<name>Pad2</name>
<alpha>127</alpha>
<size value="0.09" unit="in"/>
<color hex="000000"/>
<point x="6.4" y="3.4"/>
<type>ROUND</type>
<holeSize value="0.8" unit="mm"/>
<layer>_1</layer>
<holeType>NTPH</holeType>
</diylc.connectivity.SolderPad>
</components>
<groups/>
<lockedLayers/>
<lockedComponents/>
<hiddenLayers/>
<font name="Dialog" size="14" style="0"/>
</project>
@@ -0,0 +1,534 @@
<?xml version="1.0" encoding="UTF-8" ?>
<project>
<fileVersion>
<major>5</major>
<minor>5</minor>
<build>0</build>
</fileVersion>
<title>New Project</title>
<author>bancika</author>
<width value="10.0" unit="cm"/>
<height value="14.0" unit="cm"/>
<gridSpacing value="0.1" unit="in"/>
<dotSpacing>1</dotSpacing>
<components>
<diylc.passive.PotentiometerPanel>
<name>Volume</name>
<alpha>127</alpha>
<controlPoints>
<point x="1.8" y="4.5"/>
<point x="2.0" y="4.5"/>
<point x="2.2" y="4.5"/>
</controlPoints>
<resistance value="250.0" unit="K"/>
<orientation>DEFAULT</orientation>
<taper>LOG</taper>
<bodyDiameter value="16.0" unit="mm"/>
<spacing value="0.2" unit="in"/>
<lugDiameter value="0.15" unit="in"/>
<bodyColor hex="b6b6b6"/>
<borderColor hex="808080"/>
<nutColor hex="cbd5db"/>
<waferColor hex="cd8500"/>
<type>ThroughHole</type>
<showShaft>false</showShaft>
<view>ShaftDown</view>
</diylc.passive.PotentiometerPanel>
<diylc.connectivity.HookupWire>
<name>W7</name>
<alpha>127</alpha>
<controlPoints2>
<point x="1.9" y="2.5"/>
<point x="1.5" y="3.6"/>
<point x="1.0" y="3.9"/>
<point x="1.8" y="4.5"/>
</controlPoints2>
<color hex="cc00cc"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.guitar.SingleCoilPickup>
<name>Neck</name>
<alpha>127</alpha>
<value>Chopper</value>
<orientation>_90</orientation>
<controlPoint x="3.0" y="2.4"/>
<controlPoints>
<point x="3.0" y="2.4"/>
<point x="3.0" y="2.3"/>
<point x="3.0" y="2.2"/>
<point x="3.0" y="2.1"/>
</controlPoints>
<polarity>Humbucking</polarity>
<labelColor hex="ffffff"/>
<color hex="666666"/>
<poleColor hex="333333"/>
<baseColor hex="666666"/>
<type>Stratocaster</type>
<polePieceType>RailHumbucker</polePieceType>
<lugColor hex="e0c04c"/>
</diylc.guitar.SingleCoilPickup>
<diylc.guitar.SingleCoilPickup>
<name>Bridge</name>
<alpha>127</alpha>
<value></value>
<orientation>_270</orientation>
<controlPoint x="0.8" y="2.1"/>
<controlPoints>
<point x="0.8" y="2.1"/>
<point x="0.8" y="2.2"/>
<point x="0.8" y="2.3"/>
<point x="0.8" y="2.4"/>
</controlPoints>
<polarity>Humbucking</polarity>
<labelColor hex="ffffff"/>
<color hex="666666"/>
<poleColor hex="333333"/>
<baseColor hex="666666"/>
<type>Stratocaster</type>
<polePieceType>RailHumbucker</polePieceType>
<lugColor hex="e0c04c"/>
</diylc.guitar.SingleCoilPickup>
<diylc.guitar.LPSwitch>
<name>SW1</name>
<alpha>127</alpha>
<value></value>
<controlPoints>
<point x="1.9" y="1.2"/>
<point x="1.7" y="2.495"/>
<point x="1.9" y="2.495"/>
<point x="2.1" y="2.495"/>
</controlPoints>
<orientation>DEFAULT</orientation>
</diylc.guitar.LPSwitch>
<diylc.electromechanical.MiniToggleSwitch>
<name defined-in="diylc.AbstractComponent">Split</name>
<alpha>127</alpha>
<controlPoints>
<point x="1.9" y="3.5"/>
<point x="1.9" y="3.7"/>
<point x="1.9" y="3.9"/>
<point x="2.1" y="3.5"/>
<point x="2.1" y="3.7"/>
<point x="2.1" y="3.9"/>
</controlPoints>
<switchType>DPDT</switchType>
<orientation>VERTICAL</orientation>
<spacing value="0.2" unit="in"/>
<bodyColor hex="3299cc"/>
<borderColor hex="236b8e"/>
<terminalPadColor hex="287ba4"/>
<terminalColor hex="eeeeee"/>
<showMarkers>false</showMarkers>
</diylc.electromechanical.MiniToggleSwitch>
<diylc.electromechanical.OpenJack1__4>
<name>J1</name>
<alpha>127</alpha>
<controlPoints>
<point x="2.7" y="4.9"/>
<point x="2.9" y="4.5"/>
<point x="3.6" y="4.6"/>
</controlPoints>
<orientation>_270</orientation>
<angle>270</angle>
<type>MONO</type>
<showLabels>true</showLabels>
</diylc.electromechanical.OpenJack1__4>
<diylc.connectivity.HookupWire>
<name>W2</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.3"/>
<point x="1.2" y="2.6"/>
<point x="1.7" y="3.4"/>
<point x="1.9" y="3.7"/>
</controlPoints2>
<color hex="ffffff"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W2</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.2"/>
<point x="1.5" y="2.6"/>
<point x="1.7" y="3.5"/>
<point x="1.9" y="3.7"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.4"/>
<point x="1.0" y="4.1"/>
<point x="2.1" y="4.6"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="3.0" y="2.3"/>
<point x="2.4" y="3.0"/>
<point x="2.8" y="4.0"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W4</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.1"/>
<point x="1.1" y="2.3"/>
<point x="1.4" y="2.7"/>
<point x="1.7" y="2.6"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.7" y="4.2"/>
<point x="2.3" y="4.6"/>
<point x="2.9" y="4.5"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.0" y="4.5"/>
<point x="2.3" y="4.7"/>
<point x="2.3" y="5.1"/>
<point x="2.7" y="4.9"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.9"/>
<point x="2.2" y="3.9"/>
<point x="2.3" y="4.0"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W6</name>
<alpha>127</alpha>
<controlPoints2>
<point x="1.9" y="3.9"/>
<point x="1.9" y="3.8"/>
<point x="2.1" y="3.8"/>
<point x="2.1" y="3.9"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="3.0" y="2.2"/>
<point x="2.6" y="2.6"/>
<point x="2.1" y="2.6"/>
<point x="2.1" y="2.5"/>
</controlPoints2>
<color hex="ffffff"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W4</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.7"/>
<point x="2.2" y="3.5"/>
<point x="2.5" y="3.2"/>
<point x="3.0" y="2.4"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W6</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.7"/>
<point x="2.3" y="3.5"/>
<point x="2.7" y="2.1"/>
<point x="3.0" y="2.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.passive.AxialFilmCapacitor>
<name>Cbleed</name>
<alpha>127</alpha>
<length value="0.4" unit="in"/>
<width value="0.125" unit="in"/>
<points>
<point x="1.8" y="4.5"/>
<point x="2.0" y="4.5"/>
<point x="1.9" y="4.5"/>
</points>
<bodyColor hex="ffe303"/>
<borderColor hex="b29e02"/>
<labelColor hex="000000"/>
<leadColor hex="cccccc"/>
<display>VALUE</display>
<flipStanding>false</flipStanding>
<labelOriantation>Directional</labelOriantation>
<moveLabel>false</moveLabel>
<hideShortLeads>false</hideShortLeads>
<value value="1.2" unit="nF"/>
<voltage>_63V</voltage>
<showOuterFoil>false</showOuterFoil>
<outerFoilColor hex="ffffff"/>
</diylc.passive.AxialFilmCapacitor>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.4" y="4.2"/>
<point x="2.6" y="4.0"/>
<point x="2.9" y="4.1"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.3" y="4.2"/>
<point x="2.3" y="4.4"/>
<point x="2.2" y="4.5"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.misc.Label>
<name>L1</name>
<point x="3.3" y="4.1"/>
<text>To bridge baseplate</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L2</name>
<point x="1.5" y="4.8"/>
<text>Treble bleed</text>
<font reference="../../diylc.misc.Label/font"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L3</name>
<point x="2.0" y="0.2"/>
<text>Travelcaster Wiring Diagram</text>
<font name="Dialog" size="36" style="0"/>
<color hex="0000cc"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L3</name>
<point x="2.0" y="0.3"/>
<text>www.diy-fever.com</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="1.9" y="1.1"/>
<text>Pickup selector switch</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="2.0" y="3.2"/>
<text>Split mode</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="2.0" y="3.3"/>
<text>Push-pull</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L5</name>
<point x="0.4" y="3.1"/>
<text>DiMarzio</text>
<font name="Dialog" size="18" style="1"/>
<color hex="ffffff"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>_270</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L5</name>
<point x="3.4" y="1.4"/>
<text>DiMarzio</text>
<font name="Dialog" size="18" style="1"/>
<color hex="ffffff"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>_90</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L6</name>
<point x="0.1" y="5.4"/>
<text>* One of the pickups is rotated 180 degrees for cleaner drawing</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>LEFT</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L1</name>
<point x="3.6" y="4.5"/>
<text>Output jack</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
</components>
<groups/>
<lockedLayers/>
<lockedComponents/>
<hiddenLayers/>
<font name="Tahoma" size="14" style="0"/>
</project>
@@ -0,0 +1,574 @@
<?xml version="1.0" encoding="UTF-8" ?>
<project>
<fileVersion>
<major>5</major>
<minor>5</minor>
<build>0</build>
</fileVersion>
<title>New Project</title>
<author>bancika</author>
<width value="10.0" unit="cm"/>
<height value="14.0" unit="cm"/>
<gridSpacing value="0.1" unit="in"/>
<dotSpacing>1</dotSpacing>
<components>
<diylc.passive.PotentiometerPanel>
<name>Volume</name>
<alpha>127</alpha>
<controlPoints>
<point x="1.8" y="4.5"/>
<point x="2.0" y="4.5"/>
<point x="2.2" y="4.5"/>
</controlPoints>
<resistance value="250.0" unit="K"/>
<orientation>DEFAULT</orientation>
<taper>LOG</taper>
<bodyDiameter value="16.0" unit="mm"/>
<spacing value="0.2" unit="in"/>
<lugDiameter value="0.15" unit="in"/>
<bodyColor hex="b6b6b6"/>
<borderColor hex="808080"/>
<nutColor hex="cbd5db"/>
<waferColor hex="cd8500"/>
<type>ThroughHole</type>
<showShaft>false</showShaft>
<view>ShaftDown</view>
</diylc.passive.PotentiometerPanel>
<diylc.connectivity.HookupWire>
<name>W7</name>
<alpha>127</alpha>
<controlPoints2>
<point x="1.9" y="2.5"/>
<point x="1.5" y="3.6"/>
<point x="1.0" y="3.9"/>
<point x="1.8" y="4.5"/>
</controlPoints2>
<color hex="cc00cc"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.guitar.SingleCoilPickup>
<name>Neck</name>
<alpha>127</alpha>
<value>Chopper</value>
<orientation>_90</orientation>
<controlPoint x="3.0" y="2.4"/>
<controlPoints>
<point x="3.0" y="2.4"/>
<point x="3.0" y="2.3"/>
<point x="3.0" y="2.2"/>
<point x="3.0" y="2.1"/>
</controlPoints>
<polarity>Humbucking</polarity>
<labelColor hex="ffffff"/>
<color hex="666666"/>
<poleColor hex="333333"/>
<baseColor hex="666666"/>
<type>Stratocaster</type>
<polePieceType>RailHumbucker</polePieceType>
<lugColor hex="e0c04c"/>
</diylc.guitar.SingleCoilPickup>
<diylc.guitar.SingleCoilPickup>
<name>Bridge</name>
<alpha>127</alpha>
<value></value>
<orientation>_270</orientation>
<controlPoint x="0.8" y="2.1"/>
<controlPoints>
<point x="0.8" y="2.1"/>
<point x="0.8" y="2.2"/>
<point x="0.8" y="2.3"/>
<point x="0.8" y="2.4"/>
</controlPoints>
<polarity>Humbucking</polarity>
<labelColor hex="ffffff"/>
<color hex="666666"/>
<poleColor hex="333333"/>
<baseColor hex="666666"/>
<type>Stratocaster</type>
<polePieceType>RailHumbucker</polePieceType>
<lugColor hex="e0c04c"/>
</diylc.guitar.SingleCoilPickup>
<diylc.guitar.LPSwitch>
<name>SW1</name>
<alpha>127</alpha>
<value></value>
<controlPoints>
<point x="1.9" y="1.2"/>
<point x="1.7" y="2.495"/>
<point x="1.9" y="2.495"/>
<point x="2.1" y="2.495"/>
</controlPoints>
<orientation>DEFAULT</orientation>
</diylc.guitar.LPSwitch>
<diylc.electromechanical.MiniToggleSwitch>
<name defined-in="diylc.AbstractComponent">Split</name>
<alpha>127</alpha>
<controlPoints>
<point x="1.9" y="3.5"/>
<point x="1.9" y="3.7"/>
<point x="1.9" y="3.9"/>
<point x="2.1" y="3.5"/>
<point x="2.1" y="3.7"/>
<point x="2.1" y="3.9"/>
</controlPoints>
<switchType>DPDT</switchType>
<orientation>VERTICAL</orientation>
<spacing value="0.2" unit="in"/>
<bodyColor hex="3299cc"/>
<borderColor hex="236b8e"/>
<terminalPadColor hex="287ba4"/>
<terminalColor hex="eeeeee"/>
<showMarkers>false</showMarkers>
</diylc.electromechanical.MiniToggleSwitch>
<diylc.electromechanical.OpenJack1__4>
<name>J1</name>
<alpha>127</alpha>
<controlPoints>
<point x="2.7" y="4.9"/>
<point x="2.9" y="4.5"/>
<point x="3.6" y="4.6"/>
</controlPoints>
<orientation>_270</orientation>
<angle>270</angle>
<type>MONO</type>
<showLabels>true</showLabels>
</diylc.electromechanical.OpenJack1__4>
<diylc.connectivity.HookupWire>
<name>W2</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.3"/>
<point x="1.2" y="2.6"/>
<point x="1.7" y="3.4"/>
<point x="1.9" y="3.7"/>
</controlPoints2>
<color hex="ffffff"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W2</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.2"/>
<point x="1.5" y="2.6"/>
<point x="1.7" y="3.5"/>
<point x="1.9" y="3.7"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.4"/>
<point x="1.0" y="4.1"/>
<point x="2.1" y="4.6"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="3.0" y="2.3"/>
<point x="2.4" y="3.0"/>
<point x="2.8" y="4.0"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W4</name>
<alpha>127</alpha>
<controlPoints2>
<point x="0.8" y="2.1"/>
<point x="1.1" y="2.3"/>
<point x="1.4" y="2.7"/>
<point x="1.7" y="2.5"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.7" y="4.2"/>
<point x="2.3" y="4.6"/>
<point x="2.9" y="4.5"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.0" y="4.5"/>
<point x="2.3" y="4.7"/>
<point x="2.3" y="5.1"/>
<point x="2.7" y="4.9"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.9"/>
<point x="2.2" y="3.9"/>
<point x="2.3" y="4.0"/>
<point x="2.2" y="4.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W6</name>
<alpha>127</alpha>
<controlPoints2>
<point x="1.9" y="3.9"/>
<point x="1.9" y="3.8"/>
<point x="2.1" y="3.8"/>
<point x="2.1" y="3.9"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W3</name>
<alpha>127</alpha>
<controlPoints2>
<point x="3.0" y="2.2"/>
<point x="2.6" y="2.6"/>
<point x="2.1" y="2.6"/>
<point x="2.1" y="2.5"/>
</controlPoints2>
<color hex="ffffff"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W4</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.7"/>
<point x="2.2" y="3.5"/>
<point x="2.5" y="3.2"/>
<point x="3.0" y="2.4"/>
</controlPoints2>
<color hex="ff0000"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W6</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.1" y="3.7"/>
<point x="2.3" y="3.5"/>
<point x="2.7" y="2.1"/>
<point x="3.0" y="2.1"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.passive.AxialFilmCapacitor>
<name>Cbleed</name>
<alpha>127</alpha>
<length value="0.4" unit="in"/>
<width value="0.125" unit="in"/>
<points>
<point x="1.8" y="5.1"/>
<point x="1.8" y="4.5"/>
<point x="1.8" y="4.8"/>
</points>
<bodyColor hex="ffe303"/>
<borderColor hex="b29e02"/>
<labelColor hex="000000"/>
<leadColor hex="cccccc"/>
<display>VALUE</display>
<flipStanding>false</flipStanding>
<labelOriantation>Directional</labelOriantation>
<moveLabel>false</moveLabel>
<value value="1.2" unit="nF"/>
<voltage>_63V</voltage>
<showOuterFoil>false</showOuterFoil>
<outerFoilColor hex="ffffff"/>
</diylc.passive.AxialFilmCapacitor>
<diylc.passive.Resistor>
<name>Rbleed</name>
<alpha>127</alpha>
<length value="0.4" unit="in"/>
<width value="0.1" unit="in"/>
<points>
<point x="2.0" y="4.5"/>
<point x="2.0" y="5.1"/>
<point x="2.0" y="4.8"/>
</points>
<bodyColor hex="82cffd"/>
<borderColor hex="5b90b1"/>
<labelColor hex="000000"/>
<leadColor hex="cccccc"/>
<display>VALUE</display>
<flipStanding>false</flipStanding>
<labelOriantation>Directional</labelOriantation>
<moveLabel>false</moveLabel>
<value value="130.0" unit="K"/>
<power>HALF</power>
<colorCode>_5_BAND</colorCode>
<shape>Standard</shape>
</diylc.passive.Resistor>
<diylc.connectivity.HookupWire>
<name>W8</name>
<alpha>127</alpha>
<controlPoints2>
<point x="1.8" y="5.1"/>
<point x="1.8" y="5.2"/>
<point x="2.0" y="5.2"/>
<point x="2.0" y="5.1"/>
</controlPoints2>
<color hex="cccccc"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_26</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.4" y="4.2"/>
<point x="2.6" y="4.0"/>
<point x="2.9" y="4.1"/>
</controlPoints2>
<color hex="666666"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.connectivity.HookupWire>
<name>W5</name>
<alpha>127</alpha>
<controlPoints2>
<point x="2.2" y="4.1"/>
<point x="2.3" y="4.2"/>
<point x="2.3" y="4.4"/>
<point x="2.2" y="4.5"/>
</controlPoints2>
<color hex="00ff00"/>
<pointCount>FOUR</pointCount>
<style>SOLID</style>
<smooth>true</smooth>
<lastUpdatePointIndex>-1</lastUpdatePointIndex>
<gauge>_22</gauge>
<striped>false</striped>
<stripeColor hex="ffff00"/>
</diylc.connectivity.HookupWire>
<diylc.misc.Label>
<name>L1</name>
<point x="3.3" y="4.1"/>
<text>To bridge baseplate</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L2</name>
<point x="1.5" y="4.8"/>
<text>Treble bleed</text>
<font reference="../../diylc.misc.Label/font"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L3</name>
<point x="2.0" y="0.2"/>
<text>Travelcaster Wiring Diagram</text>
<font name="Dialog" size="36" style="0"/>
<color hex="0000cc"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L3</name>
<point x="2.0" y="0.3"/>
<text>www.diy-fever.com</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="1.9" y="1.1"/>
<text>Pickup selector switch</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="2.0" y="3.2"/>
<text>Split mode</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L4</name>
<point x="2.0" y="3.3"/>
<text>Push-pull</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L5</name>
<point x="0.4" y="3.1"/>
<text>DiMarzio</text>
<font name="Dialog" size="18" style="1"/>
<color hex="ffffff"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>_270</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L5</name>
<point x="3.4" y="1.4"/>
<text>DiMarzio</text>
<font name="Dialog" size="18" style="1"/>
<color hex="ffffff"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>_90</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L6</name>
<point x="0.1" y="5.4"/>
<text>* One of the pickups is rotated 180 degrees for cleaner drawing</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>LEFT</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
<diylc.misc.Label>
<name>L1</name>
<point x="3.6" y="4.5"/>
<text>Output jack</text>
<font name="Tahoma" size="14" style="0"/>
<color hex="000000"/>
<horizontalAlignment>CENTER</horizontalAlignment>
<verticalAlignment>CENTER</verticalAlignment>
<orientation>DEFAULT</orientation>
</diylc.misc.Label>
</components>
<groups/>
<lockedLayers/>
<lockedComponents/>
<hiddenLayers/>
<font name="Tahoma" size="14" style="0"/>
</project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="DIYLCStarter" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="org.diylc.DIYLCStarter" />
<module name="diylc-swing" />
<option name="VM_PARAMETERS" value="-Xms1024m -Xmx4096m -Dsun.java2d.uiScale=1.5 -Dorg.diylc.scriptRun=true -Dfile.encoding=UTF-8 --add-exports java.desktop/com.apple.eawt.event=ALL-UNNAMED --add-exports java.desktop/com.apple.eio=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.desktop/java.awt.geom=ALL-UNNAMED" />
<option name="VM_PARAMETERS" value="-Xms1024m -Xmx4096m -Ddorg.diylc.debugAreaPerformance=true -Dorg.diylc.scriptRun=true -Dfile.encoding=UTF-8 --add-exports java.desktop/com.apple.eawt.event=ALL-UNNAMED --add-exports java.desktop/com.apple.eio=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.desktop/java.awt.geom=ALL-UNNAMED" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.diylc.*" />
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>org.diylc</groupId>
<artifactId>diylc-parent</artifactId>
<version>5.2.0</version>
<version>5.4.0</version>
</parent>
<artifactId>diylc-core</artifactId>
@@ -45,6 +45,10 @@ public enum EventType {
* has been either reloaded or loaded with undo/redo operations.
*/
PROJECT_LOADED,
/**
* Called when the current project is saved to a file. The first parameter is the current file name.
*/
PROJECT_SAVED,
/**
* Called when either current file name or modified flag are changed. The first parameter is the
* current file name, the second one is a boolean modified flag.
@@ -3,6 +3,7 @@ package org.diylc.common;
import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.plugins.chatbot.service.ChatbotService;
import org.diylc.plugins.cloud.service.CloudService;
import org.diylc.presenter.DrawingManager;
public interface IServiceManager {
@@ -11,4 +12,6 @@ public interface IServiceManager {
ChatbotService getChatbotService();
MessageDispatcher<EventType> getMessageDispatcher();
DrawingManager getDrawingManager();
}
@@ -189,7 +189,7 @@ public abstract class AbstractComponent<T> implements IDIYComponent<T> {
// Deep copy point arrays.
// TODO: something nicer
if (value != null && value.getClass().isArray()
&& value.getClass().getComponentType().isAssignableFrom(Point2D.class)) {
&& Point2D.class.isAssignableFrom(value.getClass().getComponentType())) {
Object newArray = Array.newInstance(value.getClass().getComponentType(), Array.getLength(value));
for (int i = 0; i < Array.getLength(value); i++) {
Point2D p = (Point2D) Array.get(value, i);
@@ -89,8 +89,11 @@ public class LangUtil {
LOG.info("Loading translation for " + language);
dict = new HashMap<String, String>();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
(LangUtil.class.getResourceAsStream("/" + LANG_DIR + File.separator + language + ".txt"))));
InputStream inputStream = LangUtil.class.getResourceAsStream('/' + LANG_DIR + '/' + language + ".txt");
if (inputStream == null) {
throw new RuntimeException("Could not load language file: " + language);
}
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while(reader.ready()) {
String s = reader.readLine();
if (s.contains("|")) {
@@ -34,10 +34,10 @@ public abstract class AbstractNetlistAnalyzer {
Map<String, Summary> summaries = new HashMap<String, Summary>();
for (Netlist n : netlists) {
Summary s = summarize(n);
if (summaries.containsKey(s.getSummary()))
summaries.get(s.getSummary()).append(n);
if (summaries.containsKey(s.getSummaryHtml()))
summaries.get(s.getSummaryHtml()).append(n);
else
summaries.put(s.getSummary(), s);
summaries.put(s.getSummaryHtml(), s);
}
List<Summary> res = new ArrayList<Summary>(summaries.values());
@@ -92,7 +92,7 @@ public abstract class AbstractNetlistAnalyzer {
for (int i = 0; i < c.getControlPointCount(); i++) {
if (i != n.getPointIndex() && c.getInternalLinkName(i, n.getPointIndex()) != null) {
TreeLeaf l = new TreeLeaf(c, i, n.getPointIndex());
Node newNodeA = new Node(c, i, n.getZIndex());
Node newNodeA = new Node(c, i);
if (!visited.contains(newNodeA)) {
// visited.add(n);
Tree newCurrentPath = null;
@@ -53,11 +53,7 @@ public class Group implements Comparable<Group> {
}
public Group connect(IDIYComponent<?> component, int index) {
return connect(component, index, 0);
}
public Group connect(IDIYComponent<?> component, int index, int zOrder) {
getNodes().add(new Node(component, index, zOrder));
getNodes().add(new Node(component, index));
return this;
}
@@ -0,0 +1,8 @@
package org.diylc.netlist;
import java.util.List;
public interface IGuitarDiagramAnalyzer {
List<String> collectNotes(Netlist netlist);
}
@@ -37,10 +37,10 @@ public abstract class NetlistAnalyzer {
Map<String, Summary> summaries = new HashMap<String, Summary>();
for (Netlist n : netlists) {
Summary s = summarize(n, preferredOutput);
if (summaries.containsKey(s.getSummary()))
summaries.get(s.getSummary()).append(n);
if (summaries.containsKey(s.getSummaryHtml()))
summaries.get(s.getSummaryHtml()).append(n);
else
summaries.put(s.getSummary(), s);
summaries.put(s.getSummaryHtml(), s);
}
List<Summary> res = new ArrayList<Summary>(summaries.values());
@@ -92,7 +92,7 @@ public abstract class NetlistAnalyzer {
continue;
if (c.getInternalLinkName(targetPoint, sourcePoint) != null) {
Node newNodeA = new Node(c, targetPoint, n.getZIndex());
Node newNodeA = new Node(c, targetPoint);
if (pathVisited.contains(newNodeA))
continue;
@@ -46,7 +46,7 @@ public class NetlistBuilder {
public static final float eps = 4; // consider any nodes closer than this as connected
public static int MAX_SWITCH_COMBINATIONS = 64;
public static int MAX_SWITCH_COMBINATIONS = 256;
private static final String MAX_SWITCH_COMBINATIONS_ERROR = LangUtil
.translate("Maximum number of switching combinations exceeded. Allowed: %s, actual: %s");
@@ -75,7 +75,7 @@ public class NetlistBuilder {
String nodeName = c.getControlPointNodeName(i);
if (c.isControlPointSticky(i) && nodeName != null
&& (!includeSwitches || !ISwitch.class.isAssignableFrom(type.getInstanceClass()))) {
nodes.add(new Node(c, i, j));
nodes.add(new Node(c, i));
}
}
}
@@ -86,7 +86,7 @@ public class NetlistBuilder {
String label = ((ICommonNode) c).getCommonNodeLabel();
if (!commonNodes.contains(label)) {
commonNodes.add(label);
nodes.add(new Node(c, 0, j));
nodes.add(new Node(c, 0));
}
}
@@ -0,0 +1,60 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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.netlist;
import java.util.List;
public class NetlistHtmlReport {
public static String generateHtml(List<Netlist> netlists, String fontName) {
if (netlists == null || netlists.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder("<html><head><style>");
sb.append("body { font-family: ").append(fontName)
.append(", sans-serif; color: black; margin: 10px; }");
sb.append("h2 { font-size: 12px; margin-top: 10px; margin-bottom: 0px; }");
sb.append("p { margin: 0; }");
sb.append("hr { margin-top: 10px; margin-bottom: 0; }");
sb.append(".netlist { font-family: 'Courier New', monospace; font-size: 11px; }");
sb.append("</style></head><body>");
for (Netlist netlist : netlists) {
if (netlists.size() > 1) {
sb.append("<h2>Switch configuration: ").append(netlist.getSwitchSetup())
.append("</h2>");
}
sb.append("<div class=\"netlist\">");
sb.append("Connected node groups:<br><br>");
for (Group group : netlist.getSortedGroups()) {
sb.append("&nbsp;&nbsp;").append(group.getSortedNodes()).append("<br>");
}
sb.append("</div>");
if (netlists.size() > 1) {
sb.append("<br><hr>");
}
}
sb.append("</body></html>");
return sb.toString();
}
}
@@ -0,0 +1,54 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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.netlist;
import java.util.List;
public class NetlistSummaryHtmlReport {
public static String generateHtml(List<Summary> summaries, String fontName) {
if (summaries == null || summaries.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder("<html><head><style>");
sb.append("body { font-family: ").append(fontName)
.append(", sans-serif; color: black; margin: 10px; }");
sb.append("h2 { font-size: 12px; margin-top: 10px; margin-bottom: 0px; }");
sb.append("p { margin: 0; }");
sb.append("hr { margin-top: 10px; margin-bottom: 0; }");
sb.append("</style></head><body>");
for (Summary summary : summaries) {
if (summaries.size() > 1) {
sb.append("<h2>Switch configuration: ").append(summary.getNetlist().getSwitchSetup())
.append("</h2>");
}
sb.append(summary.getSummaryHtml());
if (summaries.size() > 1) {
sb.append("<br><hr>");
}
}
sb.append("</body></html>");
return sb.toString();
}
}
@@ -22,6 +22,7 @@
package org.diylc.netlist;
import java.awt.geom.Point2D;
import java.util.Objects;
import org.diylc.core.ICommonNode;
import org.diylc.core.IDIYComponent;
@@ -35,13 +36,11 @@ public class Node implements Comparable<Node> {
private IDIYComponent<?> component;
private int pointIndex;
private int zIndex;
public Node(IDIYComponent<?> component, int pointIndex, int zIndex) {
public Node(IDIYComponent<?> component, int pointIndex) {
super();
this.component = component;
this.pointIndex = pointIndex;
this.zIndex = zIndex;
}
public IDIYComponent<?> getComponent() {
@@ -52,10 +51,6 @@ public class Node implements Comparable<Node> {
return pointIndex;
}
public int getZIndex() {
return zIndex;
}
public String getDisplayName() {
String[] section = component.getSectionNames(pointIndex);
return (section == null || section.length > 1 ? "" : section[0] + ".") + component.getControlPointNodeName(pointIndex);
@@ -66,31 +61,16 @@ public class Node implements Comparable<Node> {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((component == null) ? 0 : component.hashCode());
result = prime * result + pointIndex;
return result;
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Node)) return false;
Node other = (Node) obj;
return Objects.equals(component, other.component) && pointIndex == other.pointIndex;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (component == null) {
if (other.component != null)
return false;
} else if (!component.equals(other.component))
return false;
if (pointIndex != other.pointIndex)
return false;
return true;
public int hashCode() {
return Objects.hash(component, pointIndex);
}
@Override
@@ -59,7 +59,7 @@ public class SpiceSumarizer {
if (c instanceof ISpiceMapper)
pointIndex = ((ISpiceMapper)c).mapToSpiceNode(pointIndex);
int nodeIndex = find(new Node(c, pointIndex, 0), groups);
int nodeIndex = find(new Node(c, pointIndex), groups);
if (nodeIndex < 0)
nodeIndex = unconnectedIndex++;
@@ -24,12 +24,12 @@ package org.diylc.netlist;
public class Summary implements Comparable<Summary> {
private Netlist netlist;
private String summary;
private String summaryHtml;
public Summary(Netlist netlist, String summary) {
public Summary(Netlist netlist, String summaryHtml) {
super();
this.netlist = netlist;
this.summary = summary;
this.summaryHtml = summaryHtml;
}
public Netlist getNetlist() {
@@ -40,8 +40,8 @@ public class Summary implements Comparable<Summary> {
this.netlist.getSwitchSetup().addAll(netlist.getSwitchSetup());
}
public String getSummary() {
return summary;
public String getSummaryHtml() {
return summaryHtml;
}
@Override
@@ -49,7 +49,7 @@ public class Summary implements Comparable<Summary> {
final int prime = 31;
int result = 1;
result = prime * result + ((netlist == null) ? 0 : netlist.hashCode());
result = prime * result + ((summary == null) ? 0 : summary.hashCode());
result = prime * result + ((summaryHtml == null) ? 0 : summaryHtml.hashCode());
return result;
}
@@ -67,17 +67,17 @@ public class Summary implements Comparable<Summary> {
return false;
} else if (!netlist.equals(other.netlist))
return false;
if (summary == null) {
if (other.summary != null)
if (summaryHtml == null) {
if (other.summaryHtml != null)
return false;
} else if (!summary.equals(other.summary))
} else if (!summaryHtml.equals(other.summaryHtml))
return false;
return true;
}
@Override
public String toString() {
return summary.toString();
return summaryHtml.toString();
}
@Override
@@ -0,0 +1,6 @@
package org.diylc.plugins.chatbot.model;
import java.util.List;
public record AiComponent(String id, String type, String value, AiPoint fromPos, AiPoint toPos, AiPoint pos, List<AiTerminal> terminals) {
}
@@ -0,0 +1,4 @@
package org.diylc.plugins.chatbot.model;
public record AiPoint(int x, int y) {
}
@@ -0,0 +1,12 @@
package org.diylc.plugins.chatbot.model;
import java.util.List;
import java.util.Map;
import java.util.Set;
public record AiProject(Map<String, String> metadata,
List<String> tags,
List<AiComponent> components,
Map<String, Set<String>> nets,
List<AiSwitch> switches) {
}
@@ -0,0 +1,6 @@
package org.diylc.plugins.chatbot.model;
import java.util.List;
public record AiSwitch(String componentId, List<AiSwitchPosition> positions) {
}
@@ -0,0 +1,7 @@
package org.diylc.plugins.chatbot.model;
import java.util.List;
import java.util.Set;
public record AiSwitchPosition(String name, List<Set<Integer>> connectedTerminalGroups) {
}
@@ -0,0 +1,4 @@
package org.diylc.plugins.chatbot.model;
public record AiTerminal(int id, String label, AiPoint pos) {
}
@@ -43,6 +43,20 @@ public interface IChatbotAPI {
@ParamName("machineId") String machineId, @ParamName("project") String project,
@ParamName("netlist") File netlist, @ParamName("prompt") String prompt);
/**
* Analyzes the provided circuit
*
* @param username
* @param token
* @param machineId
* @param project
* @param netlist
* @return
*/
String analyzeCircuit(@ParamName("username") String username, @ParamName("token") String token,
@ParamName("machineId") String machineId, @ParamName("project") String project,
@ParamName("netlist") File netlist);
SubscriptionEntity getSubscriptionInfo(@ParamName("username") String username, @ParamName("token") String token,
@ParamName("machineId") String machineId);
@@ -51,4 +65,8 @@ public interface IChatbotAPI {
List<ChatMessageEntity> getChatHistory(@ParamName("username") String username, @ParamName("token") String token,
@ParamName("machineId") String machineId, @ParamName("project") String project);
String updateChatProject(@ParamName("username") String username, @ParamName("token") String token,
@ParamName("machineId") String machineId, @ParamName("project") String project,
@ParamName("newProject") String newProject);
}
@@ -0,0 +1,186 @@
package org.diylc.plugins.chatbot.service;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.log4j.Logger;
import org.diylc.common.ComponentType;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ISwitch;
import org.diylc.core.Project;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistBuilder;
import org.diylc.netlist.NetlistException;
import org.diylc.netlist.Node;
import org.diylc.plugins.chatbot.model.*;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.ContinuityArea;
public class AiProjectBuilder {
public static AiProject build(Project project, List<ContinuityArea> continuityAreas) {
Map<String, String> metadata = getMetadata(project);
List<String> tags = getTags(project);
List<AiComponent> components = project.getComponents().stream().map(AiProjectBuilder::mapComponent).toList();
// List<Set<Node>> sets =
List<Netlist> netlists = null;
try {
netlists = NetlistBuilder.extractNetlists(false, project, continuityAreas);
} catch (NetlistException e) {
throw new RuntimeException(e);
}
Map<String, Set<String>> nets;
if (netlists == null || netlists.isEmpty()) {
nets = new HashMap<>();
} else {
AtomicInteger counter = new AtomicInteger();
nets = netlists.get(0).getGroups().stream().map(s -> s.getNodes().stream()
.map(AiProjectBuilder::mapTerminal)
.collect(Collectors.toSet()))
.collect(Collectors.toMap(x -> "N" + String.format("%03d", counter.incrementAndGet()),
Function.identity()));
}
// Map<String, Set<String>> nets =
// sets.stream().map(s -> s.stream()
// .map(AiProjectBuilder::mapTerminal)
// .collect(Collectors.toSet()))
// .collect(Collectors.toMap(x -> "N" + String.format("%03d", counter.incrementAndGet()),
// Function.identity()));
List<AiSwitch> switches =
project.getComponents().stream()
.filter(x -> x instanceof ISwitch)
.map(x -> (ISwitch) x)
.map(AiProjectBuilder::mapSwitch).toList();
return new AiProject(metadata, tags, components, nets, switches);
}
private static Map<String, String> getMetadata(Project project) {
Map<String, String> metadata = new HashMap<>();
if (project.getTitle() != null) metadata.put("title", project.getTitle());
if (project.getAuthor() != null) metadata.put("author", project.getAuthor());
if (project.getDescription() != null) metadata.put("description", project.getDescription());
metadata.put("gridSpacing", project.getGridSpacing().toString());
metadata.put("width", project.getWidth().toString());
metadata.put("height", project.getHeight().toString());
return metadata;
}
private static List<String> getTags(Project project) {
List<String> tags = new ArrayList<>();
if (project.getComponents().stream().anyMatch(x -> x.getClass().getCanonicalName().toLowerCase().contains("guitar"))) {
tags.add("guitar");
}
if (project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("symbol"))) {
tags.add("schematic");
}
if (project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("board")) &&
project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("trace")) &&
project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("pad"))) {
tags.add("PCB");
}
if (project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("tube"))) {
tags.add("tube");
}
if (project.getComponents().stream().anyMatch(x -> x.getClass().getName().toLowerCase().contains("vero"))) {
tags.add("vero/strip");
}
return tags;
}
private static final Set<Class<?>> PROPERTY_TYPES_TO_SKIP = Set.of(Font.class, Color.class);
private static final Set<String> PROPERTY_NAMES_TO_SKIP = Set.of("alpha");
static AiSwitch mapSwitch(ISwitch sw) {
List<AiSwitchPosition> positions = new ArrayList<>();
IDIYComponent<?> c = (IDIYComponent<?>) sw;
for (int p = 0; p < sw.getPositionCount(); p++) {
List<Set<Integer>> connections = new ArrayList<>();
for (int i = 0; i < c.getControlPointCount() - 1; i++) {
for (int j = i + 1; j < c.getControlPointCount(); j++) {
if (sw.arePointsConnected(i, j, p)) {
connections.add(Set.of(i, j));
}
}
}
positions.add(new AiSwitchPosition(sw.getPositionName(p), connections));
}
return new AiSwitch(c.getName(), positions);
}
static AiComponent mapComponent(IDIYComponent<?> component) {
ComponentType componentType = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
List<PropertyWrapper> properties =
ComponentProcessor.getInstance().extractProperties(component.getClass());
AiPoint pos = null;
AiPoint fromPos = null;
AiPoint toPos = null;
// Map<String, Object> componentDescriptorMap = new HashMap<>();
// if (!component.isControlPointSticky(0)) {
// Point2D controlPoint1 = component.getControlPoint(0);
// if (component.getControlPointCount() > 1 && !component.isControlPointSticky(component.getControlPointCount() - 1)) {
// Point2D controlPoint2 = component.getControlPoint(component.getControlPointCount() - 1);
// fromPos = new AiPoint((int)Math.round(controlPoint1.getX()), (int)Math.round(controlPoint1.getY()));
// toPos = new AiPoint((int)Math.round(controlPoint2.getX()), (int)Math.round(controlPoint2.getY()));
// } else {
// pos = new AiPoint((int)Math.round(controlPoint1.getX()), (int)Math.round(controlPoint1.getY()));
// }
// }
//
// if (component.getValue() != null && !PROPERTY_TYPES_TO_SKIP.contains(component.getValue().getClass())) {
// componentDescriptorMap.put("value", component.getValue().toString());
// }
// properties.forEach(p -> {
// if (PROPERTY_TYPES_TO_SKIP.contains(p.getType()))
// return;
// if (PROPERTY_NAMES_TO_SKIP.contains(p.getName().toLowerCase()))
// return;
//
// try {
// p.readFrom(component);
// if (p.getValue() == null)
// return;
//
// componentDescriptorMap.put(p.getName(), p.getValue().toString());
// } catch (Exception e) {
// LOG.warn("Error extracting properties", e);
// }
// });
List<AiTerminal> terminals = new ArrayList<>();
for (int i = 0; i < component.getControlPointCount(); i++) {
if (component.isControlPointSticky(i)) {
Point2D controlPoint = component.getControlPoint(i);
String nodeName = component.getControlPointNodeName(i);
AiTerminal terminal = new AiTerminal(i, Integer.toString(i+1).equals(nodeName) ? null : nodeName,
new AiPoint((int)Math.round(controlPoint.getX()),
(int)Math.round(controlPoint.getY())));
terminals.add(terminal);
}
}
return new AiComponent(component.getName(), componentType.getName(),
component.getValue() == null ? null : component.getValue().toString(),
fromPos, toPos, pos, terminals.isEmpty() ? null : terminals);
}
static String mapTerminal(Node terminalRef) {
return terminalRef.getComponent().getName() + "." + terminalRef.getPointIndex();
}
}
@@ -23,22 +23,26 @@ package org.diylc.plugins.chatbot.service;
import com.diyfever.httpproxy.PhpFlatProxy;
import com.diyfever.httpproxy.ProxyFactory;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.simplemq.IMessageListener;
import org.diylc.common.ComponentType;
import org.diylc.common.EventType;
import org.diylc.common.IPlugInPort;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.IDIYComponent;
import org.diylc.core.ISwitch;
import org.diylc.netlist.*;
import org.diylc.plugins.chatbot.model.ChatMessageEntity;
import org.diylc.plugins.chatbot.model.IChatbotAPI;
import org.diylc.plugins.chatbot.model.SubscriptionEntity;
import org.diylc.plugins.chatbot.model.*;
import org.diylc.plugins.cloud.model.IServiceAPI;
import org.diylc.plugins.cloud.service.NotLoggedInException;
import org.diylc.presenter.ComponentProcessor;
import org.diylc.presenter.ContinuityArea;
import org.diylc.utils.FileUtils;
import org.diylc.utils.ReflectionUtils;
import java.awt.*;
import java.io.File;
@@ -46,7 +50,6 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import static org.diylc.utils.FileUtils.extractFileName;
@@ -57,19 +60,48 @@ import static org.diylc.utils.FileUtils.extractFileName;
*/
public class ChatbotService {
public static final String LOG_IN_MESSAGE =
"Please log into your DIYLC Cloud account in order to use the AI Assistant";
private final static Logger LOG = Logger.getLogger(ChatbotService.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
public static final String WELCOME_MESSAGE = "Welcome to the DIYLC AI Assistant!";
private final IPlugInPort plugInPort;
private IChatbotAPI service;
private String currentProjectName;
public ChatbotService(IPlugInPort plugInPort) {
this.plugInPort = plugInPort;
this.plugInPort.getMessageDispatcher().registerListener(new IMessageListener<EventType>() {
@Override
public EnumSet<EventType> getSubscribedEventTypes() {
return EnumSet.of(EventType.PROJECT_LOADED, EventType.PROJECT_SAVED);
}
@Override
public void processMessage(EventType eventType, Object... objects) {
if (eventType == EventType.PROJECT_LOADED) {
ChatbotService.this.currentProjectName = extractFileName((String)objects[2]);
} else if (eventType == EventType.PROJECT_SAVED) {
String newProjectName = extractFileName((String)objects[0]);
if (!Objects.equals(ChatbotService.this.currentProjectName, newProjectName)) {
if (plugInPort.getCloudService().isLoggedIn()) {
LOG.info("Migrating project chat history from " + ChatbotService.this.currentProjectName + " to " + newProjectName);
getService().updateChatProject(plugInPort.getCloudService().getCurrentUsername(),
plugInPort.getCloudService().getCurrentToken(),
plugInPort.getCloudService().getMachineId(),
ChatbotService.this.currentProjectName, newProjectName);
}
ChatbotService.this.currentProjectName = newProjectName;
}
}
}
});
}
private IChatbotAPI getService() {
@@ -86,25 +118,12 @@ public class ChatbotService {
if (!plugInPort.getCloudService().isLoggedIn())
throw new NotLoggedInException();
LOG.info("Prompting chatbot: " + prompt);
String currentFile = plugInPort.getCurrentFileName();
String fileName = extractFileName(currentFile);
String netlist;
File netlistFile = null;
try {
netlist = extractNetlists();
if (netlist != null) {
netlistFile = File.createTempFile("diylc_netlist_", ".txt");
try (FileWriter writer = new FileWriter(netlistFile)) {
writer.write(netlist);
}
// Make sure the temp file is deleted when the JVM exits
netlistFile.deleteOnExit();
}
} catch (NetlistException | IOException e) {
LOG.error("Error extracting or saving netlist", e);
netlist = null;
}
File aiProjectFile = getAiProjectFile();
try {
return getService().promptChatbot(
@@ -112,119 +131,115 @@ public class ChatbotService {
plugInPort.getCloudService().getCurrentToken(),
plugInPort.getCloudService().getMachineId(),
fileName,
netlistFile,
aiProjectFile,
prompt);
} finally {
// Clean up the temp file immediately after use
if (netlistFile != null && netlistFile.exists()) {
netlistFile.delete();
if (aiProjectFile != null && aiProjectFile.exists()) {
aiProjectFile.delete();
}
}
}
private static final Set<Class<?>> PROPERTY_TYPES_TO_SKIP = Set.of(Font.class, Color.class);
public String analyzeCircuit() throws NotLoggedInException {
if (!plugInPort.getCloudService().isLoggedIn())
throw new NotLoggedInException();
private String extractNetlists() throws NetlistException {
List<Netlist> netlists = plugInPort.extractNetlists(true);
LOG.info("Analyzing circuit...");
if (netlists == null || netlists.isEmpty()) {
return null;
}
String currentFile = plugInPort.getCurrentFileName();
String fileName = extractFileName(currentFile);
StringBuilder sb = new StringBuilder();
// Set<? extends IDIYComponent<?>> components = netlists.stream()
// .flatMap(n -> n.getGroups().stream()
// .flatMap(g -> g.getNodes().stream().
// map(Node::getComponent)))
// .collect(Collectors.toSet());
//
// if (!components.isEmpty()) {
// sb.append(
// "Components (each element of the JSON array represents one component with all the relevant properties): ");
// outputComponents(components, sb);
// }
Set<? extends ISwitch> switches = plugInPort.getCurrentProject().getComponents()
.stream().filter(x -> x instanceof ISwitch)
.map(x -> (ISwitch)x)
.collect(Collectors.toSet());;
if (!switches.isEmpty()) {
sb.append(
"Switches (each row represents one switch, starts with the switch name and lists key properties of a switch):\n\n");
outputSwitches(switches, sb);
}
for (Netlist netlist : netlists) {
sb.append("\n");
if (!netlist.getSwitchSetup().isEmpty()) {
sb.append("Switch configuration: ").append(netlist.getSwitchSetup()).append(". ");
}
sb.append("Circuit netlist")
.append(netlist.getSwitchSetup().isEmpty() ? "" : " in this switch configuration")
.append(":\n\n");
try {
sb.append(SpiceSumarizer.summarize(netlist, false));
} catch (TreeException e) {
LOG.error("Error exporting netlist to Spice", e);
}
}
return sb.toString();
}
private static void outputSwitches(Set<? extends ISwitch> switches, StringBuilder sb) {
for (ISwitch sw : switches) {
IDIYComponent<?> component = (IDIYComponent<?>) sw;
ComponentType componentType = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) component.getClass());
sb.append(component.getName())
.append(" ").append(component.getValueForDisplay()).append(" ")
.append(componentType.getName())
.append(" [").append(componentType.getCategory()).append("]\n");
}
}
private static void outputComponents(Set<? extends IDIYComponent<?>> components, StringBuilder sb) {
List<Map<String, Object>> componentDescriptors = new ArrayList<>();
components.forEach(c -> {
ComponentType componentType = ComponentProcessor.getInstance()
.extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass());
List<PropertyWrapper> properties =
ComponentProcessor.getInstance().extractProperties(c.getClass());
Map<String, Object> componentDescriptorMap = new HashMap<>();
componentDescriptorMap.put("name", c.getName());
componentDescriptorMap.put("type", componentType.getName());
componentDescriptorMap.put("category", componentType.getCategory());
properties.forEach(p -> {
if (PROPERTY_TYPES_TO_SKIP.contains(p.getType()))
return;
try {
p.readFrom(c);
if (p.getValue() == null)
return;
componentDescriptorMap.put(p.getName(), p.getValue().toString());
} catch (Exception e) {
LOG.warn("Error extracting properties", e);
}
});
componentDescriptors.add(componentDescriptorMap);
});
File aiProjectFile = getAiProjectFile();
try {
String json = MAPPER.writeValueAsString(componentDescriptors);
sb.append(json).append("\n\n");
} catch (Exception e) {
LOG.error("Failed to serialize component descriptors to JSON", e);
String jsonResult = getService().analyzeCircuit(plugInPort.getCloudService().getCurrentUsername(),
plugInPort.getCloudService().getCurrentToken(),
plugInPort.getCloudService().getMachineId(), fileName, aiProjectFile);
return JsonToHtmlConverter.convertToHtml(jsonResult);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// Clean up the temp file immediately after use
if (aiProjectFile != null && aiProjectFile.exists()) {
aiProjectFile.delete();
}
}
}
private File getAiProjectFile() {
File aiProjectFile = null;
AiProject aiProject = extractAiProject();
if (aiProject == null) {
return null;
}
try {
String aiProjectStr = null;
try {
aiProjectStr = MAPPER.writeValueAsString(aiProject);
} catch (JsonProcessingException e) {
LOG.error("Could not serialize aiProject to JSON", e);
}
if (aiProjectStr != null) {
aiProjectFile = File.createTempFile("diylc_netlist_", ".txt");
try (FileWriter writer = new FileWriter(aiProjectFile)) {
writer.write("""
Take these notes into account when analyzing the circuit
- Coordinates are represented in pixels assuming 200px/in resolution.
- Each component will either have a set of terminals that are connectable to other components or one or two points defining its position.
- Netlists of connected terminals are defined in the 'nets' structure.
- Analyze labels - they could provide clues about nearby terminals and what they represent.
""");
if (aiProject.switches() != null && !aiProject.switches().isEmpty()) {
writer.write("""
- Switches are defined in the 'switches' structure. Each switch will have a list of available position and the collection of internal terminals that are connected in each of the positions. Take the switching matrix into consideration when analyzing the circuit operation.
""");
}
if (aiProject.tags().contains("guitar")) {
IGuitarDiagramAnalyzer guitarDiagramAnalyzer =
ReflectionUtils.getGuitarDiagramAnalyzer();
if (guitarDiagramAnalyzer != null) {
try {
List<Netlist> netlists = plugInPort.extractNetlists(true);
writer.write("""
- The circuit represents a guitar wiring diagram and below are some of the known characteristics in each combination of switch positions:
""");
netlists.forEach(netlist -> {
List<String> notes = guitarDiagramAnalyzer.collectNotes(netlist);
try {
writer.write(netlist.getSwitchSetupString() + ": \n");
for (String note : notes) {
writer.write(" - " + note + "\n");
}
} catch (IOException ex) {
LOG.warn("Could not write guitar diagram notes", ex);
}
});
} catch (NetlistException | IOException ex) {
LOG.warn("Could not extract guitar diagram netlist", ex);
}
}
}
writer.write("\nBelow is the JSON describing the circuit in detail.\n");
writer.write(aiProjectStr);
}
// Make sure the temp file is deleted when the JVM exits
aiProjectFile.deleteOnExit();
}
} catch (Exception e) {
LOG.error("Error extracting or saving netlist", e);
}
return aiProjectFile;
}
private AiProject extractAiProject() {
if (plugInPort.getCurrentProject().getComponents().isEmpty()) {
return null;
}
List<ContinuityArea> continuityAreas = plugInPort.getDrawingManager().getContinuityAreas();
return AiProjectBuilder.build(plugInPort.getCurrentProject(), continuityAreas);
}
public SubscriptionEntity getSubscriptionInfo() throws NotLoggedInException {
if (!plugInPort.getCloudService().isLoggedIn())
throw new NotLoggedInException();
@@ -286,23 +301,23 @@ public class ChatbotService {
public static final String[] diylcQuestions = {
"How do I add a component to my layout in DIYLC?",
"How can I quickly find a specific component in DIYLC?s component toolbox?",
"How can I quickly find a specific component in DIYLC's component toolbox?",
"How do I move or reposition components on the canvas in DIYLC?",
"How do I edit or change a component?s properties (e.g. value or color) in DIYLC?",
"How do I edit or change a component's properties (e.g. value or color) in DIYLC?",
"Is there a shortcut to duplicate or repeat the last component I placed in DIYLC?",
"How can I group multiple components so I can move or edit them together in DIYLC?",
"What are ?building blocks? in DIYLC and how do I create or use them?",
"What are 'building blocks' in DIYLC and how do I create or use them?",
"How do I save my DIYLC project and reopen it later on?",
"How do I export my DIYLC layout as an image or PDF file?",
"Can DIYLC generate a bill of materials (BOM) for my project, and how would I do that?",
"How can I change or hide different layers in a DIYLC project to manage overlapping components?",
"Is it possible to view the underside of a board in DIYLC, and how can I enable that?",
"What does the ?Highlight Connected Areas? feature do in DIYLC and how do I use it for debugging?",
"How can I analyze a guitar wiring diagram using DIYLC?s built-in tools?",
"What does the 'Highlight Connected Areas' feature do in DIYLC and how do I use it for debugging?",
"How can I analyze a guitar wiring diagram using DIYLC's built-in tools?",
"How do I assign a custom keyboard shortcut to a component in DIYLC for quick access?",
"What are component variants in DIYLC and how can I create and apply them?",
"What is the continuous creation mode in DIYLC and how does it help when adding many components?",
"How can I create a custom component to add to DIYLC?s component library?",
"How can I create a custom component to add to DIYLC's component library?",
"Does DIYLC support plug-ins, and how can I add new functionality to the app with them?",
"What is the DIYLC cloud feature and how can I share or download projects using it?"
};
@@ -0,0 +1,92 @@
package org.diylc.plugins.chatbot.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class JsonToHtmlConverter {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* Convert the given JSON string into HTML.
*
* @param json the input JSON
* @return an HTML document as a String
* @throws IOException if JSON parsing fails
*/
public static String convertToHtml(String json) throws IOException {
JsonNode root = MAPPER.readTree(json);
StringBuilder html = new StringBuilder();
html.append("<!DOCTYPE html>\n<html>\n<head>\n")
.append(" <meta charset=\"UTF-8\">\n")
.append(" <title>Validation Report</title>\n")
.append(" <style>\n")
.append(" body { font-family: Arial, sans-serif; }\n")
.append(" h2 { color: #444; }\n")
.append(" ul { list-style: disc; margin-left: 20px; }\n")
.append(" li { margin-bottom: 5px; }\n")
.append(" </style>\n")
.append("</head>\n<body>\n");
// Process each section
processSection(root, "critical", html, "#c00");
processSection(root, "warnings", html, "#e65c00");
processSection(root, "optimizations", html, "#0066c0");
html.append("</body>\n</html>");
return html.toString();
}
private static void processSection(JsonNode root, String field, StringBuilder html, String color) {
JsonNode array = root.path(field);
if (array.isArray() && array.size() > 0) {
html.append(String.format("<h2 style=\"color:%s;\">%s</h2>\n", color, capitalize(field)));
html.append("<ul>\n");
for (JsonNode item : array) {
String message = escapeHtml(item.path("message").asText(""));
if (item.hasNonNull("componentId") && !item.path("componentId").asText().isEmpty()) {
String id = escapeHtml(item.path("componentId").asText());
html.append(" <li><strong>")
.append(id)
.append("</strong>: ")
.append(message)
.append("</li>\n");
} else {
html.append(" <li>")
.append(message)
.append("</li>\n");
}
}
html.append("</ul>\n");
}
}
private static String capitalize(String text) {
if (text == null || text.isEmpty()) return text;
return Character.toUpperCase(text.charAt(0)) + text.substring(1);
}
/**
* Very basic HTML escapement; for more robust use Apache Commons StringEscapeUtils.escapeHtml4().
*/
private static String escapeHtml(String s) {
return s.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;");
}
// Example usage:
public static void main(String[] args) throws IOException {
String json = "{\n" +
" \"critical\": [{\"message\":\"Value too low\"}],\n" +
" \"warnings\": [{\"componentId\":\"C3\",\"message\":\"Consider higher tolerance\"}],\n" +
" \"optimizations\": [{\"componentId\":\"L2\",\"message\":\"Could use 0805 package\"}]\n" +
"}";
String html = convertToHtml(json);
System.out.println(html);
}
}
@@ -0,0 +1,125 @@
//package org.diylc.plugins.chatbot.service;
//
//import org.diylc.core.IContinuity;
//import org.diylc.core.IDIYComponent;
//import org.diylc.core.ISwitch;
//import org.diylc.netlist.Node;
//import org.diylc.presenter.ContinuityArea;
//
//import java.awt.geom.Area;
//import java.awt.geom.Point2D;
//import java.util.*;
//
//public class NetlistBuilder {
// private static final double eps = 1e-6;
//
// public static List<Set<Node>> buildNets(List<IDIYComponent<?>> components, List<ContinuityArea> continuityAreas) {
// Map<Node, Integer> terminalIndex = new HashMap<>();
// List<Node> allTerminals = new ArrayList<>();
// int idx = 0;
//
// for (IDIYComponent<?> c : components) {
// for (int i = 0; i < c.getControlPointCount(); i++) {
// if (c.isControlPointSticky(i)) {
// Node ref = new Node(c, i);
// terminalIndex.put(ref, idx++);
// allTerminals.add(ref);
// }
// }
// }
//
// UnionFind uf = new UnionFind(allTerminals.size());
//
// // Step 1: connect conductive components
// for (IDIYComponent<?> c : components) {
// if (c instanceof IContinuity continuity && !(c instanceof ISwitch)) {
// int count = c.getControlPointCount();
// for (int i = 0; i < count; i++) {
// for (int j = i + 1; j < count; j++) {
// if (continuity.arePointsConnected(i, j)) {
// Node a = new Node(c, i);
// Node b = new Node(c, j);
// uf.union(terminalIndex.get(a), terminalIndex.get(b));
// }
// }
// }
// }
// }
//
// // Step 2: connect terminals inside continuity areas
// for (ContinuityArea continuity : continuityAreas) {
// Area area = continuity.getSimplifiedArea();
// List<Node> inside = new ArrayList<>();
//
// for (Node t : allTerminals) {
// Point2D p = t.getPoint2D();
// if (area.contains(p) || area.intersects(p.getX() - eps/2, p.getY() - eps/2, eps, eps)) {
// inside.add(t);
// }
// }
//
// for (int i = 0; i < inside.size(); i++) {
// for (int j = i + 1; j < inside.size(); j++) {
// uf.union(terminalIndex.get(inside.get(i)), terminalIndex.get(inside.get(j)));
// }
// }
// }
//
// Map<Integer, Set<Node>> netMap = new HashMap<>();
// for (Node t : allTerminals) {
// int root = uf.find(terminalIndex.get(t));
// netMap.computeIfAbsent(root, k -> new HashSet<>()).add(t);
// }
//
// // Filter out nets with only one terminal and remove exact duplicates
// List<Set<Node>> nets = new ArrayList<>();
// Set<Set<Node>> seen = new HashSet<>();
//
// for (Set<Node> net : netMap.values()) {
// if (net.size() > 1 && seen.add(net)) {
// nets.add(net);
// }
// }
// return nets;
//
// }
//
// // Union-Find with path compression and union by size
// static class UnionFind {
// private final int[] parent;
// private final int[] size;
//
// UnionFind(int n) {
// parent = new int[n];
// size = new int[n];
// for (int i = 0; i < n; i++) {
// parent[i] = i;
// size[i] = 1;
// }
// }
//
// int find(int x) {
// if (parent[x] != x) {
// parent[x] = find(parent[x]); // Path compression
// }
// return parent[x];
// }
//
// void union(int x, int y) {
// int rootX = find(x);
// int rootY = find(y);
//
// if (rootX == rootY) return;
//
// // Union by size: attach smaller tree to larger tree
// if (size[rootX] < size[rootY]) {
// parent[rootX] = rootY;
// size[rootY] += size[rootX];
// } else {
// parent[rootY] = rootX;
// size[rootX] += size[rootY];
// }
// }
// }
//}
//
@@ -885,8 +885,13 @@ public class DrawingManager {
List<ContinuityArea> continuityAreas = getContinuityAreas();
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for
// approx good results
List<Area> expanded = continuityAreas.parallelStream()
.map((a) -> new Area(s.createStrokedShape(a.getArea()))).collect(Collectors.toList());
List<Area> areas = new ArrayList<>(continuityAreas.stream().map(ContinuityArea::getArea).toList());
AreaUtils.crunchAreas(areas, null);
List<Area> expanded = areas.parallelStream()
.map((a) -> new Area(s.createStrokedShape(a))).toList();
List<Area> intersections = new ArrayList<Area>();
for (int i = 0; i < expanded.size() - 1; i++) {
for (int j = i + 1; j < expanded.size(); j++) {
@@ -63,6 +63,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import org.diylc.common.ObjectCache;
import org.diylc.common.ZoomableStroke;
import org.diylc.core.IDrawingObserver;
@@ -79,9 +80,14 @@ import org.diylc.core.IDrawingObserver;
class G2DWrapper extends Graphics2D implements IDrawingObserver {
public static int LINE_SENSITIVITY_MARGIN = 2;
public static int CURVE_SENSITIVITY = 6;
public static int CURVE_SENSITIVITY = 6;
private static final Logger LOG = Logger.getLogger(G2DWrapper.class);
public static String DEBUG_AREA_PERFORMANCE = "org.diylc.debugAreaPerformance";
private boolean drawingComponent = false;
private final boolean debugAreaPerformance;
private boolean trackingAllowed = true;
private boolean trackingContinuityAllowed = false;
private boolean trackingContinuityPositive = true;
@@ -115,6 +121,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
public G2DWrapper(Graphics2D canvasGraphics, double zoom) {
super();
this.canvasGraphics = canvasGraphics;
String debugAreaPerformanceStr = System.getProperty(DEBUG_AREA_PERFORMANCE);
debugAreaPerformance = "true".equalsIgnoreCase(debugAreaPerformanceStr);
this.zoom = zoom;
currentArea = new Area();
continuityPositiveAreas = new HashMap<String, Area>();
@@ -236,8 +244,18 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
Area area = new Area(s);
area.transform(currentTx);
if (trackingAllowed)
currentArea.add(area);
Area areaBefore = null;
if (debugAreaPerformance) {
areaBefore = new Area(currentArea);
}
if (trackingAllowed) {
currentArea.add(area);
if (debugAreaPerformance && currentArea.equals(areaBefore)) {
LOG.warn("Detected unnecessary shape tracking at", new Throwable());
}
}
Map<String, Area> toAdd;
if (trackingContinuityAllowed) {
@@ -2831,7 +2831,13 @@ public class Presenter implements IPlugInPort {
return chatbotService;
}
@Override
public MessageDispatcher<EventType> getMessageDispatcher() {
return messageDispatcher;
}
@Override
public DrawingManager getDrawingManager() {
return drawingManager;
}
}
@@ -177,6 +177,7 @@ public class ProjectFileManager {
if (!isBackup) {
this.currentFileName = fileName;
this.modified = false;
messageDispatcher.dispatchMessage(EventType.PROJECT_SAVED, getCurrentFileName());
fireFileStatusChanged();
}
}
@@ -31,6 +31,7 @@ import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.Utils;
import org.diylc.core.annotations.IAutoCreator;
import org.diylc.netlist.IGuitarDiagramAnalyzer;
import org.diylc.netlist.INetlistParser;
import org.diylc.presenter.Presenter;
@@ -89,4 +90,25 @@ public class ReflectionUtils {
return null;
}
}
public static IGuitarDiagramAnalyzer getGuitarDiagramAnalyzer() throws Exception {
Set<Class<?>> classes;
classes = Utils.getClasses("org.diylc.netlist");
List<IGuitarDiagramAnalyzer> result = new ArrayList<>();
for (Class<?> clazz : classes) {
if (!Modifier.isAbstract(clazz.getModifiers()) && IGuitarDiagramAnalyzer.class.isAssignableFrom(clazz)) {
result.add((IGuitarDiagramAnalyzer) clazz.getDeclaredConstructor().newInstance());
}
}
if (result.isEmpty()) {
return null;
}
if (result.size() > 1) {
throw new RuntimeException("Found more than one IGuitarDiagramAnalyzer");
}
return result.get(0);
}
}
+116 -52
View File
@@ -1,57 +1,5 @@
<java.util.Arrays_-ArrayList>
<a class="org.diylc.appframework.update.Version-array">
<org.diylc.appframework.update.Version>
<versionNumber>
<major>4</major>
<minor>41</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-03-08 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Export traces, pads (with holes), ground fill and PCB text to Gerber files</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added ability to select PTH or NPTH holes for solder pads</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Optimized continuity calculations to be faster</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased maximum available memory for DIYLC to 4GB</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Upgraded Java JRE to version 17 LTE</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Added support for high DPI displays</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Added windows x64 build</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Pasting a building block sometimes messes up the z-order of components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Pasting a building block sometimes switches selection to previously added components and canvas moves to the wrong position</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Highlight Continuity Areas' sometimes renders stale areas when clicked</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>4</major>
@@ -252,5 +200,121 @@
</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>3</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-04-21 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved AI Assistant quality of responses, especially when analyzing a circuit</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>When saving a project to the new file, retain all the AI Assistant chat history for the new project file</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Added ability to be logged into the same DIYLC Cloud account from several machines at the same time</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Confirmation dialog before deleting AI Assistant chat history</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Output current subscription statistics at the end of the chat for usage transparency</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved styling and usability of chatbot tool</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Language selection doesn't work on some OSs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Connect to Patreon' option doesn't work if the user name contains a space or another non-alphanumerical character</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>4</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>'AI Analyzer' feature added which uses AI to scan the circuit diagram and report issues and suggestions</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Significantly improve rendering performance of most components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'Open Jack 1/4"' and 'Potentiometer Panel' now tolerate connections anywhere on the lugs instead of dead center only</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased maximum number of switching combinations to 256 for circuit analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Pilot Lamp Holder' component can sometimes hang the application</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with embedding image files into DIY projects</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with some components (e.g. DIP IC) jumping when showing underside of a board</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>5</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>'Trimmer Potentiometer' is now recognized the same as 'Panel Potentiometer' in the guitar diagram analyzer tool</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased tolerance of sticky points on guitar pickup terminals in order to make it easier for leads to make proper contact and for the circuit analyzer to work</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved trace proximity calculation to avoid showing false positives</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved the styling of circuit analysis reports</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>If the internet connection is present, but unstable, the application starts but nothing can be clicked until the connection restores</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>
@@ -4721,5 +4721,57 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>4</major>
<minor>41</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-03-08 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Export traces, pads (with holes), ground fill and PCB text to Gerber files</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added ability to select PTH or NPTH holes for solder pads</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Optimized continuity calculations to be faster</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased maximum available memory for DIYLC to 4GB</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Upgraded Java JRE to version 17 LTE</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Added support for high DPI displays</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Added windows x64 build</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Pasting a building block sometimes messes up the z-order of components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Pasting a building block sometimes switches selection to previously added components and canvas moves to the wrong position</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Highlight Continuity Areas' sometimes renders stale areas when clicked</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>
@@ -0,0 +1,188 @@
//package org.diylc.plugins.chatbot.service;
//
//import org.diylc.core.IContinuity;
//import org.diylc.core.IDIYComponent;
//import org.diylc.core.ComponentState;
//import org.diylc.core.IDrawingObserver;
//import org.diylc.core.Project;
//import org.diylc.core.VisibilityPolicy;
//import org.diylc.netlist.Node;
//import org.diylc.presenter.ContinuityArea;
//import org.junit.Test;
//import org.diylc.components.AbstractComponent;
//
//import java.awt.Graphics2D;
//import java.awt.geom.Area;
//import java.awt.geom.Point2D;
//import java.util.*;
//
//import static org.junit.Assert.*;
//
//public class NetlistBuilderTest {
//
// // Mock component that implements IContinuity
// private static class MockConductiveComponent extends AbstractComponent<Void> implements IContinuity {
// private final Point2D[] points;
// private final boolean[][] connections;
//
// public MockConductiveComponent(Point2D[] points, boolean[][] connections) {
// this.points = points;
// this.connections = connections;
// }
//
// @Override
// public int getControlPointCount() {
// return points.length;
// }
//
// @Override
// public Point2D getControlPoint(int index) {
// return points[index];
// }
//
// @Override
// public boolean isControlPointSticky(int index) {
// return true;
// }
//
// @Override
// public boolean arePointsConnected(int index1, int index2) {
// return connections[index1][index2];
// }
//
// @Override
// public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, IDrawingObserver drawingObserver) {
// // Not needed for testing
// }
//
// @Override
// public void drawIcon(Graphics2D g2d, int width, int height) {
// // Not needed for testing
// }
//
// @Override
// public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
// return VisibilityPolicy.ALWAYS;
// }
//
// @Override
// public void setControlPoint(Point2D point, int index) {
// // Not needed for testing
// }
//
// @Override
// public Void getValue() {
// return null;
// }
//
// @Override
// public void setValue(Void value) {
// // Not needed for testing
// }
// }
//
// // Mock component that doesn't implement IContinuity
// private static class MockNonConductiveComponent extends AbstractComponent<Void> {
// private final Point2D[] points;
//
// public MockNonConductiveComponent(Point2D[] points) {
// this.points = points;
// }
//
// @Override
// public int getControlPointCount() {
// return points.length;
// }
//
// @Override
// public Point2D getControlPoint(int index) {
// return points[index];
// }
//
// @Override
// public boolean isControlPointSticky(int index) {
// return true;
// }
//
// @Override
// public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, IDrawingObserver drawingObserver) {
// // Not needed for testing
// }
//
// @Override
// public void drawIcon(Graphics2D g2d, int width, int height) {
// // Not needed for testing
// }
//
// @Override
// public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
// return VisibilityPolicy.ALWAYS;
// }
//
// @Override
// public void setControlPoint(Point2D point, int index) {
// // Not needed for testing
// }
//
// @Override
// public Void getValue() {
// return null;
// }
//
// @Override
// public void setValue(Void value) {
// // Not needed for testing
// }
// }
//
// @Test
// public void testBuildNets() {
// // Create test components
// Point2D[] points1 = {
// new Point2D.Double(0, 0),
// new Point2D.Double(10, 0),
// new Point2D.Double(20, 0)
// };
// boolean[][] connections1 = {
// {false, true, false},
// {true, false, true},
// {false, true, false}
// };
// MockConductiveComponent comp1 = new MockConductiveComponent(points1, connections1);
//
// Point2D[] points2 = {
// new Point2D.Double(30, 0),
// new Point2D.Double(40, 0)
// };
// MockNonConductiveComponent comp2 = new MockNonConductiveComponent(points2);
//
// // Create a continuity area that connects comp1 and comp2
// Area area = new Area();
// area.add(new Area(new java.awt.geom.Rectangle2D.Double(15, -5, 20, 10)));
// ContinuityArea continuityArea = new ContinuityArea(0, area);
//
// // Build nets
// List<IDIYComponent<?>> components = Arrays.asList(comp1, comp2);
// List<ContinuityArea> continuityAreas = Collections.singletonList(continuityArea);
// List<Set<Node>> nets = NetlistBuilder.buildNets(components, continuityAreas);
//
// // Verify results
// assertEquals("Should have exactly one net", 1, nets.size());
// Set<Node> net = nets.get(0);
// assertEquals("Net should contain all terminals", 5, net.size());
//
// // Verify that all terminals are connected
// Set<Integer> terminalIndices = new HashSet<>();
// for (Node node : net) {
// terminalIndices.add(node.getPointIndex());
// System.out.println("Found terminal: " + node.getPointIndex() + " at " + node.getPoint2D());
// }
// System.out.println("All terminals: " + terminalIndices);
// assertEquals("Should contain all terminal indices", 5, terminalIndices.size());
// assertTrue("Should contain terminal 0", terminalIndices.contains(0));
// assertTrue("Should contain terminal 1", terminalIndices.contains(1));
// assertTrue("Should contain terminal 2", terminalIndices.contains(2));
// assertTrue("Should contain terminal 3", terminalIndices.contains(3));
// assertTrue("Should contain terminal 4", terminalIndices.contains(4));
// }
//}
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>org.diylc</groupId>
<artifactId>diylc-parent</artifactId>
<version>5.2.0</version>
<version>5.4.0</version>
</parent>
<artifactId>diylc-library</artifactId>
@@ -355,39 +355,42 @@ public abstract class AbstractLeadedComponent<T> extends AbstractLabeledComponen
if (getDisplay() == Display.BOTH) {
label = getName() + " " + (getValue() == null ? "" : getValue().toString());
}
Rectangle2D textRect = fontMetrics.getStringBounds(label, g2d);
// Don't offset in outline mode.
int offset = outlineMode ? 0 : getLabelOffset((int) length, (int) width, (int) textRect.getWidth());
// Adjust label angle if needed to make sure that it's readable.
if ((theta >= Math.PI / 2 && theta <= Math.PI) || (theta < -Math.PI / 2 && theta > -Math.PI)) {
g2d.rotate(Math.PI, length / 2, width / 2);
theta += Math.PI;
offset = -offset;
}
drawingObserver.startTracking();
if (getMoveLabel()) {
g2d.setTransform(oldTransform);
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
if (getLabelOriantation() != LabelOriantation.Horizontal) {
g2d.rotate(theta);
if (!label.isEmpty()) {
Rectangle2D textRect = fontMetrics.getStringBounds(label, g2d);
// Don't offset in outline mode.
int offset = outlineMode ? 0 : getLabelOffset((int) length, (int) width, (int) textRect.getWidth());
// Adjust label angle if needed to make sure that it's readable.
if ((theta >= Math.PI / 2 && theta <= Math.PI) || (theta < -Math.PI / 2 && theta > -Math.PI)) {
g2d.rotate(Math.PI, length / 2, width / 2);
theta += Math.PI;
offset = -offset;
}
StringUtils.drawCenteredText(g2d, label, offset, 0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
g2d.setTransform(oldTransform);
} else {
if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
if (getMoveLabel()) {
drawingObserver.startTracking();
g2d.setTransform(oldTransform);
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
if (getLabelOriantation() != LabelOriantation.Horizontal) {
g2d.rotate(theta);
}
StringUtils.drawCenteredText(g2d, label, offset, 0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
g2d.setTransform(oldTransform);
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
(int)(y + calculateLabelYOffset(shapeRect, textRect, fontMetrics)));
} else {
g2d.drawString(label, (int) (length - textRect.getWidth()) / 2 + offset,
calculateLabelYOffset(shapeRect, textRect, fontMetrics));
g2d.setTransform(oldTransform);
}
if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
drawingObserver.startTracking();
g2d.setTransform(oldTransform);
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
(int) (y + calculateLabelYOffset(shapeRect, textRect, fontMetrics)));
} else {
drawingObserver.stopTracking();
g2d.drawString(label, (int) (length - textRect.getWidth()) / 2 + offset,
calculateLabelYOffset(shapeRect, textRect, fontMetrics));
g2d.setTransform(oldTransform);
}
}
}
// if (getLabelOriantation() == LabelOriantation.Horizontal) {
//
@@ -213,6 +213,9 @@ public class PCBTerminalBlock extends AbstractTransparentComponent<PCBTerminalBl
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
g2d.fill(body[0]);
// Do not track these changes because the whole switch has been tracked
// so far.
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Color finalBorderColor;
@@ -232,9 +235,7 @@ public class PCBTerminalBlock extends AbstractTransparentComponent<PCBTerminalBl
g2d.setColor(finalBorderColor);
g2d.draw(body[0]);
}
// Do not track these changes because the whole switch has been tracked
// so far.
drawingObserver.stopTracking();
// Draw lugs.
int circleDiameter = getClosestOdd((int) (getPitch().convertToPixels() * 3d / 5));
@@ -109,6 +109,7 @@ public class SolderPad extends AbstractTransparentComponent<Void>
g2d.fill(new Rectangle2D.Double(point.getX() - diameter / 2, point.getY() - diameter / 2,
diameter, diameter));
}
drawingObserver.stopTracking();
drawingObserver.stopTrackingContinuityArea();
if (gerberDrawingObserver != null)
gerberDrawingObserver.stopGerberOutput(org.diylc.core.gerber.GerberLayer.SolderMaskBot);
@@ -103,6 +103,8 @@ public class BatterySnap9V extends AbstractTransparentComponent<String> {
: color.brighter();
}
drawingObserver.stopTracking();
g2d.setColor(finalBorderColor);
g2d.draw(body[0]);
g2d.setColor(METAL_COLOR.darker());
@@ -236,6 +236,8 @@ public class ClosedJack1_4 extends AbstractMultiPartComponent<JackType> implemen
g2d.setComposite(oldComposite);
// }
drawingObserver.stopTracking();
Color finalBorderColor;
if (outlineMode) {
Theme theme =
@@ -356,6 +356,8 @@ public class DIPSwitch extends AbstractTransparentComponent<String> implements I
g2d.fill(mainArea);
g2d.setComposite(oldComposite);
drawingObserver.stopTracking();
Color finalBorderColor;
if (outlineMode) {
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
@@ -392,8 +394,6 @@ public class DIPSwitch extends AbstractTransparentComponent<String> implements I
}
}
drawingObserver.stopTracking();
// Draw label.
g2d.setFont(project.getFont());
Color finalLabelColor;
@@ -304,6 +304,8 @@ public class MiniRelay extends AbstractLabeledComponent<String> implements IGerb
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
g2d.fill(mainArea);
g2d.setComposite(oldComposite);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -129,18 +129,29 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
if (alpha < MAX_ALPHA) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BASE_COLOR);
g2d.setColor(Constants.TRANSPARENT_COLOR);
drawingObserver.startTrackingContinuityArea(true);
g2d.fill(body[1]);
g2d.fill(body[2]);
if (body[3] != null)
g2d.fill(body[3]);
g2d.fill(body[4]);
g2d.fill(body[5]);
if (body[6] != null)
g2d.fill(body[6]);
drawingObserver.stopTrackingContinuityArea();
if (!outlineMode) {
g2d.setColor(BASE_COLOR);
g2d.fill(body[1]);
g2d.fill(body[2]);
if (body[3] != null)
g2d.fill(body[3]);
}
g2d.setComposite(oldComposite);
// }
drawingObserver.stopTracking();
if (outlineMode) {
Theme theme =
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
@@ -148,16 +159,12 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
} else {
finalBorderColor = BASE_COLOR.darker();
}
drawingObserver.stopTracking();
g2d.setColor(finalBorderColor);
g2d.draw(body[1]);
g2d.draw(body[2]);
if (body[3] != null)
g2d.draw(body[3]);
drawingObserver.startTracking();
// draw labels
if (showLabels) {
@@ -193,7 +200,7 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
public Area[] getBody() {
if (body == null) {
body = new Area[4];
body = new Area[7];
double x = controlPoints[0].getX();
double y = controlPoints[0].getY();
@@ -224,6 +231,13 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
body[1] = tip;
// no-hole tip for continuity
tip =
new Area(new RoundRectangle2D.Double(x - springWidth / 2, y - holeToEdge, springWidth, springLength
- ringDiameter / 2, springWidth, springWidth));
tip.subtract(wafer);
body[4] = tip;
Area sleeve =
new Area(new RoundRectangle2D.Double(x - springWidth / 2, y - holeToEdge, springWidth, springLength,
springWidth, springWidth));
@@ -237,6 +251,17 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
body[2] = sleeve;
// no-hole sleeve for continuity
sleeve =
new Area(new RoundRectangle2D.Double(x - springWidth / 2, y - holeToEdge, springWidth, springLength,
springWidth, springWidth));
sleeve.transform(AffineTransform.getRotateInstance(getValue() == OpenJackType.SWITCHED ? SLEEVE_SWITCHED_THETA : SLEEVE_THETA, x, centerY));
sleeve.add(new Area(new Ellipse2D.Double(x - ringDiameter / 2, centerY - ringDiameter / 2, ringDiameter,
ringDiameter)));
sleeve.subtract(new Area(new Ellipse2D.Double(x - innerDiameter / 2, centerY - innerDiameter / 2, innerDiameter,
innerDiameter)));
body[5] = sleeve;
if (getValue() != OpenJackType.MONO) {
Area ringOrSwitch =
new Area(new RoundRectangle2D.Double(x - springWidth / 2, y - holeToEdge, springWidth, springLength,
@@ -248,6 +273,15 @@ public class OpenJack1_4 extends AbstractMultiPartComponent<OpenJackType> {
outerDiameter)));
body[3] = ringOrSwitch;
ringOrSwitch =
new Area(new RoundRectangle2D.Double(x - springWidth / 2, y - holeToEdge, springWidth, springLength,
springWidth, springWidth));
ringOrSwitch.transform(AffineTransform.getRotateInstance(getValue() == OpenJackType.SWITCHED ? SWITCH_THETA : RING_THETA, x, centerY));
ringOrSwitch.subtract(new Area(new Ellipse2D.Double(x - outerDiameter / 2, centerY - outerDiameter / 2, outerDiameter,
outerDiameter)));
body[6] = ringOrSwitch;
}
// Rotate if needed
@@ -141,6 +141,8 @@ public class OpenJack1_8 extends AbstractMultiPartComponent<OpenJackType> {
g2d.setComposite(oldComposite);
// }
drawingObserver.stopTracking();
if (outlineMode) {
Theme theme =
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
@@ -148,8 +150,6 @@ public class OpenJack1_8 extends AbstractMultiPartComponent<OpenJackType> {
} else {
finalBorderColor = BASE_COLOR.darker();
}
drawingObserver.stopTracking();
g2d.setColor(finalBorderColor);
g2d.draw(body[1]);
@@ -134,6 +134,7 @@ public class PilotLampHolder extends AbstractMultiPartComponent<String> {
g2d.fill(body[2]);
drawingObserver.stopTrackingContinuityArea();
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
// }
@@ -232,6 +232,7 @@ public class PinHeader extends AbstractTransparentComponent<Void> {
g2d.setColor(shroudColor);
g2d.fill(shroudArea);
}
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -142,6 +142,7 @@ public class PlasticDCJack extends AbstractMultiPartComponent<String> implements
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
g2d.fill(body[0]);
drawingObserver.stopTracking();
if (!outlineMode) {
g2d.setColor(PHENOLIC_DARK_COLOR);
g2d.fill(body[1]);
@@ -93,6 +93,8 @@ public class RCAJack extends AbstractMultiPartComponent<String> {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : WAFER_COLOR);
g2d.fill(body[0]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
// }
@@ -116,8 +118,10 @@ public class RCAJack extends AbstractMultiPartComponent<String> {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BASE_COLOR);
drawingObserver.startTrackingContinuityArea(true);
drawingObserver.startTracking();
g2d.fill(body[1]);
g2d.fill(body[2]);
drawingObserver.stopTracking();
drawingObserver.stopTrackingContinuityArea();
if (body[3] != null)
@@ -362,7 +362,9 @@ public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenTy
drawingObserver.startTrackingContinuityArea(true);
for (int i = secondLevelStart; i < body.length; i++) {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : pinColor);
drawingObserver.startTracking();
g2d.fill(body[i]);
drawingObserver.stopTracking();
g2d.setColor(outlineMode ? finalBorderColor : pinColor.darker());
g2d.draw(body[i]);
}
@@ -376,8 +378,8 @@ public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenTy
}
g2d.setColor(finalBorderColor);
drawingObserver.startTracking();
g2d.draw(body[0]);
drawingObserver.stopTracking();
// Draw pins
@@ -182,6 +182,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getColor());
g2d.fill(body[0]);
}
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
if (outlineMode) {
@@ -198,8 +199,6 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
g2d.setColor(finalBorderColor);
g2d.draw(body[0]);
drawingObserver.stopTracking();
// Draw pins
if (!outlineMode) {
for (int i = 1; i < controlPoints.length; i++) {
@@ -247,7 +247,7 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
g2d.fill(body[0]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Color finalBorderColor;
@@ -269,9 +269,7 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
}
g2d.draw(body[0]);
}
// Do not track these changes because the whole switch has been tracked
// so far.
drawingObserver.stopTracking();
// Draw lugs.
int lugWidth;
@@ -232,7 +232,9 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
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.stopTrackingContinuityArea();
g2d.setColor(PIN_BORDER_COLOR);
g2d.draw(rect);
}
@@ -242,7 +244,9 @@ public class TactileMicroSwitch extends AbstractLabeledComponent<String> impleme
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
drawingObserver.stopTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setColor(INDENT_COLOR);
g2d.fill(indentArea);
@@ -23,6 +23,7 @@ import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
@@ -34,10 +35,7 @@ import org.diylc.common.Orientation;
import org.diylc.common.OrientationHV;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractLabeledComponent;
import org.diylc.core.ComponentState;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
import org.diylc.core.*;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
@@ -48,6 +46,7 @@ public abstract class AbstractGuitarPickup extends AbstractLabeledComponent<Stri
private static final long serialVersionUID = 1L;
protected static Size POINT_SPACING = new Size(0.1d, SizeUnit.in);
private static Size POINT_SIZE = new Size(1.5d, SizeUnit.mm);
protected static final int TERMINAL_FONT_SIZE = 11;
@@ -66,7 +65,7 @@ public abstract class AbstractGuitarPickup extends AbstractLabeledComponent<Stri
updateControlPoints();
}
protected void drawlTerminalLabels(Graphics2D g2d, Color color, Project project) {
protected void drawTerminalLabels(Graphics2D g2d, Color color, Project project) {
Point2D[] points = getControlPoints();
g2d.setColor(color);
@@ -131,6 +130,21 @@ public abstract class AbstractGuitarPickup extends AbstractLabeledComponent<Stri
g2d.setTransform(originalTx);
}
protected void markContactPoints(Graphics2D g2d, IDrawingObserver drawingObserver) {
g2d.setColor(Constants.TRANSPARENT_COLOR);
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
drawingObserver.startTrackingContinuityArea(true);
for (int i = 0; i < getControlPointCount(); i++) {
if (isControlPointSticky(i)) {
Point2D p = getControlPoint(i);
g2d.fill(
new Ellipse2D.Double(p.getX() - pointSize / 2, p.getY() - pointSize / 2, pointSize,
pointSize));
}
}
drawingObserver.stopTrackingContinuityArea();
}
protected abstract Shape[] getBody();
@@ -34,7 +34,7 @@ public abstract class AbstractSingleOrHumbuckerPickup extends AbstractGuitarPick
private static final long serialVersionUID = 1L;
@Override
protected void drawlTerminalLabels(Graphics2D g2d, Color color, Project project) {
protected void drawTerminalLabels(Graphics2D g2d, Color color, Project project) {
Point2D[] points = getControlPoints();
g2d.setColor(color);
@@ -74,7 +74,6 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
private static Size EDGE_RADIUS = new Size(4d, SizeUnit.mm);
private static Size POINT_MARGIN = new Size(1.5d, SizeUnit.mm);
private static Size SCREW_LINE = new Size(1d, SizeUnit.mm);
private static Size POINT_SIZE = new Size(2d, SizeUnit.mm);
private static Size LIP_HOLE_SIZE = new Size(2d, SizeUnit.mm);
private static Size POLE_SIZE = new Size(4d, SizeUnit.mm);
private static Size POLE_SIZE_FILTERTRON = new Size(5d, SizeUnit.mm);
@@ -93,6 +92,8 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
drawingObserver.stopTracking();
Shape[] body = getBody();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -102,10 +103,16 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
if (getType() != HumbuckerType.Filtertron)
if (getType() != HumbuckerType.Filtertron) {
drawingObserver.startTracking();
g2d.fill(body[0]);
if (body[1] != null)
drawingObserver.stopTracking();
}
if (body[1] != null) {
drawingObserver.startTracking();
g2d.fill(body[1]);
drawingObserver.stopTracking();
}
if (!outlineMode) {
if (body[4] != null) {
@@ -120,15 +127,13 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
if (getType() == HumbuckerType.Filtertron) {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
drawingObserver.startTracking();
g2d.fill(body[0]);
drawingObserver.stopTracking();
}
// g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : POINT_COLOR);
// g2d.fill(body[2]);
g2d.setComposite(oldComposite);
Color finalBorderColor;
if (outlineMode) {
Theme theme =
@@ -148,6 +153,8 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
if (body[1] != null)
g2d.draw(body[1]);
markContactPoints(g2d, drawingObserver);
if (!outlineMode) {
g2d.setColor(getPoleColor());
g2d.fill(body[3]);
@@ -175,7 +182,7 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
}
@Override
@@ -198,7 +205,7 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
int lipLength = (int) LIP_LENGTH.convertToPixels();
int edgeRadius = (int) EDGE_RADIUS.convertToPixels();
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
int lipHoleSize = getClosestOdd(LIP_HOLE_SIZE.convertToPixels());
int poleSize = (int) getType().getPoleSize().convertToPixels();
int poleSpacing = (int) POLE_SPACING.convertToPixels();
@@ -269,9 +276,6 @@ public class HumbuckerPickup extends AbstractGuitarPickup {
body[1] = legArea;
}
// contact point
body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize));
Area poleArea = new Area();
Area poleDecorationArea = new Area();
@@ -70,7 +70,6 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
private static Size LIP_HOLE_SPACING = new Size(0.1d, SizeUnit.in);
private static Size POINT_MARGIN = new Size(1.5d, SizeUnit.mm);
private static Size POINT_SIZE = new Size(2d, SizeUnit.mm);
private static Size POLE_SIZE = new Size(4d, SizeUnit.mm);
private static Size POLE_SPACING = new Size(0.85d, SizeUnit.in);
private static Size POLE_SPACING_MINOR = new Size(0.28d, SizeUnit.in);
@@ -81,6 +80,7 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
Shape[] body = getBody();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -91,9 +91,12 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
g2d.fill(body[0]);
g2d.fill(body[1]);
g2d.fill(body[1]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
markContactPoints(g2d, drawingObserver);
Color finalBorderColor;
if (outlineMode) {
Theme theme =
@@ -137,7 +140,7 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
// VerticalAlignment.CENTER);
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
}
@SuppressWarnings("incomplete-switch")
@@ -155,7 +158,6 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int lipRadius = (int) LIP_RADIUS.convertToPixels();
int lipSpacing = (int) LIP_SPACING.convertToPixels();
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
int lipHoleSize = getClosestOdd(LIP_HOLE_SIZE.convertToPixels());
int lipHoleSpacing = getClosestOdd(LIP_HOLE_SPACING.convertToPixels());
@@ -178,8 +180,6 @@ public class JazzBassPickup extends AbstractSingleOrHumbuckerPickup {
((Area)body[1]).subtract((Area)body[0]);
body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize));
int poleSize = (int) POLE_SIZE.convertToPixels();
int poleSpacing = (int) POLE_SPACING.convertToPixels();
int poleSpacingMinor = (int) POLE_SPACING_MINOR.convertToPixels();
@@ -93,6 +93,8 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
g2d.setComposite(oldComposite);
}
drawingObserver.stopTracking();
Color finalBorderColor;
if (outlineMode) {
@@ -127,7 +129,9 @@ public class LPSwitch extends AbstractTransparentComponent<String> implements IS
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
g2d.setColor(METAL_COLOR);
drawingObserver.startTracking();
g2d.draw(body[2]);
drawingObserver.stopTracking();
}
@SuppressWarnings("incomplete-switch")
@@ -111,6 +111,8 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
g2d.setComposite(oldComposite);
}
drawingObserver.stopTracking();
Color finalBorderColor;
if (outlineMode) {
Theme theme =
@@ -142,7 +144,9 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitch.LeverSwitch
g2d.draw(body[1]);
g2d.setColor(LUG_COLOR);
drawingObserver.startTracking();
g2d.fill(body[2]);
drawingObserver.stopTracking();
g2d.setColor(LUG_COLOR.darker());
g2d.draw(body[2]);
@@ -75,7 +75,6 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
private static Size LIP_RADIUS = new Size(10d, SizeUnit.mm);
private static Size POINT_MARGIN = new Size(3.5d, SizeUnit.mm);
private static Size POINT_SIZE = new Size(2d, SizeUnit.mm);
private static Size LIP_HOLE_SIZE = new Size(2.5d, SizeUnit.mm);
private static Size LIP_HOLE_SPACING = new Size(97d, SizeUnit.mm);
private static Size POLE_SIZE = new Size(4d, SizeUnit.mm);
@@ -107,6 +106,7 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
g2d.fill(body[1]);
// g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : POINT_COLOR);
// g2d.fill(body[2]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -126,7 +126,9 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
g2d.draw(body[0]);
if (body[1] != null)
g2d.draw(body[1]);
markContactPoints(g2d, drawingObserver);
if (!outlineMode) {
g2d.setColor(getPoleColor());
g2d.fill(body[3]);
@@ -153,7 +155,7 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
// VerticalAlignment.CENTER);
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
// terminal labels
// Point[] points = getControlPoints();
// g2d.setColor(finalBorderColor);
@@ -204,7 +206,6 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int totalLength = (int) TOTAL_LENGTH.convertToPixels();
int lipRadius = (int) LIP_RADIUS.convertToPixels();
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
int lipHoleSize = getClosestOdd(LIP_HOLE_SIZE.convertToPixels());
int lipHoleSpacing = getClosestOdd(LIP_HOLE_SPACING.convertToPixels());
@@ -236,8 +237,6 @@ public class P90Pickup extends AbstractSingleOrHumbuckerPickup {
body[1] = lipArea;
}
body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize));
int poleSize = (int) POLE_SIZE.convertToPixels();
int poleSpacing = (int) POLE_SPACING.convertToPixels();
int poleMargin = (length - poleSpacing * 5) / 2;
@@ -69,7 +69,6 @@ public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
private static Size LIP_HOLE_SPACING = new Size(0.1d, SizeUnit.in);
private static Size POINT_MARGIN = new Size(1.5d, SizeUnit.mm);
private static Size POINT_SIZE = new Size(2d, SizeUnit.mm);
private static Size POLE_SIZE = new Size(4d, SizeUnit.mm);
private static Size POLE_SPACING = new Size(0.38d, SizeUnit.in);
@@ -90,8 +89,11 @@ public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
g2d.fill(body[0]);
g2d.fill(body[1]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
markContactPoints(g2d, drawingObserver);
Color finalBorderColor;
if (outlineMode) {
Theme theme =
@@ -135,7 +137,7 @@ public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
// VerticalAlignment.CENTER);
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
}
@SuppressWarnings("incomplete-switch")
@@ -152,7 +154,6 @@ public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
int edgeRadius = (int) EDGE_RADIUS.convertToPixels();
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int lipRadius = (int) LIP_RADIUS.convertToPixels();
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
int lipHoleSize = getClosestOdd(LIP_HOLE_SIZE.convertToPixels());
int lipHoleSpacing = getClosestOdd(LIP_HOLE_SPACING.convertToPixels());
@@ -173,8 +174,6 @@ public class PBassPickup extends AbstractSingleOrHumbuckerPickup {
((Area)body[1]).subtract((Area)body[0]);
body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize));
int poleSize = (int) POLE_SIZE.convertToPixels();
int poleSpacing = (int) POLE_SPACING.convertToPixels();
int poleMargin = (length - poleSpacing * 3) / 2;
@@ -162,6 +162,7 @@ public class S1Switch extends AbstractTransparentComponent<Void> implements ISwi
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
g2d.fill(body);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Color finalBorderColor;
@@ -110,11 +110,14 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BASE_COLOR);
g2d.fill(body[0]);
drawingObserver.stopTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : WAFER_COLOR);
g2d.fill(body[1]);
g2d.setComposite(oldComposite);
}
drawingObserver.stopTracking();
Color finalBorderColor;
if (outlineMode) {
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
@@ -150,12 +153,16 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
g2d.draw(body[1]);
g2d.setColor(LUG_COLOR);
drawingObserver.startTracking();
g2d.fill(body[2]);
drawingObserver.stopTracking();
g2d.setColor(LUG_COLOR.darker());
g2d.draw(body[2]);
for (int i = 3; i < body.length; i++) {
g2d.setColor(ISwitch.POLE_COLORS[i - 3]);
drawingObserver.startTracking();
g2d.fill(body[i]);
drawingObserver.stopTracking();
g2d.setColor(ISwitch.POLE_COLORS[i - 3].darker());
g2d.draw(body[i]);
}
@@ -124,7 +124,11 @@ public class SingleCoilPickup extends AbstractSingleOrHumbuckerPickup {
g2d.fill(body[0]);
else
g2d.fill(body[3]);
drawingObserver.stopTracking();
markContactPoints(g2d, drawingObserver);
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getLugColor());
g2d.fill(body[1]);
g2d.setColor(outlineMode ? theme.getOutlineColor() : darkerOrLighter(LUG_COLOR));
@@ -172,7 +176,7 @@ public class SingleCoilPickup extends AbstractSingleOrHumbuckerPickup {
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
}
@Override
@@ -21,28 +21,13 @@
*/
package org.diylc.components.guitar;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.RoundRectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.common.Orientation;
import org.diylc.common.OrientationHV;
import org.diylc.components.transform.GuitarPickupTransformer;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.*;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.KeywordPolicy;
@@ -50,77 +35,86 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
import java.awt.*;
import java.awt.geom.*;
@ComponentDescriptor(name = "Stingray Bass Pickup", category = "Guitar", author = "JD",
description = "Humbucker Bass Pickup for Musicman Stingray and similar guitars",
description = "Humbucker Bass Pickup for Musicman Stingray and similar guitars",
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false,
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram", enableCache = true,
transformer = GuitarPickupTransformer.class)
keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram",
enableCache = true, transformer = GuitarPickupTransformer.class)
/**
* Component type for Stingray Bass Humbucker
* Modified from the P Bass Pickup. Author: JD
* @author JD
*/
public class StingrayBassPickup extends AbstractGuitarPickup {
*/ public class StingrayBassPickup extends AbstractGuitarPickup {
private static final long serialVersionUID = 1L;
private static Color BODY_COLOR = Color.decode("#333333");
private static Size WIDTH = new Size(1.910, SizeUnit.in);
private static Size LENGTH = new Size(3.505, SizeUnit.in);
private static Size EDGE_RADIUS = new Size(0.08d, SizeUnit.in);
private static Size LIP_RADIUS = new Size(0.4d, SizeUnit.in);
private static Size LIP_HOLE_SIZE = new Size(0.1d, SizeUnit.in);
private static Size LIP_HOLE_SPACING = new Size(0.05d, SizeUnit.in);
private static Size LIP_DISTANCE_VERTICAL = new Size(0.935d, SizeUnit.in);// Left side vertical between mount points
private static Size LIP_DISTANCE_VERTICAL = new Size(0.935d, SizeUnit.in);
// Left side vertical between mount points
// private static Size LIP_DISTANCE_HORIZONTAL = new Size(3.7000d, SizeUnit.in); // Distance across pickup
private static Size POINT_MARGIN = new Size(1.5d, SizeUnit.mm);
private static Size POINT_SIZE = new Size(2d, SizeUnit.mm);
private static Size POLE_SIZE = new Size(.39d, SizeUnit.in);
private static Size POLE_SPACING_HORIZONTAL = new Size(0.75366d, SizeUnit.in); // Distance Between poles
private static Size POLE_SPACING_TO_EDGE= new Size(0.4875d, SizeUnit.in);// Outer edge to centre of nearest pole
// (Perpendicular of course!)
private static Size POLE_SPACING_HORIZONTAL = new Size(0.75366d, SizeUnit.in);
// Distance Between poles
private static Size POLE_SPACING_TO_EDGE = new Size(0.4875d, SizeUnit.in);
// Outer edge to centre of nearest pole
// (Perpendicular of course!)
private Color color = BODY_COLOR;
private Color poleColor = METAL_COLOR;
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
Project project, IDrawingObserver drawingObserver) {
Project project, IDrawingObserver drawingObserver) {
Shape[] body = getBody();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Composite oldComposite = g2d.getComposite();
if (alpha < MAX_ALPHA) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
g2d.fill(body[0]);
g2d.fill(body[1]);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
if (outlineMode) {
Theme theme =
(Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
Theme theme = (Theme) ConfigurationManager.getInstance()
.readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
finalBorderColor =
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
: theme.getOutlineColor();
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ?
SELECTION_COLOR :
theme.getOutlineColor();
} else {
finalBorderColor =
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
: color.darker();
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ?
SELECTION_COLOR :
color.darker();
}
g2d.setColor(finalBorderColor);
g2d.draw(body[0]);
g2d.draw(body[1]);
markContactPoints(g2d, drawingObserver);
if (!outlineMode) {
g2d.setColor(getPoleColor());
g2d.fill(body[3]);
@@ -128,27 +122,9 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
g2d.draw(body[3]);
}
// Color finalLabelColor;
// if (outlineMode) {
// Theme theme =
// (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
// finalLabelColor =
// componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
// : theme.getOutlineColor();
// } else {
// finalLabelColor =
// componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
// : LABEL_COLOR;
// }
// g2d.setColor(finalLabelColor);
// g2d.setFont(project.getFont());
// Rectangle bounds = body[0].getBounds();
// drawCenteredText(g2d, value, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, HorizontalAlignment.CENTER,
// VerticalAlignment.CENTER);
drawMainLabel(g2d, project, outlineMode, componentState);
drawlTerminalLabels(g2d, finalBorderColor, project);
drawTerminalLabels(g2d, finalBorderColor, project);
}
@SuppressWarnings("incomplete-switch")
@@ -158,72 +134,76 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
body = new Shape[4];
Point2D[] points = getControlPoints();
double x = points[0].getX();
double y = points[0].getY();
int width = (int) WIDTH.convertToPixels();
int length = (int) LENGTH.convertToPixels();
int edgeRadius = (int) EDGE_RADIUS.convertToPixels();
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int lipRadius = (int) LIP_RADIUS.convertToPixels();
int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
int lipHoleSize = (int) LIP_HOLE_SIZE.convertToPixels();
double x = points[0].getX();
double y = points[0].getY();
int width = (int) WIDTH.convertToPixels();
int length = (int) LENGTH.convertToPixels();
int edgeRadius = (int) EDGE_RADIUS.convertToPixels();
int pointMargin = (int) POINT_MARGIN.convertToPixels();
int lipRadius = (int) LIP_RADIUS.convertToPixels();
int lipHoleSize = (int) LIP_HOLE_SIZE.convertToPixels();
int lipHoleSpacing = getClosestOdd(LIP_HOLE_SPACING.convertToPixels());
int lipDistanceV = (int) LIP_DISTANCE_VERTICAL.convertToPixels();
int lipDistanceV = (int) LIP_DISTANCE_VERTICAL.convertToPixels();
// This is the main body
body[0] =
new Area(new RoundRectangle2D.Double(x - length, y - pointMargin, length, width, edgeRadius,
body[0] = new Area(
new RoundRectangle2D.Double(x - length, y - pointMargin, length, width, edgeRadius,
edgeRadius));
// Lips around pickup - change transforms
Area lip = new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip.subtract(new Area(new Ellipse2D.Double(-lipRadius / 2 + lipHoleSpacing, -lipRadius / 4 + lipHoleSize / 2, lipHoleSize, lipHoleSize)));
lip.transform(AffineTransform.getTranslateInstance(x - length, y - pointMargin + (width - lipDistanceV) / 2));
Area lip2 = new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
Area lip =
new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip.subtract(new Area(
new Ellipse2D.Double(-lipRadius / 2 + lipHoleSpacing, -lipRadius / 4 + lipHoleSize / 2,
lipHoleSize, lipHoleSize)));
lip.transform(AffineTransform.getTranslateInstance(x - length,
y - pointMargin + (width - lipDistanceV) / 2));
Area lip2 =
new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip2 = new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip2.subtract(new Area(new Ellipse2D.Double(lipRadius / 2 - lipHoleSize - lipHoleSpacing, -lipRadius / 4 + lipHoleSize / 2, lipHoleSize, lipHoleSize)));
lip2.subtract(new Area(new Ellipse2D.Double(lipRadius / 2 - lipHoleSize - lipHoleSpacing,
-lipRadius / 4 + lipHoleSize / 2, lipHoleSize, lipHoleSize)));
lip2.transform(AffineTransform.getTranslateInstance(x, y - pointMargin + width / 2));
lip.add(lip2);
Area lip3 = new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip3.subtract(new Area(new Ellipse2D.Double(-lipRadius / 2 + lipHoleSpacing, -lipRadius / 4 + lipHoleSize / 2, lipHoleSize, lipHoleSize)));
lip3.transform(AffineTransform.getTranslateInstance(x - length, (y - pointMargin) + (width - ((width - lipDistanceV) / 2))));
Area lip3 =
new Area(new Ellipse2D.Double(-lipRadius / 2, -lipRadius / 2, lipRadius, lipRadius));
lip3.subtract(new Area(
new Ellipse2D.Double(-lipRadius / 2 + lipHoleSpacing, -lipRadius / 4 + lipHoleSize / 2,
lipHoleSize, lipHoleSize)));
lip3.transform(AffineTransform.getTranslateInstance(x - length,
(y - pointMargin) + (width - ((width - lipDistanceV) / 2))));
lip.add(lip3);
body[1] = new Area(lip);
// Merge
((Area)body[1]).subtract((Area)body[0]);
// ??? - Perhaps Overall Area - it's never filled in draw()
body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize));
((Area) body[1]).subtract((Area) body[0]);
// Poles
int poleSize = (int) POLE_SIZE.convertToPixels();
int poleSpacing = (int) POLE_SPACING_HORIZONTAL.convertToPixels();
int poleSize = (int) POLE_SIZE.convertToPixels();
int poleSpacing = (int) POLE_SPACING_HORIZONTAL.convertToPixels();
int poleSpacingEdge = (int) POLE_SPACING_TO_EDGE.convertToPixels();
int poleMargin = (length - poleSpacing * 3) / 2; // Edge to centre of outer poles
int poleMargin = (length - poleSpacing * 3) / 2; // Edge to centre of outer poles
Area poleArea = new Area();
for (int i = 0; i < 4; i++) {
// Top Row
Ellipse2D pole =
new Ellipse2D.Double(x - length + poleMargin + i * poleSpacing - poleSize / 2,
y + poleSpacingEdge - pointMargin - poleSize / 2 ,
poleSize, poleSize);
new Ellipse2D.Double(x - length + poleMargin + i * poleSpacing - poleSize / 2,
y + poleSpacingEdge - pointMargin - poleSize / 2, poleSize, poleSize);
// Bottom Row
Ellipse2D pole2 =
new Ellipse2D.Double(x - length + poleMargin + i * poleSpacing - poleSize / 2,
Ellipse2D pole2 =
new Ellipse2D.Double(x - length + poleMargin + i * poleSpacing - poleSize / 2,
y - pointMargin - poleSize / 2 + (width - poleSpacingEdge), poleSize, poleSize);
poleArea.add(new Area(pole));
poleArea.add(new Area(pole2));
}
body[3] = poleArea;
// Rotate if needed
if (orientation != Orientation.DEFAULT) {
// Set variables depending on required transform
@@ -250,7 +230,7 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
} // end the if (body == null)
return body;
}
@Override
protected int getMainLabelYOffset() {
return (int) (WIDTH.convertToPixels() / 2 - 20);
@@ -262,23 +242,28 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
int bodyWidth = (int) (14f * width / 32);
int bodyLength = (int) (26f * width / 32);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
g2d.setColor(BODY_COLOR);
g2d.fillRoundRect((width - bodyWidth) / 2, (height - bodyLength) / 2, bodyWidth, bodyLength, 3, 3);
g2d.fillRoundRect((width - bodyWidth) / 2, (height - bodyLength) / 2, bodyWidth, bodyLength, 3,
3);
// Very neat solution, doesn't work for this :(
// Could potentially make them shorter so they don't poke out both ends and then do three of them?
int lipSize = (int) (4f * width / 32);
// g2d.fillRoundRect((width - lipSize) / 2, (height - bodyLength) / 2 - lipSize + 1, lipSize, bodyLength + 2 * lipSize - 2, lipSize, lipSize);
g2d.fillRoundRect((width - lipSize) / 2, (height - bodyLength) / 2 - lipSize + 1, lipSize, lipSize, lipSize, lipSize); // Right, center
g2d.fillRoundRect(9 * width / 32, bodyLength + ((height - bodyLength) / 2), lipSize, lipSize, lipSize, lipSize);
g2d.fillRoundRect(18 * width / 32, bodyLength + ((height - bodyLength) / 2), lipSize, lipSize, lipSize, lipSize);
g2d.fillRoundRect((width - lipSize) / 2, (height - bodyLength) / 2 - lipSize + 1, lipSize,
lipSize, lipSize, lipSize); // Right, center
g2d.fillRoundRect(9 * width / 32, bodyLength + ((height - bodyLength) / 2), lipSize, lipSize,
lipSize, lipSize);
g2d.fillRoundRect(18 * width / 32, bodyLength + ((height - bodyLength) / 2), lipSize, lipSize,
lipSize, lipSize);
//g2d.fillRoundRect((width - lipSize) / 2, 12 * width / 32, lipSize, lipSize,lipSize,lipSize);
//g2d.fillRoundRect((width - lipSize) / 2, 20 * width / 32, lipSize, lipSize,lipSize,lipSize);
g2d.setColor(BODY_COLOR.darker());
g2d.drawRoundRect((width - bodyWidth) / 2, (height - bodyLength) / 2, bodyWidth, bodyLength, 3, 3);
g2d.drawRoundRect((width - bodyWidth) / 2, (height - bodyLength) / 2, bodyWidth, bodyLength, 3,
3);
// g2d.setColor(Color.gray);
// g2d.drawLine(width / 2, 4 * width / 32, width / 2, 4 * width / 32);
@@ -289,15 +274,17 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
int poleSpacing = (int) (6d * width / 32);
for (int i = 0; i < 4; i++) {
// Top Row
g2d.fillOval(11 * width / 32, 5 * width / 32 + i * poleSpacing, poleSize * width / 32, poleSize * width / 32);
g2d.fillOval(11 * width / 32, 5 * width / 32 + i * poleSpacing, poleSize * width / 32,
poleSize * width / 32);
//g2d.fillOval((width - poleSize) / 2, (height - poleSpacing) / 2 + (i * poleSpacing / 3), poleSize, poleSize);
// Bottom Row
g2d.fillOval(17 * width / 32, 5 * width / 32 + i * poleSpacing, poleSize * width / 32, poleSize * width / 32);
g2d.fillOval(17 * width / 32, 5 * width / 32 + i * poleSpacing, poleSize * width / 32,
poleSize * width / 32);
}
}
@Override
protected OrientationHV getControlPointDirection() {
protected OrientationHV getControlPointDirection() {
return OrientationHV.VERTICAL;
}
@@ -309,7 +296,7 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
public void setColor(Color color) {
this.color = color;
}
@EditableProperty(name = "Pole Color")
public Color getPoleColor() {
if (poleColor == null)
@@ -320,7 +307,7 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
public void setPoleColor(Color poleColor) {
this.poleColor = poleColor;
}
@Override
public boolean isControlPointSticky(int index) {
return true;
@@ -330,10 +317,10 @@ public class StingrayBassPickup extends AbstractGuitarPickup {
public boolean isHumbucker() {
return true;
}
@Override
public Polarity getPolarity() {
// just to disable editor
return super.getPolarity();
}
}
}
@@ -234,6 +234,7 @@ public class Image extends AbstractTransparentComponent<Void> {
public void setData(byte[] data) {
this.data = data;
this.imageIcon = null;
this.image = null;
if (getSizingMode() == ImageSizingMode.TwoPoints) {
ImageIcon imageIcon = getImage();
@@ -121,7 +121,7 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
if (body == null) {
// mandatory: 0, 1, 2 pins, 3 body, 9 wafer
// optional: 4, 5, 6 lugs, 7 nut, 8 shaft
body = new Area[10];
body = new Area[13];
// Add lugs.
int pinWidth = (int) PIN_SIZE.convertToPixels();
@@ -210,6 +210,7 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
new Area(new Ellipse2D.Double(controlPoints[i].getX() - lugDiameter / 2, controlPoints[i].getY() - lugDiameter / 2,
lugDiameter, lugDiameter));
body[4 + i] = area;
body[10 + i] = new Area(area);
}
for (int i = 0; i < 3; i++) {
@@ -272,6 +273,11 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
case 9:
g2d.setColor(getWaferColor());
break;
case 10:
case 11:
case 12:
g2d.setColor(Constants.TRANSPARENT_COLOR);
break;
default:
g2d.setColor(getBodyColor());
}
@@ -281,11 +287,11 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
if (!outlineMode) {
// if (i >= 3 && i <= 6)
// drawingObserver.startTrackingContinuityArea(true);
if (i >= 10)
drawingObserver.startTrackingContinuityArea(true);
g2d.fill(shape);
// if (i >= 3 && i <= 6)
// drawingObserver.stopTrackingContinuityArea();
if (i >= 10)
drawingObserver.stopTrackingContinuityArea();
}
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -302,6 +308,9 @@ public class PotentiometerPanel extends AbstractPotentiometer implements ILayere
g2d.draw(shape);
}
}
drawingObserver.stopTracking();
// Draw caption.
g2d.setFont(project.getFont());
@@ -103,6 +103,12 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
dx2 = 0;
dy2 = 2 * spacing;
break;
case FLAT_XSMALL2:
dx1 = spacing;
dy1 = spacing;
dx2 = 0;
dy2 = spacing;
break;
case FLAT_SMALL2:
dx1 = 4 * spacing;
dy1 = spacing;
@@ -150,6 +156,12 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
dx2 = -2 * spacing;
dy2 = 0;
break;
case FLAT_XSMALL2:
dx1 = -spacing;
dy1 = spacing;
dx2 = -spacing;
dy2 = 0;
break;
case FLAT_SMALL2:
dx1 = -spacing;
dy1 = 4 * spacing;
@@ -197,6 +209,12 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
dx2 = 0;
dy2 = -2 * spacing;
break;
case FLAT_XSMALL2:
dx1 = -spacing;
dy1 = -spacing;
dx2 = 0;
dy2 = -spacing;
break;
case FLAT_SMALL2:
dx1 = -4 * spacing;
dy1 = -spacing;
@@ -244,6 +262,12 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
dx2 = 2 * spacing;
dy2 = 0;
break;
case FLAT_XSMALL2:
dx1 = spacing;
dy1 = -spacing;
dx2 = spacing;
dy2 = 0;
break;
case FLAT_SMALL2:
dx1 = spacing;
dy1 = -4 * spacing;
@@ -309,9 +333,10 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
case FLAT_LARGE:
case FLAT_SMALL:
case FLAT_XSMALL:
case FLAT_XSMALL2:
case FLAT_XLARGE:
case FLAT_SMALL2:
if (getType() == TrimmerType.FLAT_XSMALL)
if (getType() == TrimmerType.FLAT_XSMALL || getType() == TrimmerType.FLAT_XSMALL2)
length = getClosestOdd(FLAT_SMALL_BODY_SIZE.convertToPixels());
else if (getType() == TrimmerType.FLAT_XLARGE)
length = getClosestOdd(FLAT_LARGE_BODY_SIZE.convertToPixels());
@@ -351,12 +376,35 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
return body;
}
// in all methods that deal with point index we need to flip the indices to treat 2 as 0 and 0 and 2
// this is because controlPoints in TrimmerPotentiometer are made in the opposite order than
// PotentiometerPanel and opposite of how the actual pins are ordered on a trimmer
@Override
public Point2D getControlPoint(int index) {
return super.getControlPoint(2 - index);
}
@Override
public void setControlPoint(Point2D point, int index) {
super.setControlPoint(point, index);
super.setControlPoint(point, 2 - index);
body = null;
}
@Override
public String getInternalLinkName(int index1, int index2) {
if (index1 < index2)
return getInternalLinkName(index2, index1);
index1 = 2 - index1;
index2 = 2 - index2;
if (index2 - index1 == 1)
return (index1 + 1) + "-" + (index2 + 1);
return null;
}
@Override
public void setOrientation(Orientation orientation) {
super.setOrientation(orientation);
@@ -375,14 +423,13 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
Shape shaftShape = getBody()[1];
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
if (mainShape != null) {
g2d.setColor(bodyColor);
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
Composite oldComposite = g2d.getComposite();
if (alpha < MAX_ALPHA) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
if (!outlineMode) {
g2d.fill(mainShape);
}
g2d.fill(mainShape);
drawingObserver.stopTracking();
if (!outlineMode && shaftShape != null) {
g2d.setColor(SHAFT_COLOR);
g2d.fill(shaftShape);
@@ -541,10 +588,16 @@ public class TrimmerPotentiometer extends AbstractPotentiometer {
return false;
}
public static enum TrimmerType {
FLAT_SMALL("Horizontal Small 1"), FLAT_SMALL2("Horizontal Small 2"), FLAT_XSMALL("Horizontal X-Small"), FLAT_LARGE(
"Horizontal Medium"), FLAT_XLARGE("Horizontal Large"), VERTICAL_INLINE("Vertical Inline"), VERTICAL_OFFSET(
"Vertical Offset 1"), VERTICAL_OFFSET_BIG_GAP("Vertical Offset 2");
public enum TrimmerType {
FLAT_SMALL("Horizontal Small 1"),
FLAT_SMALL2("Horizontal Small 2"),
FLAT_XSMALL("Horizontal X-Small 1"),
FLAT_XSMALL2("Horizontal X-Small 2"),
FLAT_LARGE("Horizontal Medium"),
FLAT_XLARGE("Horizontal Large"),
VERTICAL_INLINE("Vertical Inline"),
VERTICAL_OFFSET("Vertical Offset 1"),
VERTICAL_OFFSET_BIG_GAP("Vertical Offset 2");
String label;
@@ -315,7 +315,9 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
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);
}
@@ -325,7 +327,9 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -356,8 +360,6 @@ public class DIL_IC extends AbstractLabeledComponent<String> implements IGerberC
}
}
drawingObserver.stopTracking();
// Draw label.
g2d.setFont(project.getFont());
@@ -308,6 +308,7 @@ public class SIL_IC extends AbstractTransparentComponent<String> implements IGer
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getBodyColor());
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -161,6 +161,8 @@ public class TransistorTO1 extends AbstractTransistorPackage {
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
drawingObserver.stopTracking();
if (checkPointsClipped(g2d.getClip())) {
return;
}
@@ -184,7 +186,9 @@ public class TransistorTO1 extends AbstractTransistorPackage {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -183,6 +183,7 @@ public class TransistorTO126 extends AbstractTransistorPackage {
if (checkPointsClipped(g2d.getClip())) {
return;
}
drawingObserver.stopTracking();
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
@@ -211,7 +212,9 @@ public class TransistorTO126 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX() + leadLength - leadThickness / 2, point.getY());
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -220,7 +223,9 @@ public class TransistorTO126 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX(), point.getY() + leadLength - leadThickness / 2);
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -229,7 +234,9 @@ public class TransistorTO126 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX() - leadLength - leadThickness / 2, point.getY());
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -238,7 +245,9 @@ public class TransistorTO126 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX(), point.getY() - leadLength);
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -266,7 +275,9 @@ public class TransistorTO126 extends AbstractTransistorPackage {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(mainArea);
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
if (!outlineMode) {
@@ -210,6 +210,7 @@ public class TransistorTO220 extends AbstractTransistorPackage {
if (checkPointsClipped(g2d.getClip())) {
return;
}
drawingObserver.stopTracking();
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
@@ -238,7 +239,9 @@ public class TransistorTO220 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX() + leadLength - leadThickness / 2, point.getY());
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -247,7 +250,9 @@ public class TransistorTO220 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX(), point.getY() + leadLength - leadThickness / 2);
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -256,7 +261,9 @@ public class TransistorTO220 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX() - leadLength - leadThickness / 2, point.getY());
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -265,7 +272,9 @@ public class TransistorTO220 extends AbstractTransistorPackage {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness));
g2d.setColor(finalPinBorderColor);
line = new Line2D.Double(point.getX(), point.getY(), point.getX(), point.getY() - leadLength);
drawingObserver.startTracking();
g2d.draw(line);
drawingObserver.stopTracking();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(leadThickness - 2));
g2d.setColor(finalPinColor);
g2d.draw(line);
@@ -294,7 +303,9 @@ public class TransistorTO220 extends AbstractTransistorPackage {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
Color finalTabColor;
if (outlineMode) {
finalTabColor = theme.getOutlineColor();
@@ -231,6 +231,7 @@ public class TransistorTO3 extends AbstractLabeledComponent<String> {
if (checkPointsClipped(g2d.getClip())) {
return;
}
drawingObserver.stopTracking();
int pinSize = (int) PIN_DIAMETER.convertToPixels() / 2 * 2;
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
@@ -251,7 +252,9 @@ public class TransistorTO3 extends AbstractLabeledComponent<String> {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
@@ -187,6 +187,7 @@ public class TransistorTO92 extends AbstractTransistorPackage {
if (checkPointsClipped(g2d.getClip())) {
return;
}
drawingObserver.stopTracking();
int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
@@ -206,7 +207,9 @@ public class TransistorTO92 extends AbstractTransistorPackage {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
drawingObserver.startTracking();
g2d.fill(mainArea);
drawingObserver.stopTracking();
g2d.setComposite(oldComposite);
Color finalBorderColor;
if (outlineMode) {
@@ -40,12 +40,7 @@ import org.diylc.components.guitar.P90Pickup;
import org.diylc.components.guitar.PBassPickup;
import org.diylc.components.guitar.SingleCoilPickup;
import org.diylc.components.guitar.StingrayBassPickup;
import org.diylc.components.passive.AxialFilmCapacitor;
import org.diylc.components.passive.PotentiometerPanel;
import org.diylc.components.passive.RadialCeramicDiskCapacitor;
import org.diylc.components.passive.RadialFilmCapacitor;
import org.diylc.components.passive.RadialMicaCapacitor;
import org.diylc.components.passive.Resistor;
import org.diylc.components.passive.*;
import org.diylc.core.IDIYComponent;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistAnalyzer;
@@ -57,7 +52,7 @@ import org.diylc.netlist.TreeException;
import org.diylc.netlist.TreeLeaf;
import org.diylc.netlist.Tree.ITreeWalker;
public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements INetlistAnalyzer {
public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements INetlistAnalyzer, IGuitarDiagramAnalyzer {
private static Set<String> JACK_TYPES = new HashSet<String>();
private static Set<String> POT_TYPES = new HashSet<String>();
@@ -71,6 +66,7 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
JACK_TYPES.add(ClosedJack1_4.class.getCanonicalName());
POT_TYPES.add(PotentiometerPanel.class.getCanonicalName());
POT_TYPES.add(TrimmerPotentiometer.class.getCanonicalName());
PICKUP_TYPES.add(SingleCoilPickup.class.getCanonicalName());
PICKUP_TYPES.add(HumbuckerPickup.class.getCanonicalName());
@@ -119,6 +115,27 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
return Set.of(NetlistSwitchPreference.WITH);
}
public List<String> collectNotes(Netlist netlist) {
List<String> notes = new ArrayList<String>();
Tree tree = null;
try {
tree = constructTree(netlist);
} catch (TreeException e) {
throw new RuntimeException(e);
}
if (tree == null) {
notes.add("No pickups are connected to the output jack");
return notes;
}
processTree(tree, notes);
return notes;
}
protected Summary summarize(Netlist netlist) throws TreeException {
List<String> notes = new ArrayList<String>();
@@ -370,8 +387,10 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupMap,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupReverseMap) {
TreeLeaf leaf1 = new TreeLeaf(pot, 0, 1);
TreeLeaf leaf2 = new TreeLeaf(pot, 1, 2);
Tree tree1 = tree.locate(leaf1, false);
Tree tree2 = tree.locate(leaf2, false);
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>org.diylc</groupId>
<artifactId>diylc-parent</artifactId>
<version>5.2.0</version>
<version>5.4.0</version>
</parent>
<artifactId>diylc-swing</artifactId>
@@ -21,6 +21,7 @@ import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import javax.swing.Icon;
import org.apache.log4j.Logger;
import org.diylc.swing.actions.analyze.AiAnalyzeAction;
import org.diylc.swing.actions.analyze.HighlightConnectedAreasAction;
import org.diylc.swingframework.IDrawingProvider;
@@ -275,6 +276,10 @@ public class ActionFactory {
return new GenerateNetlistAction(plugInPort, swingUI, includeSwitches);
}
public AiAnalyzeAction createAiAnalyzeAction(IPlugInPort plugInPort, ISwingUI swingUI) {
return new AiAnalyzeAction(plugInPort, swingUI);
}
public SummarizeNetlistAction createSummarizeNetlistAction(IPlugInPort plugInPort,
ISwingUI swingUI, INetlistAnalyzer summarizer, boolean includeSwitches) {
return new SummarizeNetlistAction(plugInPort, swingUI, summarizer, includeSwitches);
@@ -0,0 +1,59 @@
package org.diylc.swing.actions.analyze;
import org.diylc.common.INetlistAnalyzer;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ITask;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.Summary;
import org.diylc.swing.ISwingUI;
import org.diylc.swingframework.TextDialog;
import org.diylc.utils.IconLoader;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.List;
public class AiAnalyzeAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private IPlugInPort plugInPort;
private ISwingUI swingUI;
public AiAnalyzeAction(IPlugInPort plugInPort, ISwingUI swingUI) {
super();
this.plugInPort = plugInPort;
this.swingUI = swingUI;
putValue(AbstractAction.NAME, "AI Circuit Analyzer");
putValue(AbstractAction.SMALL_ICON, IconLoader.Brain.getIcon());
}
@Override
public void actionPerformed(ActionEvent e) {
swingUI.executeBackgroundTask(new ITask<String>() {
@Override
public String doInBackground() throws Exception {
return plugInPort.getChatbotService().analyzeCircuit();
}
@Override
public void failed(Exception e) {
swingUI.showMessage(e.getMessage(), "AI Circuit Analyzer", ISwingUI.ERROR_MESSAGE);
}
@Override
public void complete(String res) {
if (res == null || res.isEmpty()) {
swingUI.showMessage("The generated analysis is empty, nothing to show.",
"AI Circuit Analyzer", ISwingUI.INFORMATION_MESSAGE);
return;
}
new TextDialog(swingUI.getOwnerFrame().getRootPane(), res, "AI Circuit Analyzer",
new Dimension(800, 600)).setVisible(true);
}
}, true);
}
}
@@ -10,8 +10,8 @@ import org.diylc.swingframework.TextDialog;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ITask;
import org.diylc.netlist.Group;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistHtmlReport;
import org.diylc.swing.ActionFactory;
import org.diylc.swing.ISwingUI;
import org.diylc.utils.IconLoader;
@@ -62,23 +62,9 @@ public class GenerateNetlistAction extends AbstractAction {
ISwingUI.INFORMATION_MESSAGE);
return;
}
StringBuilder sb = new StringBuilder("<html>");
for (Netlist netlist : res) {
sb.append("<p style=\"font-family: " + new JLabel().getFont().getName()
+ "; font-size: 9px\">");
if (includeSwitches) {
sb.append("<b>Switch configuration: ").append(netlist.getSwitchSetup())
.append("</b><br><br>");
}
sb.append("Connected node groups:<br><br>");
for (Group v : netlist.getSortedGroups()) {
sb.append("&nbsp;&nbsp;").append(v.getSortedNodes()).append("<br>");
}
sb.append("</p><br><hr>");
}
sb.append("</html>");
new TextDialog(swingUI.getOwnerFrame().getRootPane(), sb.toString(), "DIYLC Netlist",
String html = NetlistHtmlReport.generateHtml(res, new JLabel().getFont().getName());
new TextDialog(swingUI.getOwnerFrame().getRootPane(), html, "DIYLC Netlist",
new Dimension(800, 600)).setVisible(true);
}
}, true);
@@ -1,3 +1,24 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 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.swing.actions.analyze;
import java.awt.Dimension;
@@ -12,6 +33,7 @@ import org.diylc.common.IPlugInPort;
import org.diylc.common.ITask;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.Summary;
import org.diylc.netlist.NetlistSummaryHtmlReport;
import org.diylc.swing.ISwingUI;
import org.diylc.utils.IconLoader;
@@ -68,25 +90,9 @@ public class SummarizeNetlistAction extends AbstractAction {
summarizer.getName(), ISwingUI.INFORMATION_MESSAGE);
return;
}
StringBuilder sb = new StringBuilder("<html>");
for (Summary summary : res) {
sb.append("<p style=\"font-family: ").append(summarizer.getFontName())
.append("; font-size: 9px\">");
if (res.size() > 1)
sb.append("<b>Switch configuration: ").append(summary.getNetlist().getSwitchSetup())
.append("</b><br><br>");
sb.append(summary.getSummary());
sb.append("</p><br>");
if (res.size() > 1)
sb.append("<hr>");
}
sb.append("</html>");
new TextDialog(swingUI.getOwnerFrame().getRootPane(), sb.toString(), summarizer.getName(),
String html = NetlistSummaryHtmlReport.generateHtml(res, summarizer.getFontName());
new TextDialog(swingUI.getOwnerFrame().getRootPane(), html, summarizer.getName(),
new Dimension(800, 600)).setVisible(true);
}
}, true);
@@ -66,6 +66,7 @@ public class AnalyzeMenuPlugin implements IPlugIn {
List<Action> menuActions = new ArrayList<>();
// Add all menu actions to the list
menuActions.add(actionFactory.createAiAnalyzeAction(plugInPort, swingUI));
menuActions.add(actionFactory.createCheckProximityAction(plugInPort, swingUI));
menuActions.add(actionFactory.createBomAction(plugInPort));
menuActions.add(actionFactory.createGenerateNetlistAction(plugInPort, swingUI, false));
@@ -16,6 +16,8 @@ import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Objects;
import java.util.Random;
@@ -34,6 +36,10 @@ public class ChatbotPane extends JPanel {
private static final Font MONOSPACED_FONT = new Font("Monospaced", Font.PLAIN, 12);
private static final Color TERMINAL_BG = new Color(40, 40, 40);
private static final Color TERMINAL_FG = new Color(200, 200, 200);
private static final Color BUTTON_BG = new Color(70, 70, 70); // Brighter background for buttons
private static final Color BUTTON_FG = new Color(240, 240, 240); // Bright text for contrast
private static final Color BUTTON_BORDER = new Color(100, 100, 100); // Subtle border
private static final Color BUTTON_HOVER = new Color(90, 90, 90); // Hover state color
public static final String HTML_STYLE = String.format(
"<style>" + "body { font-family: %s; font-size: %dpt; color: rgb(%d,%d,%d); background-color: rgb(%d,%d,%d); padding: 0px; margin: 0; }" + "pre { white-space: pre-wrap; margin: 0; }" + ".user { color: #98C379; }" + // Softer green that's easier on the eyes
".assistant { color: #61AFEF; }" + // Lighter, more vibrant blue
@@ -58,6 +64,7 @@ public class ChatbotPane extends JPanel {
private boolean loggedIn = false;
private String projectFileName = null;
private SubscriptionEntity subscriptionInfo;
public ChatbotPane(ISwingUI swingUI, IPlugInPort plugInPort) {
super();
@@ -66,6 +73,18 @@ public class ChatbotPane extends JPanel {
this.chatbotService = new ChatbotService(plugInPort);
setName("AI Assistant");
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
e.consume();
}
@Override
public void mousePressed(MouseEvent e) {
e.consume();
}
});
// Set panel background color
setBackground(TERMINAL_BG);
@@ -98,12 +117,15 @@ public class ChatbotPane extends JPanel {
int lineHeight = getPromptArea().getFontMetrics(getPromptArea().getFont()).getHeight();
int desiredHeight = lineHeight * 3 + 10; // 3 lines + padding
// Set minimum and preferred size for both text area and scroll pane
Dimension promptSize = new Dimension(200, desiredHeight);
getPromptArea().setMinimumSize(promptSize);
getPromptArea().setPreferredSize(promptSize);
promptScrollPane.setMinimumSize(new Dimension(200, desiredHeight + 10));
promptScrollPane.setPreferredSize(new Dimension(200, desiredHeight + 10));
// Set minimum and preferred size for scroll pane
promptScrollPane.setMinimumSize(new Dimension(200, desiredHeight));
promptScrollPane.setPreferredSize(new Dimension(200, desiredHeight));
// Enable vertical scrolling
getPromptArea().setLineWrap(true);
getPromptArea().setWrapStyleWord(true);
getPromptArea().setRows(3); // Set initial rows
promptScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
gbc.gridx = 0;
gbc.gridy = 3;
@@ -162,20 +184,19 @@ public class ChatbotPane extends JPanel {
promptArea.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(java.awt.event.KeyEvent e) {
if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER && !e.isShiftDown()) {
e.consume(); // Prevent the newline from being added
if (getAskButton().isEnabled()) {
getAskButton().doClick();
if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) {
if (e.isShiftDown()) {
// Insert newline for Shift+Enter
promptArea.insert("\n", promptArea.getCaretPosition());
} else {
e.consume(); // Always consume Enter to prevent multiple newlines
if (getAskButton().isEnabled()) {
getAskButton().doClick();
}
}
}
}
});
// Calculate height based on font metrics
int lineHeight = promptArea.getFontMetrics(promptArea.getFont()).getHeight();
int desiredHeight = lineHeight * 3 + 10; // 3 lines + padding
promptArea.setMinimumSize(new Dimension(200, desiredHeight));
promptArea.setPreferredSize(new Dimension(200, desiredHeight));
// Add placeholder text behavior
final String placeholder = "Ask me anything";
@@ -235,6 +256,9 @@ public class ChatbotPane extends JPanel {
public void refreshChat(SubscriptionEntity subscriptionInfo) {
boolean currentLoggedIn = plugInPort.getCloudService().isLoggedIn();
String currentProjectFileName = FileUtils.extractFileName(plugInPort.getCurrentFileName());
if (subscriptionInfo != null) {
this.subscriptionInfo = subscriptionInfo;
}
if (this.loggedIn == currentLoggedIn && Objects.equals(currentProjectFileName, this.projectFileName))
return;
@@ -249,23 +273,47 @@ public class ChatbotPane extends JPanel {
getClearButton().setEnabled(false);
getPremiumButton().setVisible(false);
} else {
if (subscriptionInfo != null) {
getPremiumButton().setVisible(FREE_TIER.equals(subscriptionInfo.getTier()));
String subscriptionInfoText = "Your are currently subscribed to the '" + subscriptionInfo.getTier() + "' tier, expiring on " + subscriptionInfo.getEndDate() + ", with " + subscriptionInfo.getRemainingCredits() + " credits remaining.";
if (FREE_TIER.equals(subscriptionInfo.getTier())) {
subscriptionInfoText +=
" To subscribe to the Premium tier, unlock advanced AI models and virtually limitless usage, visit <a href='http://" + GET_PREMIUM_URL + "'>" + GET_PREMIUM_URL + "</a>.";
swingUI.executeBackgroundTask(new ITask<Void>() {
@Override
public Void doInBackground() throws Exception {
fetchChatHistory();
return null;
}
appendSection(ChatbotService.SYSTEM, subscriptionInfoText);
}
fetchChatHistory();
getAskButton().setEnabled(true);
getClearButton().setEnabled(true);
@Override
public void failed(Exception e) {
}
@Override
public void complete(Void result) {
if (ChatbotPane.this.subscriptionInfo != null) {
getPremiumButton().setVisible(FREE_TIER.equals(ChatbotPane.this.subscriptionInfo.getTier()));
String subscriptionInfoText = "Your are currently subscribed to the '" +
ChatbotPane.this.subscriptionInfo.getTier() + "' tier, expiring on " +
ChatbotPane.this.subscriptionInfo.getEndDate() + ", with " +
ChatbotPane.this.subscriptionInfo.getRemainingCredits() + " credits remaining.";
if (FREE_TIER.equals(ChatbotPane.this.subscriptionInfo.getTier())) {
subscriptionInfoText +=
" To subscribe to one of the premium tiers, unlock advanced AI models and get more credits, visit <a href='http://" + GET_PREMIUM_URL + "'>" + GET_PREMIUM_URL + "</a> and become a Patreon supporter. " +
"All the details about the AI Assistant limits and other benefits will be specified there. " +
"If you are already a Patreon supporter, make sure to link your account with the DIYLC Cloud account from the 'Cloud' menu.";
} else {
subscriptionInfoText += " Thank you for supporting further development of DIYLC!";
}
appendSection(ChatbotService.SYSTEM, subscriptionInfoText);
}
getAskButton().setEnabled(true);
getClearButton().setEnabled(true);
}
}, false);
}
// Scroll to the end of the chat pane
SwingUtilities.invokeLater(() -> {
if (getChatEditorPane().getParent() == null) {
return;
}
JScrollPane scrollPane = (JScrollPane) getChatEditorPane().getParent().getParent();
JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
verticalScrollBar.setValue(verticalScrollBar.getMaximum());
@@ -346,10 +394,7 @@ public class ChatbotPane extends JPanel {
public JButton getAskButton() {
if (askButton == null) {
askButton = new JButton(LangUtil.translate("Send"));
askButton.setBackground(TERMINAL_BG);
askButton.setForeground(TERMINAL_FG);
askButton.setFocusPainted(false);
askButton.setBorderPainted(true);
styleButton(askButton);
askButton.addActionListener(e -> {
String prompt = getPromptArea().getText();
appendSection(ChatbotService.USER, ME + prompt);
@@ -407,32 +452,37 @@ public class ChatbotPane extends JPanel {
public JButton getClearButton() {
if (clearButton == null) {
clearButton = new JButton(LangUtil.translate("Clear"));
clearButton.setBackground(TERMINAL_BG);
clearButton.setForeground(TERMINAL_FG);
clearButton.setFocusPainted(false);
clearButton.setBorderPainted(true);
styleButton(clearButton);
clearButton.addActionListener(e -> {
// Show confirmation dialog
int result = swingUI.showConfirmDialog(
LangUtil.translate("Are you sure you want to clear the chat history for this project? This action cannot be undone!"),
"AI Assistant",
ISwingUI.YES_NO_OPTION,
ISwingUI.WARNING_MESSAGE);
if (result == ISwingUI.YES_OPTION) {
swingUI.executeBackgroundTask(new ITask<Void>() {
swingUI.executeBackgroundTask(new ITask<Void>() {
@Override
public Void doInBackground() throws Exception {
chatbotService.deleteChatHistory();
return null;
}
@Override
public Void doInBackground() throws Exception {
chatbotService.deleteChatHistory();
return null;
}
@Override
public void failed(Exception e) {
appendSection(ChatbotService.SYSTEM, "Failed to delete chat history from the server.");
LOG.error("Failed to delete chat history from the server", e);
getClearButton().setEnabled(true);
}
@Override
public void failed(Exception e) {
appendSection(ChatbotService.SYSTEM, "Failed to delete chat history from the server.");
LOG.error("Failed to delete chat history from the server", e);
getClearButton().setEnabled(true);
}
@Override
public void complete(Void result) {
clearChat();
}
}, false);
@Override
public void complete(Void result) {
clearChat();
}
}, false);
}
});
}
return clearButton;
@@ -441,13 +491,9 @@ public class ChatbotPane extends JPanel {
public JButton getPremiumButton() {
if (premiumButton == null) {
premiumButton = new JButton(LangUtil.translate("Get Premium"));
premiumButton.setBackground(TERMINAL_BG);
premiumButton.setForeground(TERMINAL_FG);
premiumButton.setFocusPainted(false);
premiumButton.setBorderPainted(true);
styleButton(premiumButton);
premiumButton.setVisible(false);
premiumButton.addActionListener(e -> {
try {
java.awt.Desktop.getDesktop().browse(new java.net.URI("http://diy-fever.com/get-premium"));
} catch (Exception ex) {
@@ -459,4 +505,45 @@ public class ChatbotPane extends JPanel {
}
return premiumButton;
}
private void styleButton(JButton button) {
button.setBackground(BUTTON_BG);
button.setForeground(BUTTON_FG);
button.setEnabled(true); // Reset enabled state to ensure proper color
button.setFocusPainted(false);
button.setBorderPainted(true);
button.setContentAreaFilled(false);
button.setOpaque(true);
// Create a simple rounded border with padding
button.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(BUTTON_BORDER, 1),
BorderFactory.createEmptyBorder(4, 8, 4, 8)
));
// Add hover effect
button.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
if (button.isEnabled()) {
button.setBackground(BUTTON_HOVER);
}
}
public void mouseExited(java.awt.event.MouseEvent evt) {
if (button.isEnabled()) {
button.setBackground(BUTTON_BG);
}
}
});
// Set minimum and preferred size based on button type
if (button == premiumButton) {
// Wider size for Premium button
button.setMinimumSize(new Dimension(120, 25));
button.setPreferredSize(new Dimension(120, 25));
} else {
// Standard size for other buttons
button.setMinimumSize(new Dimension(80, 25));
button.setPreferredSize(new Dimension(80, 25));
}
}
}
@@ -48,7 +48,7 @@ public class ChatbotPlugin implements IPlugIn {
@Override
public EnumSet<EventType> getSubscribedEventTypes() {
return EnumSet.of(EventType.FILE_STATUS_CHANGED, EventType.PROJECT_LOADED,
return EnumSet.of(EventType.PROJECT_SAVED, EventType.PROJECT_LOADED,
EventType.CLOUD_LOGGED_IN, EventType.CLOUD_LOGGED_OUT);
}
@@ -56,10 +56,8 @@ public class ChatbotPlugin implements IPlugIn {
@Override
public void processMessage(EventType eventType, Object... params) {
switch (eventType) {
case FILE_STATUS_CHANGED:
case PROJECT_SAVED:
case PROJECT_LOADED:
getChatbotPane().refreshChat(null);
break;
case CLOUD_LOGGED_IN:
case CLOUD_LOGGED_OUT:
SubscriptionEntity subscriptionInfo = null;
@@ -35,6 +35,8 @@ import org.diylc.utils.IconLoader;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class ConnectPatreonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@@ -58,7 +60,8 @@ public class ConnectPatreonAction extends AbstractAction {
return;
}
String username = plugInPort.getCloudService().getCurrentUsername();
String url = "http://diy-fever.com/diylc/api/v1/ai/patreon_login.php?username=" + username;
username ="a b c";
String url = "http://diy-fever.com/diylc/api/v1/ai/patreon_login.php?username=" + URLEncoder.encode(username, StandardCharsets.UTF_8);
try {
java.awt.Desktop.getDesktop().browse(new java.net.URI(url));
} catch (Exception ex) {
@@ -72,7 +72,7 @@ public enum IconLoader {
ComponentLarge("component_large.png"), ImportNetlist("import_netlist.png"), SplashIC("splash_ic.png"),
ComponentPreferences("component_preferences.png"), ComponentReplace("component_replace.png"), Barcode("barcode.png"),
DocumentGerber("document_gerber.png"), DocumentX2("document_x2.png"), Export("export.png"), Multimeter("multimeter.png"),
Patreon("patreon.png");
Patreon("patreon.png"), Brain("brain.png");
protected String name;
Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>org.diylc</groupId>
<artifactId>diylc-parent</artifactId>
<version>5.2.0</version>
<version>5.4.0</version>
<packaging>pom</packaging>
<name>DIY Layout Creator</name>
+44
View File
@@ -0,0 +1,44 @@
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Component placement';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Component placement','
When designing a circuit layout, it''s a good practice to align the component placement with the signal path—typically from left to right, though the reverse can also work depending on the context. One key consideration is to keep AC power sources as far away from the signal path as possible to minimize noise and interference. A common strategy is to position the power supply on the opposite side of the board from the input and ensure that the DC supply flows in the opposite direction of the signal.
Components are often arranged perpendicular to the signal path. This orientation can help make efficient use of space while maintaining a clear flow of the circuit.
When working with integrated circuits—like in a Tubescreamer-style overdrive pedal—the same principles apply, although it can be a bit more challenging. A useful approach is to place the IC at the center of the layout and arrange surrounding components in a “bug-like” formation, radiating outward. This helps maintain logical signal flow and simplifies connections, especially in point-to-point builds.');
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Grounding scheme';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Grounding scheme','
For both amplifiers and effects, a variation of the bus grounding scheme is often effective. The input jack should be grounded either through direct contact with the chassis or via a short ground lead to minimize noise at the entry point.
It''s a good idea to ground all stages of the amplifier in close proximity to each other, along with their corresponding filter capacitors. This approach resembles star grounding and helps reduce ground loops and hum.
When grounding control pots, it''s best to connect them to the bus near the stage they belong to. Keeping grounds local to each stage helps maintain a clean, stable ground reference throughout the circuit.');
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Lead dress';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Lead dress','
Lead routing plays a critical role in minimizing noise. Keep the following guidelines in mind:
- Shorter is better: Keep leads as short as practical to reduce the chance of picking up interference.
- Separate AC and signal paths: Maintain as much distance as possible between leads carrying AC power and those carrying audio signals.
- Use orthogonal crossings: If AC and signal leads must cross, do so at right angles to minimize crosstalk.
- Respect signal levels: Avoid routing high-level signal leads close to low-level signal paths. If necessary, use shielded wire or route the leads orthogonally to each other.
Balancing these principles often involves compromise, but following them as closely as possible leads to cleaner, quieter builds.
');
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Shielded wire';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Shielded wire','
Shielded wire can help eliminate noise and oscillations, especially in sensitive parts of a circuit. It’s most useful at the input, where low-level signals are especially vulnerable to interference. Since any noise picked up there will be amplified downstream, keeping this path clean is essential.
Generally, only one end of the shield should be grounded to avoid ground loops. However, in some cases—such as connecting a gain pot—it may be practical to ground both ends and use the shield as the ground conductor.
While shielded wire improves noise resistance, it’s harder to work with than standard wire. Teflon-coated shielded wire is a strong choice due to its durability and resistance to melting during soldering, though it''s less flexible than PVC-insulated wire.
Use shielded wire where it matters most, and route it thoughtfully—keeping input and output paths orthogonal and avoiding tight clusters with unrelated leads. This helps reduce crosstalk and keeps the layout clean and quiet.
');
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Twisted wire pair';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Twisted wire pair','
Twisting wire pairs is a simple and effective technique for reducing noise in AC-carrying leads—such as tube heater lines or high-voltage secondaries. By twisting the wires tightly, electromagnetic radiation from one lead cancels out that of the other, helping to minimize interference.
To create a clean and consistent twist, place both wires in the chuck of a hand drill and twist slowly while holding the other end about 5 inches apart. This method produces uniform results in seconds and is far more effective than twisting by hand.
For best results, twists should be tight, uniform, and run parallel to the target route. Zip wire (parallel conductor cable) is also effective for AC lines, as it naturally maintains close and consistent spacing, which also aids in noise rejection.
As a general rule, keep AC leads well separated from sensitive signal paths and ensure good grounding practices—such as grounding central tube socket pins to a chassis lug—to further reduce noise and hum.');
DELETE FROM diylc_knowledge_base WHERE section = 'Circuit Building Tips - Shielding with Components and Materials';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('Building', 'Circuit Building Tips - Shielding with Components and Materials', '
Effective shielding isn’t limited to wires—many components can serve as physical barriers to block noise. For example, transformers can act as shields when placed between noisy elements (like rectifier diodes) and sensitive circuitry. Grounding the transformer’s metal housing enhances this shielding effect.
Large electrolytic capacitors also serve a dual purpose. Since one terminal is grounded, they can be positioned between noise sources and signal paths to absorb interference.
Sheet metal, such as aluminum L-profiles, can be used to mount and simultaneously shield power transformers or tubes from other parts of the circuit. Even tube sockets may contribute—some include a central metal sleeve that can be grounded to help reduce pin-to-pin interference.
Pots also offer shielding when their bodies make contact with a grounded chassis. If using a painted enclosure, ensure bare metal contact by stripping paint around pot holes. Alternatively, grounding a bus wire across the pot backs works too—but avoid mixing both methods to prevent ground loops.
Strategic placement and grounding of these components can significantly reduce noise and improve circuit performance.');
+42 -2
View File
@@ -235,7 +235,10 @@ INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('User Manua
'DIYLC AI Assistant is an integrated feature that allows users to interact with DIY Layout Creator (DIYLC) using natural language. It functions as a chatbot powered by the OpenAI API to generate helpful responses. The AI Assistant is available only while logged into the DIYLC Cloud with an active account and requires an internet connection.
There are two subscription tiers for using the AI Assistant:
- Free Tier: Provides limited monthly usage and access to basic AI models. This option is automatically available to all registered DIYLC Cloud users.
- Premium Tier: Offers up to 1000 requests per month using more advanced AI models. The Premium subscription can be purchased at http://www.diy-fever.com/get-premium. If your email used to subscribe to the premium tier is different from the email used to login to the DIYLC Cloud, please reach out to bancika@gmail.com to manually activate your premium status.
- DIYLC Supporter tier: increases monthly credits to 50 and unlocks a slightly more advanced AI model.
- DIYLC Pro tier: offers up to 500 requests per month using more advanced AI models.
The paid subscription can be purchased by becoming a DIYLC Patreon subscriber at http://www.diy-fever.com/get-premium.
In order to activate your paid subscription, please make sure to link your DIYLC Cloud account to your Patreon account from DIYLC''s "Cloud" menu.
');
DELETE FROM diylc_knowledge_base WHERE section = 'AI Assitant/Chatbot/ChatGPT/GPT - Using the AI Assistant';
@@ -255,4 +258,41 @@ INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('User Manua
- Use simple language and avoid complex technical terms.
- Ask follow-up questions if needed.
- Use the "Clear" button to reset the conversation.
- Each chat conversation is associated with the current project and is saved in the cloud and will be automatically downloaded when that project is opened.');
- Each chat conversation is associated with the current project and is saved in the cloud and will be automatically downloaded when that project is opened.');
DELETE FROM diylc_knowledge_base WHERE section = 'User Manual - Keyboard Shortcuts';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('User Manual', 'User Manual - Keyboard Shortcuts','
- X key: quick jump to the search box in the Project Explorer pane (if visible)
- Q key: quick jump to the search box in the component toolbox tree
- ~ key: repeat previously added component
- Ctrl and drag to "unstick" a component
- Ctrl (Command on Mac) + Alt (Option on Mac) and drag to move a component with more precision, without snapping to the grid
- Ctrl (Command on Mac) + G to group components
- Ctrl (Command on Mac) + U to ungroup components
- Ctrl (Command on Mac) + H to hide the status bar
- Ctrl (Command on Mac) + L to lock the status bar
- Ctrl (Command on Mac) + P to print the current project
- Arrow keys to move components 1 grid spacing at a time
- Shift + Arrow keys to move components in 1 px increments
- Alt (Option on Mac) + Left or Right arrow key to rotate a component by 90 degrees counterclockwise or clockwise respectively
- Alt (Option on Mac) + Up or Down arrow key to move a component up or down in the Z-order
- Alt (Option on Mac) + H to mirror the selected components horizontally
- Alt (Option on Mac) + V to mirror the selected components vertically
- Ctrl (Command on Mac) + number keys 1 through 6 to toggle the visibility of layers from Chassis, Board, Trace, Component, Wiring, Text
- ');
DELETE FROM diylc_knowledge_base WHERE section = 'User Manual - Creating a New Component';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('User Manual', 'User Manual - Creating a New Component','
There are several ways to create a new component:
- if it can be composed of existing components, you can combine multiple components together in a "building block" from the context menu and then easily recall it from the component tree
- for more complex components that require custom appearance and behavior, you can create a new component from scratch using the Component API, as described in the [Component API](https://github.com/bancika/diy-layout-creator/blob/wiki/ComponentAPI.md) documentation
- you can file a component request on the [DIYLC GitHub repository](https://github.com/bancika/diy-layout-creator/issues) and I will implement it if I think it would be useful for other users without guaranteeing if and when it will be implemented
- you can reach out to me at [bancika@gmail.com](mailto:bancika@gmail.com) to sponsor a custom component for your project and expedite its development');
DELETE FROM diylc_knowledge_base WHERE section = 'User Manual - Panning/Moving the Screen/Canvas';
INSERT INTO diylc_knowledge_base(category, section, content) VALUES ('User Manual', 'Panning/Moving the Screen/Canvas','
There are several ways you can pan the screen/canvas and move around:
- horizontal and vertical scroll bars
- touch pad (if available)
- mouse scroll button (if available) - hold ctrl for horizontal scrolling
- click on the tiny "Auto-scroll" button in the bottom-right corner of the canvas to open a miniature version of the canvas and quickly jump to any part of it');