This commit is contained in:
Bane Stojkovic
2026-08-25 15:02:28 +02:00
parent 34422c501c
commit ac8fac0746
2 changed files with 243 additions and 23 deletions
@@ -99,9 +99,12 @@ public class RaspberryPiZero extends AbstractMakerBoard {
double y = p0.getY();
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 55;
double boardY = y - 45;
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 12, 12);
double pin1OffsetX = new Size(8.37d, SizeUnit.mm).convertToPixels();
double pin1OffsetY = new Size(3.5d, SizeUnit.mm).convertToPixels() + PIN_SPACING.convertToPixels() / 2.0;
double boardX = x - pin1OffsetX;
double boardY = y - pin1OffsetY;
double cornerArc = new Size(6.0d, SizeUnit.mm).convertToPixels();
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, cornerArc, cornerArc);
}
@Override
@@ -122,8 +125,10 @@ public class RaspberryPiZero extends AbstractMakerBoard {
double boardW = BOARD_WIDTH.convertToPixels();
double boardH = BOARD_HEIGHT.convertToPixels();
double boardX = x - 55;
double boardY = y - 45;
double pin1OffsetX = new Size(8.37d, SizeUnit.mm).convertToPixels();
double pin1OffsetY = new Size(3.5d, SizeUnit.mm).convertToPixels() + PIN_SPACING.convertToPixels() / 2.0;
double boardX = x - pin1OffsetX;
double boardY = y - pin1OffsetY;
Shape boardShape = getBodyShape();
@@ -132,31 +137,79 @@ public class RaspberryPiZero extends AbstractMakerBoard {
drawingObserver.startTracking();
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
g2d.fill(boardShape);
drawingObserver.stopTracking();
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
g2d.draw(boardShape);
if (!outlineMode) {
drawMountingHole(g2d, boardX + 25, boardY + 25, 20);
drawMountingHole(g2d, boardX + 25, boardY + boardH - 25, 20);
drawMountingHole(g2d, boardX + boardW - 25, boardY + 25, 20);
drawMountingHole(g2d, boardX + boardW - 25, boardY + boardH - 25, 20);
// 4 Mounting holes (diameter 2.75mm, located 3.5mm from board edges)
double holeDiameter = new Size(2.75d, SizeUnit.mm).convertToPixels();
drawMountingHole(g2d, boardX + new Size(3.5d, SizeUnit.mm).convertToPixels(),
boardY + new Size(3.5d, SizeUnit.mm).convertToPixels(), holeDiameter);
drawMountingHole(g2d, boardX + new Size(3.5d, SizeUnit.mm).convertToPixels(),
boardY + boardH - new Size(3.5d, SizeUnit.mm).convertToPixels(), holeDiameter);
drawMountingHole(g2d, boardX + boardW - new Size(3.5d, SizeUnit.mm).convertToPixels(),
boardY + new Size(3.5d, SizeUnit.mm).convertToPixels(), holeDiameter);
drawMountingHole(g2d, boardX + boardW - new Size(3.5d, SizeUnit.mm).convertToPixels(),
boardY + boardH - new Size(3.5d, SizeUnit.mm).convertToPixels(), holeDiameter);
// Mini HDMI & Dual Micro-USB
drawMetalConnector(g2d, boardX + 80, boardY + boardH - 8, 65, 40, "HDMI");
drawMetalConnector(g2d, boardX + 240, boardY + boardH - 8, 55, 40, "USB");
drawMetalConnector(g2d, boardX + 350, boardY + boardH - 8, 55, 40, "PWR");
// MicroSD card slot on the left edge
double sdW = new Size(11.0d, SizeUnit.mm).convertToPixels();
double sdH = new Size(12.0d, SizeUnit.mm).convertToPixels();
double sdX = boardX + new Size(1.5d, SizeUnit.mm).convertToPixels();
double sdY = boardY + new Size(7.5d, SizeUnit.mm).convertToPixels();
drawMetalConnector(g2d, sdX, sdY, sdW, sdH, "SD");
// SoC chip
drawChip(g2d, boardX + 150, boardY + 80, 80, 80, "RPi SoC");
// Mini HDMI Connector (center at X = 12.4mm, width = 11.2mm, height = 7.5mm, overhang = 1.5mm)
double hdmiW = new Size(11.2d, SizeUnit.mm).convertToPixels();
double hdmiH = new Size(7.5d, SizeUnit.mm).convertToPixels();
double hdmiX = boardX + new Size(12.4d, SizeUnit.mm).convertToPixels() - hdmiW / 2.0;
double hdmiY = boardY + boardH - new Size(6.0d, SizeUnit.mm).convertToPixels();
drawMetalConnector(g2d, hdmiX, hdmiY, hdmiW, hdmiH, "HDMI");
// Micro USB Data (center at X = 41.4mm, width = 7.5mm, height = 5.6mm, overhang = 1.4mm)
double usbW = new Size(7.5d, SizeUnit.mm).convertToPixels();
double usbH = new Size(5.6d, SizeUnit.mm).convertToPixels();
double usbX = boardX + new Size(41.4d, SizeUnit.mm).convertToPixels() - usbW / 2.0;
double usbY = boardY + boardH - new Size(4.2d, SizeUnit.mm).convertToPixels();
drawMetalConnector(g2d, usbX, usbY, usbW, usbH, "USB");
// Micro USB Power (center at X = 54.0mm, width = 7.5mm, height = 5.6mm, overhang = 1.4mm)
double pwrX = boardX + new Size(54.0d, SizeUnit.mm).convertToPixels() - usbW / 2.0;
double pwrY = boardY + boardH - new Size(4.2d, SizeUnit.mm).convertToPixels();
drawMetalConnector(g2d, pwrX, pwrY, usbW, usbH, "PWR");
// Camera Connector (CSI) on right edge
double csiW = new Size(3.2d, SizeUnit.mm).convertToPixels();
double csiH = new Size(16.5d, SizeUnit.mm).convertToPixels();
double csiX = boardX + boardW - new Size(4.2d, SizeUnit.mm).convertToPixels();
double csiY = boardY + new Size(6.75d, SizeUnit.mm).convertToPixels();
drawChip(g2d, csiX, csiY, csiW, csiH, "CSI");
// Main SoC chip (Broadcom BCM2835 / RP3A0)
double socW = new Size(12.0d, SizeUnit.mm).convertToPixels();
double socH = new Size(12.0d, SizeUnit.mm).convertToPixels();
double socX = boardX + new Size(19.5d, SizeUnit.mm).convertToPixels();
double socY = boardY + new Size(11.5d, SizeUnit.mm).convertToPixels();
drawChip(g2d, socX, socY, socW, socH, "");
// Raspberry Pi logo on SoC chip
double logoSize = new Size(7.0d, SizeUnit.mm).convertToPixels();
double logoW = logoSize * (72.51 / 92.604);
double logoX = socX + (socW - logoW) / 2.0;
double logoY = socY + (socH - logoSize) / 2.0;
drawRaspberryPiLogo(g2d, logoX, logoY, logoSize);
// Silkscreen "GPIO" label next to the top pins
g2d.setColor(Color.WHITE);
g2d.setFont(SILK_FONT);
StringUtils.drawCenteredText(g2d, "Pi Zero", boardX + 280, boardY + 100, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
g2d.setFont(SILK_FONT_SMALL);
StringUtils.drawCenteredText(g2d, "GPIO", boardX + new Size(47.5d, SizeUnit.mm).convertToPixels(),
boardY + new Size(7.8d, SizeUnit.mm).convertToPixels(), HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
drawingObserver.stopTracking();
g2d.setTransform(oldTx);
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
@@ -167,18 +220,26 @@ public class RaspberryPiZero extends AbstractMakerBoard {
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setColor(RPI_GREEN);
g2d.fill(new RoundRectangle2D.Double(2, 8, width - 4, height - 16, 4, 4));
g2d.fill(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 3, 3));
g2d.setColor(RPI_GREEN.darker());
g2d.draw(new RoundRectangle2D.Double(2, 8, width - 4, height - 16, 4, 4));
g2d.draw(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 3, 3));
// GPIO Header
g2d.setColor(HEADER_BODY_COLOR);
g2d.fillRect(6, 10, width - 12, 3);
g2d.fillRect(5, 7, width - 10, 3);
// SoC
g2d.setColor(IC_BODY_COLOR);
g2d.fillRect(11, 15, 10, 6);
g2d.fillRect(10, 12, 6, 6);
// Connectors on bottom
g2d.setColor(USB_METAL_COLOR);
g2d.fillRect(6, height - 7, 4, 3);
g2d.fillRect(18, height - 7, 3, 3);
g2d.fillRect(23, height - 7, 3, 3);
g2d.setColor(Color.WHITE);
g2d.setFont(new Font("SansSerif", Font.BOLD, 5));
StringUtils.drawCenteredText(g2d, "ZERO", width / 2, height / 2 + 5, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, "ZERO", width / 2 + 4, 15, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
}
@@ -0,0 +1,159 @@
package org.diylc.components.micro;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.concurrent.atomic.AtomicInteger;
import org.diylc.common.Orientation;
import org.diylc.core.ComponentState;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.junit.Assert;
import org.junit.Test;
public class RaspberryPiZeroTest {
@Test
public void testControlPointCountAndNames() {
RaspberryPiZero pi = new RaspberryPiZero();
Assert.assertEquals(40, pi.getControlPointCount());
for (int i = 0; i < pi.getControlPointCount(); i++) {
String name = pi.getControlPointNodeName(i);
Assert.assertNotNull("Pin " + i + " name should not be null", name);
Assert.assertFalse("Pin " + i + " name should not be empty", name.trim().isEmpty());
}
Assert.assertEquals("3.3V (Pin 1)", pi.getControlPointNodeName(0));
Assert.assertEquals("5V (Pin 2)", pi.getControlPointNodeName(1));
Assert.assertEquals("GPIO2/SDA (Pin 3)", pi.getControlPointNodeName(2));
Assert.assertEquals("5V (Pin 4)", pi.getControlPointNodeName(3));
Assert.assertEquals("GND (Pin 39)", pi.getControlPointNodeName(38));
Assert.assertEquals("GPIO21 (Pin 40)", pi.getControlPointNodeName(39));
}
@Test
public void testDimensionsAndPinGeometry() {
RaspberryPiZero pi = new RaspberryPiZero();
// Board dimensions: 65 mm x 30 mm
double expectedWidth = new Size(65.0d, SizeUnit.mm).convertToPixels();
double expectedHeight = new Size(30.0d, SizeUnit.mm).convertToPixels();
Shape body = pi.getBodyShape();
Assert.assertNotNull(body);
Rectangle2D bounds = body.getBounds2D();
Assert.assertEquals(expectedWidth, bounds.getWidth(), 0.1);
Assert.assertEquals(expectedHeight, bounds.getHeight(), 0.1);
// Verify Pin 1 location relative to board top-left edge
// Pin 1 offset X: 8.37 mm from left edge (centered between holes at X=3.5mm and X=61.5mm)
// Pin 1 offset Y: 4.77 mm from top edge (3.5 mm + spacing/2 = 4.77 mm)
// Pin 2 offset Y: 2.23 mm from top edge (3.5 mm - spacing/2 = 2.23 mm)
// Vertical center of header: (4.77 + 2.23) / 2 = 3.5 mm (dead-centered with mounting holes)
double expectedPin1OffsetX = new Size(8.37d, SizeUnit.mm).convertToPixels();
double expectedPin1OffsetY = new Size(3.5d, SizeUnit.mm).convertToPixels() + new Size(0.1d, SizeUnit.in).convertToPixels() / 2.0;
Point2D p0 = pi.getControlPoint(0); // Pin 1
Assert.assertEquals(expectedPin1OffsetX, p0.getX() - bounds.getX(), 0.1);
Assert.assertEquals(expectedPin1OffsetY, p0.getY() - bounds.getY(), 0.1);
// Pin 2 (outer row) should be at Y = 2.23 mm from top edge (3.5 mm - spacing/2)
Point2D p1 = pi.getControlPoint(1); // Pin 2
double expectedPin2OffsetY = new Size(3.5d, SizeUnit.mm).convertToPixels() - new Size(0.1d, SizeUnit.in).convertToPixels() / 2.0;
Assert.assertEquals(p0.getX(), p1.getX(), 0.01);
Assert.assertEquals(expectedPin2OffsetY, p1.getY() - bounds.getY(), 0.1);
// Verify vertical center of header is dead in the middle with the 3.5mm mounting hole line
double headerCenterY = (p0.getY() + p1.getY()) / 2.0 - bounds.getY();
Assert.assertEquals(new Size(3.5d, SizeUnit.mm).convertToPixels(), headerCenterY, 0.1);
// Check pin spacing (20px per 0.1" pitch)
double spacing = new Size(0.1d, SizeUnit.in).convertToPixels();
for (int col = 0; col < 19; col++) {
int pinOdd1 = col * 2;
int pinOdd2 = (col + 1) * 2;
Point2D po1 = pi.getControlPoint(pinOdd1);
Point2D po2 = pi.getControlPoint(pinOdd2);
Assert.assertEquals(spacing, po2.getX() - po1.getX(), 0.01);
Assert.assertEquals(po1.getY(), po2.getY(), 0.01);
int pinEven1 = col * 2 + 1;
int pinEven2 = (col + 1) * 2 + 1;
Point2D pe1 = pi.getControlPoint(pinEven1);
Point2D pe2 = pi.getControlPoint(pinEven2);
Assert.assertEquals(spacing, pe2.getX() - pe1.getX(), 0.01);
Assert.assertEquals(pe1.getY(), pe2.getY(), 0.01);
}
// Header center should be at 32.5 mm from left edge
Point2D pLastOdd = pi.getControlPoint(38); // Pin 39 (col 19)
double headerCenterX = (p0.getX() + pLastOdd.getX()) / 2.0;
double expectedHeaderCenterX = bounds.getX() + new Size(32.5d, SizeUnit.mm).convertToPixels();
Assert.assertEquals(expectedHeaderCenterX, headerCenterX, 0.1);
}
@Test
public void testDrawingAndOutline() {
RaspberryPiZero pi = new RaspberryPiZero();
pi.setControlPoint(new Point2D.Double(200, 200), 0);
BufferedImage img = new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setClip(new Rectangle(0, 0, 600, 600));
Project project = new Project();
final AtomicInteger trackingStartCount = new AtomicInteger(0);
final AtomicInteger trackingStopCount = new AtomicInteger(0);
IDrawingObserver observer = new IDrawingObserver() {
@Override public void startTracking() {
trackingStartCount.incrementAndGet();
}
@Override public void stopTracking() {
trackingStopCount.incrementAndGet();
}
@Override public void startTrackingContinuityArea(boolean positive) {}
@Override public void stopTrackingContinuityArea() {}
@Override public boolean isTrackingContinuityArea() { return false; }
@Override public void setContinuityMarker(String marker) {}
};
pi.draw(g2d, ComponentState.NORMAL, false, project, observer);
Assert.assertEquals(1, trackingStartCount.get());
Assert.assertEquals(1, trackingStopCount.get());
pi.draw(g2d, ComponentState.SELECTED, false, project, observer);
pi.draw(g2d, ComponentState.NORMAL, true, project, observer);
pi.drawIcon(g2d, 32, 32);
g2d.dispose();
}
@Test
public void testRotation() {
RaspberryPiZero pi = new RaspberryPiZero();
pi.setControlPoint(new Point2D.Double(200, 200), 0);
Point2D p0Initial = pi.getControlPoint(0);
Point2D p1Initial = pi.getControlPoint(1);
pi.setOrientation(Orientation._90);
Point2D p0Rot = pi.getControlPoint(0);
Point2D p1Rot = pi.getControlPoint(1);
Assert.assertEquals(p0Initial.getX(), p0Rot.getX(), 0.01);
Assert.assertEquals(p0Initial.getY(), p0Rot.getY(), 0.01);
// After 90 deg clockwise rotation, p1 (which was at (x, y - spacing)) should be at (x + spacing, y)
double spacing = new Size(0.1d, SizeUnit.in).convertToPixels();
Assert.assertEquals(p0Rot.getX() + spacing, p1Rot.getX(), 0.01);
Assert.assertEquals(p0Rot.getY(), p1Rot.getY(), 0.01);
}
}