Controllers (#1080)

* initial

* more components

* fixes

* Update TreePanel.java

* fix boost converter

* Improve wrapping of component names

* Update ULN2003Driver.java

* Update NRF24L01Transceiver.java

* imp

* updates

* improvements

* more components

* arduino

* uno

* icon

* cleanup

* mega

* mega

* improveent

* nano

* esp boards

* esp

* esp32

* esp32

* Update ESP8266NodeMCU.java

* d1

* Update WemosD1Mini.java

* pi

* consolidate esp32

* pi pico updates

* pi zero

* Update RaspberryPiZero.java

* pi pico

* cleanup

* pi improvements

* consolidate pins

* antenna consolidation

* improve solder pads

* teensy

* headers

* teensy fix, test cleanup

* s3

* Update ESP32DevKit.java

* usb standardization

* pico w

* button standardization

* arduino improvements

* Update AbstractMakerBoard.java

* Update RaspberryPiZero.java

* Update RaspberryPi.java

* pi 3

* pi 5 improvements

* Update RaspberryPiZero.java

* fix tests

* esp pinout fix

* Update ESP32DevKit.java

* pin name improvements

* Create tier-1-modern-boards.md

* new versions

* pinouts

* nano improvements

* sanitize node names

* label improvements

* minima fixes

* arduino uno labels

* nano labels

* Update ArduinoNano.java

* mega

* mega

* comment out future stuff

* release prep

* Update RaspberryPiPico.java

* Refactor abstract class

* labeling improvement

* label offset fixes

* Update Teensy.java

* consolidate tests

* micro variants

* Update RaspberryPi.java

* displays tweaks

* display improvements

* neopix

* displays

* improvements

* led matrix

* strip

* led bar graph

* jewel

* tft

* tft

* Update maker-peripherals.md

* nokia

* led bar

* labels

* tft

* cleanup

* oled

* cleanup

* display enhancement

* displays

* neopixel

* displays

* displays

* new tft

* displays

* icon

* variants

* 2-digit 0.56 inch 7 segment

* Added ability to hide component categories in the component tree and toolbar from the Config menu

* led bar

* Update variants.xml

* color selection for bar graph

* Added 1.8" sd

* tft fixes

* Update WS2812BRing.java

* neopixel improvements

* netlist and node name improvements

* oled tweak

* cleanup

* mockito fix

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Bane Stojkovic
2026-10-09 09:38:09 +02:00
committed by GitHub
parent f62b3ddf04
commit a743de4024
87 changed files with 11343 additions and 1933 deletions
+11
View File
@@ -74,6 +74,17 @@
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<!-- Subclass mock maker, selected in src/test/resources/mockito-extensions. Mockito 5
defaults to the inline maker, which attaches a Java agent to the running JVM; that
attachment fails outright on some CI JDKs and is deprecated since Java 21. Every type
mocked in these tests is an interface, which the subclass maker handles without an
agent. -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-subclass</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<!-- Local dependencies from lib folder -->
<dependency>
@@ -117,5 +117,11 @@ public enum EventType {
* Called when the mouse hovers over sticky control points (nodes) in tooltip mode.
* Parameters: String text (semicolon-separated component and node names, or null), Point point.
*/
NODE_NAME_HOVER_TOOLTIP;
NODE_NAME_HOVER_TOOLTIP,
/**
* Called when the set of enabled component categories changes. The first parameter is a
* <code>Set&lt;String&gt;</code> of disabled category names. All categories not included are
* enabled.
*/
COMPONENT_CATEGORIES_CHANGED;
}
@@ -83,6 +83,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String HARDWARE_ACCELERATION = "hardwareAcceleration";
public static final String EXTRA_SPACE_KEY = "extraSpace";
public static final String FAVORITES_KEY = "favorites";
public static final String DISABLED_CATEGORIES_KEY = "disabledComponentCategories";
public static final String RENUMBER_ON_PASTE_KEY = "renumberOnPaste";
public static final String RULER_IN_SUBDIVISION_KEY = "rulerInSubdivision";
public static final String CACHING_ENABLED_KEY = "cachingEnabled";
@@ -213,6 +214,20 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
*/
Map<String, List<ComponentType>> getComponentTypes();
/**
* Returns the {@link ComponentType}s that should be offered to the user, i.e. the result of
* {@link #getComponentTypes()} without the categories the user disabled. Intended for component
* browsers; anything that needs to resolve an arbitrary component class must use
* {@link #getComponentTypes()} instead.
*/
Map<String, List<ComponentType>> getVisibleComponentTypes();
boolean isCategoryEnabled(String category);
void setCategoryEnabled(String category, boolean enabled);
void resetCategoryVisibility();
/**
* Draws project on the provided {@link Graphics2D}. If the provided filter is not null, it will
* be used to filter the components that are shown.
@@ -411,6 +411,23 @@ public class NetlistBuilder {
if (((IContinuity) c).arePointsConnected(i, j))
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j), z));
}
// Pins of one component that name the same rail are the same node: a board's eight ground
// pins land on one plane, and grounding one of them must not leave the rest floating.
// Switches are excluded because their connectivity depends on the position, which a name
// cannot express -- a switch states it through ISwitch.arePointsConnected instead.
if (!(c instanceof ISwitch)) {
for (int i = 0; i < c.getControlPointCount() - 1; i++) {
String rail = Node.railName(c.getControlPointNodeName(i));
if (rail == null) {
continue;
}
for (int j = i + 1; j < c.getControlPointCount(); j++) {
if (rail.equals(Node.railName(c.getControlPointNodeName(j)))) {
connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j), z));
}
}
}
}
// handle switches
if (ISwitch.class.isAssignableFrom(type.getInstanceClass())
&& switchPositions.containsKey(c)) {
@@ -22,7 +22,10 @@
package org.diylc.netlist;
import java.awt.geom.Point2D;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.diylc.core.ICommonNode;
import org.diylc.core.IDIYComponent;
@@ -34,6 +37,10 @@ import org.diylc.core.IDIYComponent;
*/
public class Node implements Comparable<Node> {
/** Pin names that mark an absent connection, so repeats of them are not one net. */
private static final Set<String> NON_NET_NAMES =
new HashSet<String>(Arrays.asList("NC", "RSV"));
private IDIYComponent<?> component;
private int pointIndex;
@@ -60,8 +67,9 @@ public class Node implements Comparable<Node> {
* <code>MCU1.D11</code> rather than <code>MCU1.D11 (~, MOSI)</code>. The bare name identifies the
* pin; everything in parentheses documents its alternate functions and only adds noise here.
*
* <p>A trailing disambiguator such as the <code>_1</code> in <code>GND_1</code> is deliberately
* kept, because those mark genuinely different pins that a netlist has to tell apart.
* <p>A trailing disambiguator such as the <code>_1</code> in <code>GND_1</code> is kept, so the
* netlist can still say which pad a connection reached. Whether two such pins are the same
* net is a separate question, answered by {@link #railName(String)}.
*
* @param nodeName raw name from {@link IDIYComponent#getControlPointNodeName(int)}
* @return the sanitized name, or null if the raw name was null
@@ -75,6 +83,28 @@ public class Node implements Comparable<Node> {
// Never let sanitizing leave a pin nameless
return sanitized.isEmpty() ? nodeName.trim() : sanitized;
}
/**
* The rail a pin belongs to: its sanitized name with a trailing <code>_1</code>, <code>_2</code>
* disambiguator removed, so <code>GND_1</code> and <code>GND_2</code> report the same rail.
*
* <p>Only a numeric suffix is dropped. An underscore does three unrelated jobs across the
* pinouts in this library: it disambiguates a repeated rail, it namespaces a connector's signals
* (<code>ETH_TX-</code>, <code>USB_D+</code>), and it qualifies a genuinely separate net
* (<code>MISO_16U2</code>, <code>3V3_EN</code>). Cutting at the first underscore instead would
* report an Ethernet header as one rail.
*
* @param nodeName raw name from {@link IDIYComponent#getControlPointNodeName(int)}
* @return the rail name, or null if the pin has no name or names no net at all
*/
public static String railName(String nodeName) {
String sanitized = sanitizeNodeName(nodeName);
if (sanitized == null || sanitized.isEmpty()) {
return null;
}
String rail = sanitized.replaceAll("_\\d+$", "");
return NON_NET_NAMES.contains(rail) ? null : rail;
}
public Point2D getPoint2D() {
return component.getControlPoint(pointIndex);
@@ -202,6 +202,14 @@ public class Presenter implements IPlugInPort {
messageDispatcher.dispatchMessage(EventType.NODE_NAME_HOVER_TOOLTIP, null, null);
}
});
configManager.addConfigListener(IPlugInPort.DISABLED_CATEGORIES_KEY, new IConfigListener() {
@Override
public void valueChanged(String key, Object value) {
messageDispatcher.dispatchMessage(EventType.COMPONENT_CATEGORIES_CHANGED,
getDisabledCategories());
}
});
}
public void installPlugin(Supplier<IPlugIn> plugInSupplier) {
@@ -408,12 +416,58 @@ public class Presenter implements IPlugInPort {
return projectFileManager.isModified();
}
@SuppressWarnings("unchecked")
private Set<String> getDisabledCategories() {
List<String> list = (List<String>) configManager.readObject(
IPlugInPort.DISABLED_CATEGORIES_KEY, new ArrayList<String>());
if (list == null) {
return new HashSet<String>();
}
return new HashSet<String>(list);
}
@SuppressWarnings("unchecked")
@Override
public Map<String, List<ComponentType>> getComponentTypes() {
return ComponentProcessor.getInstance().getComponentTypes();
}
@Override
public Map<String, List<ComponentType>> getVisibleComponentTypes() {
Set<String> disabled = getDisabledCategories();
Map<String, List<ComponentType>> all = getComponentTypes();
Map<String, List<ComponentType>> visible = new HashMap<String, List<ComponentType>>();
for (Map.Entry<String, List<ComponentType>> entry : all.entrySet()) {
if (!disabled.contains(entry.getKey())) {
visible.put(entry.getKey(), entry.getValue());
}
}
return visible;
}
@Override
public boolean isCategoryEnabled(String category) {
return !getDisabledCategories().contains(category);
}
@Override
public void setCategoryEnabled(String category, boolean enabled) {
Set<String> disabled = getDisabledCategories();
if (enabled) {
disabled.remove(category);
} else {
disabled.add(category);
}
List<String> list = new ArrayList<String>(disabled);
Collections.sort(list);
configManager.writeValue(IPlugInPort.DISABLED_CATEGORIES_KEY, list);
}
@Override
public void resetCategoryVisibility() {
configManager.writeValue(IPlugInPort.DISABLED_CATEGORIES_KEY, new ArrayList<String>());
}
@SuppressWarnings({"unchecked"})
private boolean isComponentVisible(IDIYComponent<?> component) {
ComponentType componentType =
File diff suppressed because it is too large Load Diff
@@ -496,5 +496,85 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>6</major>
<minor>7</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-10-11 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>7-Segment Display - 1-digit 0.56 inch, 2-digit 0.56 inch, 4-digit in 0.36 inch and 0.56 inch, and TM1637 driver module</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Character LCD - 16x2 and 20x4, with I2C backpack or 16-pin parallel interface</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>LED Bar Graph - 4, 5, 6, 8, 10 and 12 segments</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>LED Matrix (MAX7219) - single 8x8, compact 8x8, 4-in-1 32x8, 8-in-1 64x8</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Breakout (WS2812B) - single LED</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Jewel (WS2812B) - 7 LEDs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Panel (WS2812B) - 8x8, 64 LEDs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Ring (WS2812B) - 12, 16 and 24 LEDs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Stick (WS2812B) - 8 LEDs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>NeoPixel Strip (WS2812B) - 30, 60 and 144 LEDs per meter, up to 144 LEDs</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Nokia 5110 LCD (PCD8544)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>OLED Display - 0.96 inch SSD1306, 0.91 inch SSD1306, 1.3 inch SH1106, in I2C, 7-pin SPI and 6-pin SPI</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>TFT Display - 2.8 inch and 2.4 inch ILI9341, 1.8 inch and 0.96 inch ST7735, 1.54 inch ST7789, 1.28 inch round GC9A01</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added ability to hide component categories in the component tree and toolbar from the Config menu</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Bill of Materials now lists the selected variant of microcontroller boards and displays, so different versions of the same board are no longer merged into a single row</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>4-pin Jumbo Tube Socket - the two filament pins are now labeled F1 and F2 instead of both being labeled F, so they no longer read as the same node</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Netlists now treat the repeated supply pins of a microcontroller or display board as one node, so grounding a single GND pin no longer leaves the others floating</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>
@@ -48,4 +48,41 @@ public class NodeNameTest {
Assert.assertEquals("(only annotation)", Node.sanitizeNodeName("(only annotation)"));
Assert.assertNull(Node.sanitizeNodeName(null));
}
@Test
public void railNameDropsOnlyANumericDisambiguator() {
Assert.assertEquals("GND", Node.railName("GND_1"));
Assert.assertEquals("GND", Node.railName("GND_12"));
Assert.assertEquals("GND", Node.railName("GND"));
Assert.assertEquals("GND", Node.railName("GND (Pin 6)"));
Assert.assertEquals("+5V", Node.railName("+5V_2"));
// the annotation is stripped before the disambiguator, so an annotated rail still resolves
Assert.assertEquals("3.3V", Node.railName("3.3V_2 (250mA)"));
Assert.assertEquals("GND", Node.railName("GND_4 (Mid)"));
}
@Test
public void railNameKeepsNonNumericQualifiers() {
// namespaced connector signals
Assert.assertEquals("ETH_TX-", Node.railName("ETH_TX-"));
Assert.assertEquals("USB_D+", Node.railName("USB_D+"));
// qualifiers that name a separate net
Assert.assertEquals("MISO_16U2", Node.railName("MISO_16U2"));
Assert.assertEquals("3V3_EN", Node.railName("3V3_EN"));
// a bare trailing digit is part of the name, not a disambiguator
Assert.assertEquals("GP1", Node.railName("GP1"));
Assert.assertEquals("GND1", Node.railName("GND1"));
// digits that are not at the end are part of the name too: the 16U2 bridge's ground and 5V
// are separate nets from the board's own, and USB_5V is a connector pin, not a 5V rail
Assert.assertEquals("GND_16U2", Node.railName("GND_16U2"));
Assert.assertEquals("5V_16U2", Node.railName("5V_16U2"));
Assert.assertEquals("USB_5V", Node.railName("USB_5V"));
}
@Test
public void railNameRejectsPinsThatNameNoNet() {
Assert.assertNull(Node.railName("NC_1"));
Assert.assertNull(Node.railName("RSV_2"));
Assert.assertNull(Node.railName(null));
}
}
@@ -0,0 +1 @@
member-accessor-reflection
@@ -0,0 +1 @@
mock-maker-subclass