Compare commits

...

37 Commits

Author SHA1 Message Date
Bane Stojkovic d166c58161 Copper keeps its 'Layer' property in the editor, the term PCB tools use for copper, while still counting as a Board Side component in code 2026-11-11 21:40:33 +01:00
Bane Stojkovic 144c19b44b Changelog entry for PCB text moving to the bottom page of the silkscreen print-out 2026-11-11 21:19:55 +01:00
Bane Stojkovic aef300d902 Traces, curved traces, ground fills, solder pads and PCB text call their layer 'Board Side', like every other part, and count as Board Side components in code; the saved field is still 'layer' 2026-11-11 21:15:26 +01:00
Bane Stojkovic 7ae773ab77 A part's Gerber silkscreen layer follows its Board Side in IGerberComponentSimple's default, so the exporter no longer swaps layers after the fact and GerberLayer.flip() is gone 2026-11-11 20:48:35 +01:00
Bane Stojkovic 698fba5630 Changelog: the Underside Display editing and the realistic board rendering get their own entries, and entries are ordered new features, improvements, bug fixes; CLAUDE.md records the ordering rule 2026-11-11 20:40:39 +01:00
Bane Stojkovic 34d475b18a Merge branch 'master' into copper-improvements
# Conflicts:
#	diylc/diylc-core/src/main/resources/update.xml
2026-11-11 18:39:47 +01:00
Bane Stojkovic 66d2eb9a0e Changelog entry for the 'drawCoordinates' warning fix 2026-11-11 18:39:30 +01:00
Bane Stojkovic 1fe823270e Hovering over a control point in a board's Underside Display shows its node name tooltip, as it does on the board itself 2026-11-11 18:39:29 +01:00
Bane Stojkovic 0d7e60b5a4 Opening an older file with a board no longer warns about an unknown 'drawCoordinates' property 2026-11-11 18:33:20 +01:00
Bane Stojkovic 13b31e6dd5 Review fixes: a drag in the Underside Display picks the part a click would, the part being placed there is drawn like the parts already there, a bottom Dot no longer adds an empty silkscreen page; one helper decides a part's board side and copper layers share an interface 2026-11-11 17:32:46 +01:00
Bane Stojkovic 5ef6952ef4 The AI component catalog no longer offers the deprecated inner layers, and lists the new 'Board Side' property 2026-11-11 17:05:44 +01:00
Bane Stojkovic 19c78e5fa9 Parts dragged from the component tree or toolbox into a board's Underside Display go on the bottom of the board 2026-11-11 17:02:06 +01:00
Bane Stojkovic 7f8c790936 A 'Dot' used as a solder joint can be put on the bottom of the board with the 'Board Side' property 2026-11-11 16:57:35 +01:00
Bane Stojkovic 27b32b0b3c Changelog entries for the faded copper view, the true underside view and the hidden inner layers 2026-11-11 16:39:49 +01:00
Bane Stojkovic eeca9f112d New parts can be placed in a board's Underside Display; a part placed there goes on the bottom of the board 2026-11-11 15:46:29 +01:00
Bane Stojkovic 122a91a6f0 The silkscreen print-out has a separate page for parts placed on the bottom of the board 2026-11-11 15:37:27 +01:00
Bane Stojkovic 246763419c All PCB parts with a top silkscreen, including tube sockets, rotary switches, relays and transformers, have the 'Board Side' property 2026-11-11 15:34:40 +01:00
Bane Stojkovic 9721307586 Bottom-side plan: the board side is a plain property and parts are not mirrored for the user 2026-11-11 15:31:32 +01:00
Bane Stojkovic 9765f04e3c Parts placed on the bottom of the board are drawn faded on the canvas and solid in the Underside Display mirror 2026-11-11 15:20:06 +01:00
Bane Stojkovic df873df04a Resistors, capacitors, diodes, ICs, transistors and other PCB parts can be placed on the bottom of the board with the new 'Board Side' property; Gerber export puts their silkscreen on the bottom layer with the text mirrored 2026-11-11 15:15:19 +01:00
Bane Stojkovic 16f54fa479 'Solder Pad' on the top layer put its solder mask opening on the bottom layer in Gerber export 2026-11-11 15:15:10 +01:00
Bane Stojkovic b76d315f44 Components can be dragged and their points stretched through the Underside Display mirror of a board 2026-11-11 15:05:28 +01:00
Bane Stojkovic 90e10f4597 Components can be selected by clicking them in the Underside Display mirror of a board 2026-11-11 14:56:02 +01:00
Bane Stojkovic 3506c97403 A component can report which side of the board it is mounted on, and Gerber export puts the silkscreen of a part mounted underneath on the bottom layer 2026-11-11 14:48:57 +01:00
Bane Stojkovic 324cb20634 Gerber export decides each component's layer in one place, and Gerber layers can be mapped to the other side of the board 2026-11-11 14:46:02 +01:00
Bane Stojkovic 39c7743c30 Regenerated regression reference images for the far-side copper fade 2026-11-11 14:25:25 +01:00
Bane Stojkovic 0c26a71174 Merge branch 'master' into copper-improvements
# Conflicts:
#	diylc/diylc-core/src/test/java/org/diylc/presenter/DrawingManagerTest.java
2026-11-11 10:26:39 +01:00
Bane Stojkovic 2e6354883f Updated the regression-test skill: the suite has just under 100 projects, the classpath is built in the same reactor run as the compile, and the expected version is the newest one in update.xml 2026-11-11 10:21:58 +01:00
Bane Stojkovic a6329c45bd 'Highlight Connected Areas', netlists and AI analysis could ignore trace cuts after the board under them was moved or edited, or when the cuts were scrolled out of view, and showed the whole strip as connected 2026-11-11 10:21:44 +01:00
Bane Stojkovic f73574641c phase 1 2026-11-10 22:50:16 +01:00
Bane Stojkovic 9f6124f719 rojects with a resistor, capacitor or other leaded component whose Width or Length had been cleared in the property editor could not be opened; such values now fall back to the default size, and Width and Length can no longer be left empty 2026-11-10 16:58:23 +01:00
Bane Stojkovic 47d67d6311 Update ValueNotationBinder.java 2026-11-10 11:18:08 +01:00
Bane Stojkovic 025fcab11b add inductance and voltage 2026-11-10 10:40:34 +01:00
Bane Stojkovic b2f99c7022 Added 'Value Notation' to the Config menu 2026-11-09 21:44:17 +01:00
Bane Stojkovic bff9e05843 Rotated labels in pasted components and building blocks were drawn in the wrong place 2026-11-07 20:14:42 +01:00
Bane Stojkovic b245396845 Delete 6.10.0-patreon.md 2026-11-06 18:24:10 +01:00
Bane Stojkovic eea7d45cc9 ai notes 2026-11-06 10:51:34 +01:00
204 changed files with 5106 additions and 520 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 KiB

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 KiB

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 775 KiB

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 KiB

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 805 KiB

