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
@@ -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