After

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 KiB

After

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

After

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 MiB

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 KiB

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 KiB

After

Width:  |  Height:  |  Size: 862 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 KiB

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 155 KiB

+14 -11
View File
@@ -1,6 +1,6 @@
---
name: regression-test
description: Run the DIYLC end-to-end regression suite, which renders ~700 sample .diy projects and compares PNG exports and netlists against reference outputs. Use after any change to rendering, component drawing, serialization, netlist or analyzer logic, or when asked to check for regressions or regenerate reference files.
description: Run the DIYLC end-to-end regression suite, which renders just under 100 sample .diy projects and compares PNG exports and netlists against reference outputs. Use after any change to rendering, component drawing, serialization, netlist or analyzer logic, or when asked to check for regressions or regenerate reference files.
---
# Running the DIYLC regression suite
@@ -29,16 +29,18 @@ already has the arguments and JVM flags. Offer that first.
The runner needs the `--add-opens` / `--add-exports` flags and a classpath spanning all three
modules; `mvn exec:java` will not work because it runs in Maven's own classloader and fails to find
Reflections. Build a classpath and fork a JVM:
Reflections. Build a classpath and fork a JVM.
The classpath has to be built in the same reactor run as the compile. Running
`dependency:build-classpath` on a single module fails with a `DependencyResolutionException`,
because `diylc-processor` is not installed in the local repository and the plugin can only resolve
it from the reactor. The relative `outputFile` gives each module its own `target/cp.txt`:
```bash
cd diylc
SP=/tmp # or the session scratchpad
mvn -q -pl diylc-swing -am test-compile
mvn -q -pl diylc-swing dependency:build-classpath -Dmdep.outputFile=$SP/cp-swing.txt -Dmdep.includeScope=test
mvn -q -pl diylc-core dependency:build-classpath -Dmdep.outputFile=$SP/cp-core.txt -Dmdep.includeScope=test
mvn -q -pl diylc-library dependency:build-classpath -Dmdep.outputFile=$SP/cp-lib.txt -Dmdep.includeScope=test
mvn -q -pl diylc-swing -am test-compile dependency:build-classpath \
-Dmdep.outputFile=target/cp.txt -Dmdep.includeScope=test
java \
--add-exports java.desktop/com.apple.eawt.event=ALL-UNNAMED \
@@ -49,19 +51,20 @@ java \
--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 \
-cp "diylc-swing/target/test-classes:diylc-swing/target/classes:diylc-core/target/classes:diylc-library/target/classes:$(cat $SP/cp-swing.txt):$(cat $SP/cp-core.txt):$(cat $SP/cp-lib.txt)" \
-cp "diylc-swing/target/test-classes:diylc-swing/target/classes:diylc-core/target/classes:diylc-library/target/classes:$(cat diylc-swing/target/cp.txt):$(cat diylc-core/target/cp.txt):$(cat diylc-library/target/cp.txt)" \
org.diylc.RegressionTestRunner \
"$(cd .. && pwd)/diylc-regression-data" TEST [filter]
```
Sanity-check the log line `Presenter - Current DIYLC version: 6.4.0` near the end. If it reports
`5.0.0`, the `--add-opens` flags are missing and the run is invalid.
Sanity-check the log line `Presenter - Current DIYLC version: ...`. It should report the newest
version in `diylc-core/src/main/resources/update.xml`; if it reports `5.0.0`, the `--add-opens`
flags are missing and the run is invalid.
### Arguments
1. Path to `diylc-regression-data`
2. `TEST` or `PREPARE`
3. Optional filter — a substring matched against file paths. **Use it.** The full suite is ~700
3. Optional filter — a substring matched against file paths. The full suite is just under 100
projects; while narrowing down an issue, run one file or one directory at a time
(e.g. `splitter2`, or `guitar`).
+8
View File
@@ -210,6 +210,14 @@ logic, netlist and analyzer behaviour, datasheet parsing, core utilities. Compon
user interaction are covered by the regression suite instead, not by unit tests. Test classes are
named `*Test` or `*Tests` and carry the license header like any other file.
## Changelog
User-facing changes are listed in `diylc-core/src/main/resources/update.xml`, one `Version` block
per release. Within a block, all `NEW_FEATURE` entries come first, then `IMPROVEMENT`, then
`BUG_FIX`; keep the existing order inside each group and add a new entry at the end of its own
group, not at the end of the block. After a merge that brings entries in from both sides, sort the
unreleased block again. Released blocks stay exactly as they shipped.
## Git
**Do not commit or push.** Make the change in the working tree and stop; the maintainer reviews and
+234 -8
View File
@@ -1,5 +1,5 @@
{
"version" : "6.10.0",
"version" : "6.11.0",
"components" : [ {
"name" : "Blank Board",
"className" : "boards.BlankBoard",
@@ -7,7 +7,7 @@
"namePrefix" : "Board",
"description" : "Blank circuit board",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "Use 'Explicit' value for 'Dimension Mode' when placing a new board. The single control point places the top-left corner and 'Explicit Length' and 'Explicit Width' properties determine the dimensions", "Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'", "Use this board as a base of PCB, eyelet and turret boards unless asked otherwise" ],
"notes" : [ "Use 'Explicit' value for 'Dimension Mode' when placing a new board. The single control point places the top-left corner and 'Explicit Length' and 'Explicit Width' properties determine the dimensions", "Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'", "Use this board as a base of PCB, eyelet and turret boards unless asked otherwise", "When the board needs mounting holes, because the request asks for them or the board goes into an enclosure, also select Drill Hole and use one Drill Hole per hole" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -1035,7 +1035,7 @@
}, {
"name" : "Layer",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8" ],
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_1"
}, {
"name" : "Name",
@@ -1077,7 +1077,7 @@
}, {
"name" : "Layer",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8" ],
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_1"
}, {
"name" : "Name",
@@ -1196,6 +1196,11 @@
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "A junction marker for schematics and wiring diagrams; it is drawing only and connects nothing, since ends that meet at the same point are already connected. For a pad on a board use Solder Pad" ],
"properties" : [ {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Color",
"type" : "Color",
"format" : "hex",
@@ -1277,7 +1282,7 @@
}, {
"name" : "Layer",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8" ],
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_1"
}, {
"name" : "Name",
@@ -1459,6 +1464,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -1706,7 +1716,7 @@
}, {
"name" : "Layer",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8" ],
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_1"
}, {
"name" : "Node Name",
@@ -7155,6 +7165,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Font Size Override",
"type" : "Integer"
@@ -7228,6 +7243,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Font Size Override",
"type" : "Integer"
@@ -7306,6 +7326,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Font Size Override",
"type" : "Integer"
@@ -7746,6 +7771,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Display",
"type" : "Display",
@@ -7851,6 +7881,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -8509,6 +8544,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Name",
"type" : "String"
@@ -8577,6 +8617,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Font Size Override",
"type" : "Integer"
@@ -8645,6 +8690,11 @@
}, {
"name" : "Angle Offset",
"type" : "Angle"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Color",
"type" : "Color",
@@ -9035,6 +9085,11 @@
}, {
"name" : "Angle",
"type" : "Angle"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Color",
"type" : "Color",
@@ -9372,6 +9427,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -9496,6 +9556,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -11466,6 +11531,7 @@
"namePrefix" : "Hole",
"description" : "Drill hole marker for PCB, chassis or faceplate",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "Use for round holes: the mounting holes of a Blank Board, and the holes for jacks, pots, switches and LEDs in an Enclosure; for other openings in an Enclosure use the Rectangular, Elliptical or Polygonal Cutout", "The single control point is the centre of the hole and 'Diameter' is the drilled size, e.g. 3.2mm for an M3 mounting screw. Place mounting holes inside the board, typically one near each corner and clear of the components and traces", "A hole is not conductive and has no terminals, so nothing connects to it; on a PCB it is exported as a non-plated drill" ],
"properties" : [ {
"name" : "Alpha",
"type" : "Percentage",
@@ -11526,7 +11592,7 @@
"namePrefix" : "ENC",
"description" : "Top panel of an enclosure or chassis, optionally with its sides unfolded",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "The two control points are opposite corners of the enclosure's top panel. With 'Height' above zero the sides are unfolded around it as an exploded view, each as tall as 'Height' and set 'Spacing' apart from it: the front against the top panel's upper edge, the back against its lower edge and, when 'Sides' is All, the left and right sides against its left and right edges. The bottom is never drawn. With 'Sides' set to None, or 'Height' at zero, it is a single flat panel of any size, such as one side of a chassis drawn on its own", "The enclosure is not conductive in the netlist: jacks, pots and lugs drawn on it are not joined through it, so chassis ground points must be connected explicitly", "A datasheet model is a Hammond enclosure part number such as 1590BB, 1550B or 1444-32, or a generic size such as 125B, and is stored as 'Value'; it sets the top panel to the enclosure's length and width, upright for pedal-sized die-cast boxes up to 125 mm long and horizontal for bigger boxes and amplifier chassis and 'Height' to its wall height. 125B is a generic size sold by several makers; its Hammond equivalent is 1590N1" ],
"notes" : [ "One Enclosure draws a whole box, so use one per box rather than one per side. Also select Drill Hole for the round holes of its jacks, pots, switches and LEDs, and the Rectangular, Elliptical or Polygonal Cutout for any other openings", "Draw each hole and cutout on the panel it goes through, inside that panel's outline", "The two control points are opposite corners of the enclosure's top panel. With 'Height' above zero the sides are unfolded around it as an exploded view, each as tall as 'Height' and set 'Spacing' apart from it: the front against the top panel's upper edge, the back against its lower edge and, when 'Sides' is All, the left and right sides against its left and right edges. The bottom is never drawn. With 'Sides' set to None, or 'Height' at zero, it is a single flat panel of any size, such as one side of a chassis drawn on its own", "The enclosure is not conductive in the netlist: jacks, pots and lugs drawn on it are not joined through it, so chassis ground points must be connected explicitly", "A datasheet model is a Hammond enclosure part number such as 1590BB, 1550B or 1444-32, or a generic size such as 125B, and is stored as 'Value'; it sets the top panel to the enclosure's length and width, upright for pedal-sized die-cast boxes up to 125 mm long and horizontal for bigger boxes and amplifier chassis and 'Height' to its wall height. 125B is a generic size sold by several makers; its Hammond equivalent is 1590N1" ],
"datasheetModels" : [ "1444-10", "1444-1172", "1444-12", "1444-1372", "1444-14", "1444-15", "1444-16", "1444-17132", "1444-18", "1444-22", "1444-26", "1444-29", "1444-30", "1444-45351", "1444-542", "1444-6", "1444-6415", "1444-772", "1444-8", "1444-84515", "1444-862", "1444-9", "1444-9552", "1444-972", "1444-1053", "1444-10825", "1444-12103", "1444-12123", "1444-1273", "1444-12825", "1444-14103", "1444-1463", "1444-1573", "1444-1593", "1444-17123", "1444-17133", "1444-17143", "1444-17173", "1444-1763", "1444-1773", "1444-1783", "1444-20", "1444-24", "1444-28", "1444-32", "1444-643", "1444-753", "1444-95525", "1444-9553", "1444-17105", "1444-17134", "1444-17135", "1444-17154", "1444-17156", "1444-17174", "1444-33", "1441-10", "1441-12", "1441-14", "1441-15", "1441-16", "1441-18", "1441-20", "1441-22", "1441-24", "1441-26", "1441-28", "1441-29", "1441-30", "1441-32", "1441-33", "1441-34", "1441-36", "1441-38", "1441-40", "1441-42", "1441-6", "1441-8", "1441-9", "1550A", "1550B", "1550C", "1550D", "1550E", "1550F", "1550G", "1550H", "1550J", "1550K", "1550L", "1550M", "1550N", "1550P", "1550Q", "1590A", "1590B", "1590B2", "1590B3", "1590BB", "1590BB2", "1590BBS", "1590BS", "1590BX", "1590BX2", "1590C", "1590CE", "1590D", "1590DD", "1590DE", "1590E", "1590EE", "1590F", "1590G", "1590G2", "1590H", "1590J", "1590K", "1590KK", "1590LB", "1590LLB", "1590N1", "1590P1", "1590Q", "1590R1", "1590S", "1590T", "1590U", "1590V", "1590X", "1590XX", "1590Y", "125B" ],
"properties" : [ {
"name" : "Alpha",
@@ -11841,6 +11907,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -11969,7 +12040,7 @@
}, {
"name" : "Layer",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8" ],
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_1"
}, {
"name" : "Orientation",
@@ -12081,6 +12152,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12169,6 +12245,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12251,6 +12332,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12366,6 +12452,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12491,6 +12582,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12588,6 +12684,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12692,6 +12793,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12803,6 +12909,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12912,6 +13023,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -12993,6 +13109,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -13082,6 +13203,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Coil",
"type" : "Color",
@@ -13221,6 +13347,11 @@
"type" : "Color",
"format" : "hex",
"defaultValue" : "#333333"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -13300,6 +13431,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -13458,6 +13594,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -13526,6 +13667,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -13620,6 +13766,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -13727,6 +13878,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -13785,6 +13941,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -13843,6 +14004,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -16500,6 +16666,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -16573,6 +16744,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -16836,6 +17012,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -16929,6 +17110,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Border",
"type" : "Color",
@@ -17016,6 +17202,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Color",
"type" : "Color",
@@ -17101,6 +17292,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -17262,6 +17458,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -17343,6 +17544,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -17778,6 +17984,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -18209,6 +18420,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -19071,6 +19287,11 @@
"type" : "Percentage",
"format" : "number without the % sign, e.g. 100",
"defaultValue" : "100"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Body",
"type" : "Color",
@@ -20010,6 +20231,11 @@
"type" : "Base",
"possibleValues" : [ "B9A", "OCTAL", "B7G", "B12C", "UX4" ],
"defaultValue" : "B9A"
}, {
"name" : "Board Side",
"type" : "PCBLayer",
"possibleValues" : [ "_1", "_2" ],
"defaultValue" : "_2"
}, {
"name" : "Color",
"type" : "Color",
+6 -4
View File
@@ -1,10 +1,10 @@
{
"version" : "6.10.0",
"version" : "6.11.0",
"categories" : {
"Boards" : [ {
"name" : "Blank Board",
"className" : "boards.BlankBoard",
"notes" : [ "Use this board as a base of PCB, eyelet and turret boards unless asked otherwise" ]
"notes" : [ "Use this board as a base of PCB, eyelet and turret boards unless asked otherwise", "When the board needs mounting holes, because the request asks for them or the board goes into an enclosure, also select Drill Hole and use one Drill Hole per hole" ]
}, {
"name" : "Breadboard",
"className" : "boards.Breadboard"
@@ -283,13 +283,15 @@
"className" : "chassis.DialScale"
}, {
"name" : "Drill Hole",
"className" : "electromechanical.DrillHole"
"className" : "electromechanical.DrillHole",
"notes" : [ "Use for round holes: the mounting holes of a Blank Board, and the holes for jacks, pots, switches and LEDs in an Enclosure; for other openings in an Enclosure use the Rectangular, Elliptical or Polygonal Cutout" ]
}, {
"name" : "Elliptical Cutout",
"className" : "chassis.EllipticalCutout"
}, {
"name" : "Enclosure",
"className" : "chassis.ChassisPanel"
"className" : "chassis.ChassisPanel",
"notes" : [ "One Enclosure draws a whole box, so use one per box rather than one per side. Also select Drill Hole for the round holes of its jacks, pots, switches and LEDs, and the Rectangular, Elliptical or Polygonal Cutout for any other openings" ]
}, {
"name" : "Polygonal Cutout",
"className" : "chassis.PolygonalCutout"
@@ -72,5 +72,9 @@ public enum DrawOption {
/**
* When applied, all colors will be converted to blackest black or whitest white
*/
MONOCHROMATIC;
MONOCHROMATIC,
/**
* Paint on the bottom side of a board is drawn at reduced opacity, as if seen through the board.
*/
FADE_FAR_SIDE;
}
@@ -90,6 +90,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String FONT_SCALE_FACTOR_KEY = "fontScaleFactor";
public static final String SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY = "showResizeDimensionsTooltip";
public static final String AUTO_UNIT_CONVERSION_KEY = "autoUnitConversion";
public static final String VALUE_NOTATION_KEY = "valueNotation";
public static final String RULER_IN_SUBDIVISION_2 = "base of 2";
@@ -101,10 +102,16 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String SNAP_TO_COMPONENTS = "Components";
public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID;
public static final String VALUE_NOTATION_DECIMAL = "Decimal (4.7K, 0.22uF, 6.3V)";
public static final String VALUE_NOTATION_RKM = "RKM (4K7, 0u22, 6V3)";
public static final String VALUE_NOTATION_DEFAULT = VALUE_NOTATION_DECIMAL;
public static final String SHOW_NODE_NAME_TOOLTIPS_KEY = "showNodeNameTooltips";
public static final String LOCKED_ALPHA = "lockedAlpha";
public static final String FADE_FAR_SIDE_KEY = "fadeFarSide";
public static final String LANGUAGE = "language";
public static final String LANGUAGE_DEFAULT = "English";
@@ -25,7 +25,11 @@ import org.diylc.core.gerber.GerberLayer;
public enum PCBLayer {
_1("Bottom"), _2("Top"), _3("Inner 1"), _4("Inner 2"), _5("Inner 3"), _6("Inner 4"), _7("Inner 5"), _8("Inner 6");
_1("Bottom"), _2("Top"),
// DIYLC models two-sided boards only. The inner layers are kept, in order, so that files using
// them still load with the same layer ids; the editor hides them and Gerber export rejects them.
@Deprecated _3("Inner 1"), @Deprecated _4("Inner 2"), @Deprecated _5("Inner 3"),
@Deprecated _6("Inner 4"), @Deprecated _7("Inner 5"), @Deprecated _8("Inner 6");
private String label;
@@ -0,0 +1,58 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.core;
import org.diylc.common.PCBLayer;
/**
* Interface for components on one side of a board, such as a resistor, an IC or a trace. The side
* says where the part sits physically; it is separate from {@link ILayeredComponent}, which drives
* continuity, because the leads of a through-hole part connect to copper on both sides whichever
* side the part is mounted on. Copper implements it through {@link IPCBLayerComponent}, whose layer
* is its side and also drives its continuity.
*
* @author Branislav Stojkovic
*/
public interface IBoardSideComponent {
/**
* @return {@link PCBLayer#_2} for the top of the board or {@link PCBLayer#_1} for the bottom,
* never an inner layer or null
*/
PCBLayer getBoardSide();
/**
* Sets the side of the board the component is mounted on. The component is not mirrored.
*
* @param boardSide {@link PCBLayer#_2} for the top or {@link PCBLayer#_1} for the bottom
*/
void setBoardSide(PCBLayer boardSide);
/**
* @return {@link PCBLayer#_1} for a component mounted on the bottom of the board, and
* {@link PCBLayer#_2} for any other, including components that have no side
*/
static PCBLayer sideOf(IDIYComponent<?> component) {
return component instanceof IBoardSideComponent sided && sided.getBoardSide() == PCBLayer._1
? PCBLayer._1 : PCBLayer._2;
}
}
@@ -21,6 +21,8 @@
*/
package org.diylc.core;
import org.diylc.common.PCBLayer;
/**
* Interface for tracking component drawing process. Components may use it to stop or restart
* tracking. Anything drawn while tracking is stopped will not be added to the mouse hot-spot area.
@@ -65,4 +67,26 @@ public interface IDrawingObserver {
* @param marker
*/
void setContinuityMarker(String marker);
/**
* Notifies the observer that what follows is painted on the given side of the board, until
* {@link #stopDrawingSide()}. Paint that is not bracketed is on the top (component) side. The
* observer may draw paint on the side facing away from the viewer at reduced opacity, as if seen
* through the board. Inner layers count as the bottom side.
*
* @param side the layer the paint lies on
*/
default void startDrawingSide(PCBLayer side) {}
/**
* Notifies the observer that what follows is visible from both sides of the board, such as a
* drilled hole or a cut through the copper, and is never drawn faded, until
* {@link #stopDrawingSide()}.
*/
default void startDrawingBothSides() {}
/**
* Notifies the observer that paint is back on the top (component) side.
*/
default void stopDrawingSide() {}
}
@@ -0,0 +1,52 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.core;
import org.diylc.common.PCBLayer;
/**
* Interface for copper drawn on a layer of a PCB, such as a trace, pad, ground fill or PCB text.
* The layer is the copper's board side, but unlike the side of a mounted part it also decides
* which other copper the copper connects to. It keeps the name 'Layer' in the editor, which is
* what PCB tools call it for copper.
*
* @author Branislav Stojkovic
*/
public interface IPCBLayerComponent extends IBoardSideComponent {
PCBLayer getLayer();
void setLayer(PCBLayer layer);
/**
* @return the layer, with the deprecated inner layers counted as the bottom
*/
@Override
default PCBLayer getBoardSide() {
return getLayer() == PCBLayer._2 ? PCBLayer._2 : PCBLayer._1;
}
@Override
default void setBoardSide(PCBLayer boardSide) {
setLayer(boardSide);
}
}
@@ -0,0 +1,41 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.core.annotations;
import org.diylc.core.ValidationException;
/**
* Same as {@link PositiveNonZeroMeasureValidator}, but also rejects an empty value. Use it for
* measures the component cannot be drawn without.
*
* @author Branislav Stojkovic
*/
public class RequiredPositiveNonZeroMeasureValidator extends PositiveNonZeroMeasureValidator {
@Override
public void validate(Object owner, Object value) throws ValidationException {
if (value == null) {
throw new ValidationException("must not be empty.");
}
super.validate(owner, value);
}
}
@@ -16,8 +16,10 @@ import org.apache.log4j.Logger;
import com.bancika.gerberwriter.DataLayer;
import org.diylc.common.PCBLayer;
import org.diylc.core.ComponentState;
import org.diylc.core.IBoard;
import org.diylc.core.IBoardSideComponent;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IView;
import org.diylc.core.Project;
@@ -145,8 +147,12 @@ public class GerberExporter {
LOG.info("Completed export to gerber");
}
private static void drawComponent(Project currentProject, GerberG2DWrapper g2d,
IDIYComponent<?> c, boolean outlineMode) {
/**
* Draws the specified component into the Gerber layers it belongs to. This is the only place that
* decides which layer a component's output goes to.
*/
static void drawComponent(Project currentProject, GerberG2DWrapper g2d,
IDIYComponent<?> c, boolean outlineMode) {
IGerberComponent gerberComponent = (IGerberComponent) c;
g2d.startedDrawingComponent();
if (gerberComponent instanceof IGerberComponentCustom) {
@@ -156,7 +162,10 @@ public class GerberExporter {
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(),
gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative());
// text on the bottom silkscreen is read from below, so it is written mirrored
g2d.setMirrorText(IBoardSideComponent.sideOf(c) == PCBLayer._1);
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
g2d.setMirrorText(false);
g2d.stopGerberOutput();
}
}
@@ -58,6 +58,7 @@ public class GerberG2DWrapper extends Graphics2D
private double tolerance;
private Rectangle2D boardRect;
private boolean mirrorText = false;
public GerberG2DWrapper(Graphics2D graphics2d, String diylcVersion, Rectangle2D boardRect) {
super();
@@ -154,6 +155,15 @@ public class GerberG2DWrapper extends Graphics2D
return layerMap;
}
/**
* Sets whether text is written mirrored, each string about its own centre. The output is in top
* view, so text on the bottom side has to be mirrored to read correctly once the board is turned
* over.
*/
public void setMirrorText(boolean mirrorText) {
this.mirrorText = mirrorText;
}
@Override
public void draw(Shape s) {
if (trackingGerber && !getColor().equals(Constants.TRANSPARENT_COLOR)) {
@@ -181,6 +191,10 @@ public class GerberG2DWrapper extends Graphics2D
GlyphVector gv = font.createGlyphVector(graphics2d.getFontRenderContext(), str);
AffineTransform translateInstance = AffineTransform.getTranslateInstance(x, y);
if (mirrorText) {
translateInstance.translate(gv.getLogicalBounds().getWidth(), 0);
translateInstance.scale(-1, 1);
}
for (int i = 0; i < gv.getNumGlyphs(); i++) {
Shape glyphShape = gv.getGlyphOutline(i);
@@ -25,29 +25,10 @@ import com.bancika.gerberwriter.Point;
import com.bancika.gerberwriter.path.MoveTo;
import com.bancika.gerberwriter.path.Path;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.measures.SizeUnit;
public class GerberPathRenderer {
public static void drawComponent(Project currentProject, GerberG2DWrapper g2d, IDIYComponent<?> c,
boolean outlineMode) {
IGerberComponent gerberComponent = (IGerberComponent) c;
g2d.startedDrawingComponent();
if (gerberComponent instanceof IGerberComponentCustom) {
IGerberComponentCustom gerberComponentCustom = (IGerberComponentCustom) gerberComponent;
gerberComponentCustom.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d, g2d);
} else if (gerberComponent instanceof IGerberComponentSimple) {
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(),
gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative());
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
g2d.stopGerberOutput();
}
}
public static void outputPathArea(PathIterator pathIterator, DataLayer dataLayer, double d,
boolean isNegative, String function) {
List<PathWithNegative> outputPath =
@@ -1,9 +1,18 @@
package org.diylc.core.gerber;
import org.diylc.common.PCBLayer;
import org.diylc.core.IBoardSideComponent;
import org.diylc.core.IDIYComponent;
public interface IGerberComponentSimple extends IGerberComponent {
/**
* @return the silkscreen on the side of the board the component is mounted on
*/
default GerberLayer getGerberLayer() {
return GerberLayer.SilkscreenTop;
return this instanceof IDIYComponent<?> component
&& IBoardSideComponent.sideOf(component) == PCBLayer._1
? GerberLayer.SilkscreenBot : GerberLayer.SilkscreenTop;
}
default String getGerberFunction() {
@@ -41,14 +41,16 @@ public class Capacitance extends AbstractMeasure<CapacitanceUnit> {
}
public static Capacitance parseCapacitance(String value) {
value = value.replace("*", "");
for (CapacitanceUnit unit : CapacitanceUnit.values()) {
if (value.toLowerCase().endsWith(unit.toString().toLowerCase())) {
value = value.substring(0, value.length() - unit.toString().length()).trim();
return new Capacitance(parse(value), unit);
}
ValueNotation.ParsedMeasure<CapacitanceUnit> parsed;
try {
parsed = ValueNotation.parse(value, CapacitanceUnit::fromSymbol);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not parse capacitance: " + value, e);
}
throw new IllegalArgumentException("Could not parse capacitance: " + value);
if (parsed.unit() == null) {
throw new IllegalArgumentException("Could not parse capacitance: " + value);
}
return new Capacitance(parsed.value(), parsed.unit());
}
public Double getNormalizedValue() {
@@ -56,4 +58,12 @@ public class Capacitance extends AbstractMeasure<CapacitanceUnit> {
return null;
return value * unit.getFactor();
}
@Override
public String toString() {
if (ValueNotation.getCurrent() == ValueNotation.RKM && value != null && unit != null) {
return ValueNotation.formatRkm(value, unit.getRkmSymbol());
}
return super.toString();
}
}
@@ -23,14 +23,16 @@ package org.diylc.core.measures;
public enum CapacitanceUnit implements Unit {
pF(1e-6, "pF"), nF(1e-3, "nF"), uF(1, "uF"), mF(1e3, "mF"), F(1e6, "F");
pF(1e-6, "pF", "p"), nF(1e-3, "nF", "n"), uF(1, "uF", "u"), mF(1e3, "mF", "m"), F(1e6, "F", "F");
double factor;
String display;
String rkmSymbol;
private CapacitanceUnit(double factor, String display) {
private CapacitanceUnit(double factor, String display, String rkmSymbol) {
this.factor = factor;
this.display = display;
this.rkmSymbol = rkmSymbol;
}
@Override
@@ -38,8 +40,49 @@ public enum CapacitanceUnit implements Unit {
return factor;
}
public String getRkmSymbol() {
return rkmSymbol;
}
@Override
public String toString() {
return display;
}
/**
* Resolves the unit written after a capacitance value, e.g. "nF", "n", "u", "\u00B5F" or "F".
* Case does not matter when the trailing "F" is present, which is how values have always been
* parsed. Without it, "M" is rejected so that it is never mistaken for mega.
*
* @param symbol unit text with no surrounding whitespace
* @return the unit, or null when the text is not a capacitance unit
*/
public static CapacitanceUnit fromSymbol(String symbol) {
if (symbol.equalsIgnoreCase("F")) {
return F;
}
String prefix = symbol;
if (symbol.length() == 2 && (symbol.endsWith("F") || symbol.endsWith("f"))) {
prefix = symbol.substring(0, 1).toLowerCase();
} else if (symbol.equals("M")) {
return null;
}
switch (prefix) {
case "p":
case "P":
return pF;
case "n":
case "N":
return nF;
case "u":
case "U":
case "\u00B5":
case "\u03BC":
return uF;
case "m":
return mF;
default:
return null;
}
}
}
@@ -41,13 +41,24 @@ public class Inductance extends AbstractMeasure<InductanceUnit> {
}
public static Inductance parseCapacitance(String value) {
value = value.replace("*", "");
for (InductanceUnit unit : InductanceUnit.values()) {
if (value.toLowerCase().endsWith(unit.toString().toLowerCase())) {
value = value.substring(0, value.length() - unit.toString().length()).trim();
return new Inductance(parse(value), unit);
}
ValueNotation.ParsedMeasure<InductanceUnit> parsed;
try {
parsed = ValueNotation.parse(value, InductanceUnit::fromSymbol);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not parse inductance: " + value, e);
}
throw new IllegalArgumentException("Could not parse inductance: " + value);
if (parsed.unit() == null) {
throw new IllegalArgumentException("Could not parse inductance: " + value);
}
return new Inductance(parsed.value(), parsed.unit());
}
@Override
public String toString() {
if (ValueNotation.getCurrent() == ValueNotation.RKM && value != null && unit != null
&& unit.getRkmSymbol() != null) {
return ValueNotation.formatRkm(value, unit.getRkmSymbol());
}
return super.toString();
}
}
@@ -23,14 +23,16 @@ package org.diylc.core.measures;
public enum InductanceUnit implements Unit {
pH(1, "pH"), nH(1e3, "nH"), uH(1e6, "uH"), mH(1e9, "mH"), H(1e12, "H");
pH(1, "pH", "p"), nH(1e3, "nH", "n"), uH(1e6, "uH", "u"), mH(1e9, "mH", "m"), H(1e12, "H", "H");
double factor;
String display;
String rkmSymbol;
private InductanceUnit(double factor, String display) {
private InductanceUnit(double factor, String display, String rkmSymbol) {
this.factor = factor;
this.display = display;
this.rkmSymbol = rkmSymbol;
}
@Override
@@ -38,8 +40,50 @@ public enum InductanceUnit implements Unit {
return factor;
}
public String getRkmSymbol() {
return rkmSymbol;
}
@Override
public String toString() {
return display;
}
/**
* Resolves the unit written after an inductance value, e.g. "uH", "u", "\u00B5H" or "H".
* Case does not matter when the trailing "H" is present. Without it, "M" is rejected so that it
* is never mistaken for mega. "R" is not accepted: inductor makers print 4R7 for 4.7uH, while
* in RKM it would mean 4.7H.
*
* @param symbol unit text with no surrounding whitespace
* @return the unit, or null when the text is not an inductance unit
*/
public static InductanceUnit fromSymbol(String symbol) {
if (symbol.equalsIgnoreCase("H")) {
return H;
}
String prefix = symbol;
if (symbol.length() == 2 && (symbol.endsWith("H") || symbol.endsWith("h"))) {
prefix = symbol.substring(0, 1).toLowerCase();
} else if (symbol.equals("M")) {
return null;
}
switch (prefix) {
case "p":
case "P":
return pH;
case "n":
case "N":
return nH;
case "u":
case "U":
case "\u00B5":
case "\u03BC":
return uH;
case "m":
return mH;
default:
return null;
}
}
}

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