From a2e52b55208a252a18b587da30b833fe59322152 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Wed, 19 Aug 2026 13:55:36 +0200 Subject: [PATCH] mega --- .../diylc/components/micro/ArduinoMega.java | 200 ++++++++++++------ .../components/micro/ArduinoMegaTest.java | 152 +++++++++++++ 2 files changed, 293 insertions(+), 59 deletions(-) create mode 100644 diylc/diylc-library/src/test/java/org/diylc/components/micro/ArduinoMegaTest.java diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/micro/ArduinoMega.java b/diylc/diylc-library/src/main/java/org/diylc/components/micro/ArduinoMega.java index 4f581ce6..22920f0e 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/micro/ArduinoMega.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/micro/ArduinoMega.java @@ -28,8 +28,8 @@ import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import org.diylc.awt.StringUtils; @@ -62,24 +62,29 @@ public class ArduinoMega extends AbstractMakerBoard { public static Size BOARD_WIDTH = new Size(101.6d, SizeUnit.mm); public static Size BOARD_HEIGHT = new Size(53.4d, SizeUnit.mm); + // Pin names in sequence (98 pins total) public static final String[] PIN_NAMES = new String[] { - // Power (0..7) + // Power Header (0..7) "NC", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN", // Analog Low A0..A7 (8..15) "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", // Analog High A8..A15 (16..23) "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", - // Digital Low (24..31) - "D0 (RX0)", "D1 (TX0)", "D2", "D3 (~)", "D4", "D5 (~)", "D6 (~)", "D7", - // Digital High (32..41) - "D8", "D9 (~)", "D10 (~)", "D11 (~)", "D12", "D13", "GND3", "AREF", "SDA", "SCL", - // Communication Header (42..49) + // Digital Low D0..D7 (24..31) + "D0 (RX0)", "D1 (TX0)", "D2 (~)", "D3 (~)", "D4 (~)", "D5 (~)", "D6 (~)", "D7 (~)", + // Digital High D8..D13, GND, AREF, SDA, SCL (32..41) + "D8 (~)", "D9 (~)", "D10 (~)", "D11 (~)", "D12 (~)", "D13 (~)", "GND3", "AREF", "SDA", "SCL", + // Communication Header D14..D21 (42..49) "D14 (TX3)", "D15 (RX3)", "D16 (TX2)", "D17 (RX2)", "D18 (TX1)", "D19 (RX1)", "D20 (SDA)", "D21 (SCL)", - // Double Digital 2x18 Header (50..85: D22..D53, 5V, 5V, GND, GND) + // Double Digital 2x18 Header (50..85: D22..D53, GNDx2, 5Vx2) "D22", "D23", "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", "D32", "D33", "D34", "D35", "D36", "D37", "D38", "D39", "D40", "D41", "D42", "D43", "D44", "D45", "D46", "D47", "D48", "D49", "D50 (MISO)", "D51 (MOSI)", "D52 (SCK)", "D53 (SS)", - "GND_EXT1", "GND_EXT2", "5V_EXT1", "5V_EXT2" + "GND_EXT1", "GND_EXT2", "5V_EXT1", "5V_EXT2", + // Main ICSP Header (86..91, ATmega2560) + "MISO", "5V_ICSP", "SCK", "MOSI", "RST_ICSP", "GND_ICSP", + // Top-Left ICSP Header (92..97, ATmega16U2) + "MISO_16U2", "5V_16U2", "SCK_16U2", "MOSI_16U2", "RST_16U2", "GND_16U2" }; public ArduinoMega() { @@ -99,57 +104,77 @@ public class ArduinoMega extends AbstractMakerBoard { @Override protected void updateControlPoints() { Point2D firstPoint = controlPoints[0]; - double spacing = PIN_SPACING.convertToPixels(); // 20px for 0.1" + double spacing = PIN_SPACING.convertToPixels(); // 20px for 0.1" (100 mils) - // Reference: Pin 0 (NC) is at (boardX + 220, boardY + 400) - // Board is 800px wide, 420px high + // Reference: Pin 0 (NC) is at (boardX + 220.0, boardY + 400.0) [1100 mils, 100 mils from bottom-left] + // Board is 800px wide (4000 mils), 420px high (2100 mils) double[][] relativeOffsets = new double[PIN_NAMES.length][2]; - // Power header pins 0..7 (bottom row) + // Power header pins 0..7 (bottom row, 1100..1800 mils) for (int i = 0; i < 8; i++) { relativeOffsets[i][0] = i * spacing; relativeOffsets[i][1] = 0; } - // Analog Low A0..A7 (8..15) (bottom row, separated by 0.16" = 32px) - double a0X = 7 * spacing + 32; + // Analog Low A0..A7 pins 8..15 (bottom row, 2000..2700 mils, separated from VIN by 0.2" = 40px) + double a0X = 7 * spacing + 40; // 180px for (int i = 0; i < 8; i++) { relativeOffsets[8 + i][0] = a0X + i * spacing; relativeOffsets[8 + i][1] = 0; } - // Analog High A8..A15 (16..23) (bottom row, continuing from A7) - double a8X = a0X + 8 * spacing; + // Analog High A8..A15 pins 16..23 (bottom row, 2900..3600 mils, separated from A7 by 0.2" = 40px) + double a8X = a0X + 7 * spacing + 40; // 360px for (int i = 0; i < 8; i++) { relativeOffsets[16 + i][0] = a8X + i * spacing; relativeOffsets[16 + i][1] = 0; } - // Digital Low D0..D7 (24..31) (top row) + // Digital Low D0..D7 pins 24..31 (top row, 2500..1800 mils, Y=2000 mils) double topRowY = -380; - double d0X = 300; + double d0X = 280; // aligns with A5 at 2500 mils for (int i = 0; i < 8; i++) { relativeOffsets[24 + i][0] = d0X - i * spacing; relativeOffsets[24 + i][1] = topRowY; } - // Digital High D8..D13/GND/AREF/SDA/SCL (32..41) (top row) - double d8X = 128; + // Digital High D8..D13/GND/AREF/SDA/SCL pins 32..41 (top row, 1640..740 mils, gap between D7 and D8 is 0.16" = 32px) + double d8X = 140 - 32; // 108px for (int i = 0; i < 10; i++) { relativeOffsets[32 + i][0] = d8X - i * spacing; relativeOffsets[32 + i][1] = topRowY; } - // Communication header D14..D21 (42..49) (top row) - double commX = a8X; + // Communication header D14..D21 pins 42..49 (top row, 2700..3400 mils, gap from D0 is 0.2" = 40px) + double d14X = d0X + 40; // 320px, aligns with A7 at 2700 mils for (int i = 0; i < 8; i++) { - relativeOffsets[42 + i][0] = commX + i * spacing; + relativeOffsets[42 + i][0] = d14X + i * spacing; relativeOffsets[42 + i][1] = topRowY; } - // Double digital header 2x18 at far right (50..85: D22..D53, GND, GND, 5V, 5V) - double d22X = 510; + // Double digital header 2x18 at far right pins 50..85 (D22..D53, GNDx2, 5Vx2; X=3600, 3700 mils, Y=2000..300 mils) + double d22InnerX = 500; // (3600 - 1100) * 0.2 + double d22OuterX = 520; // (3700 - 1100) * 0.2 for (int row = 0; row < 18; row++) { - double py = -360 + row * spacing; - relativeOffsets[50 + row * 2][0] = d22X; + double py = topRowY + row * spacing; + relativeOffsets[50 + row * 2][0] = d22InnerX; relativeOffsets[50 + row * 2][1] = py; - relativeOffsets[50 + row * 2 + 1][0] = d22X + spacing; + relativeOffsets[50 + row * 2 + 1][0] = d22OuterX; relativeOffsets[50 + row * 2 + 1][1] = py; } + // Main ICSP header (2x3 pins, 86..91) for ATmega2560 at (2505, 1200) mils + double icspX = (2505 - 1100) * 0.2; // 281.0 px + double icspY = (100 - 1200) * 0.2; // -220.0 px + relativeOffsets[86] = new double[] {icspX, icspY}; + relativeOffsets[87] = new double[] {icspX + spacing, icspY}; + relativeOffsets[88] = new double[] {icspX, icspY + spacing}; + relativeOffsets[89] = new double[] {icspX + spacing, icspY + spacing}; + relativeOffsets[90] = new double[] {icspX, icspY + 2 * spacing}; + relativeOffsets[91] = new double[] {icspX + spacing, icspY + 2 * spacing}; + + // Top-Left ICSP header (2x3 pins, 92..97) for ATmega16U2 near USB jack at (820, 1870) mils + double icsp2X = (820 - 1100) * 0.2; // -56.0 px + double icsp2Y = (100 - 1870) * 0.2; // -354.0 px + relativeOffsets[92] = new double[] {icsp2X, icsp2Y}; + relativeOffsets[93] = new double[] {icsp2X, icsp2Y + spacing}; + relativeOffsets[94] = new double[] {icsp2X - spacing, icsp2Y}; + relativeOffsets[95] = new double[] {icsp2X - spacing, icsp2Y + spacing}; + relativeOffsets[96] = new double[] {icsp2X - 2 * spacing, icsp2Y}; + relativeOffsets[97] = new double[] {icsp2X - 2 * spacing, icsp2Y + spacing}; rotatePoints(firstPoint, relativeOffsets); } @@ -159,11 +184,25 @@ public class ArduinoMega extends AbstractMakerBoard { Point2D p0 = controlPoints[0]; double x = p0.getX(); double y = p0.getY(); - double boardW = BOARD_WIDTH.convertToPixels(); - double boardH = BOARD_HEIGHT.convertToPixels(); - double boardX = x - 220; - double boardY = y - 400; - return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 16, 16); + double boardX = x - 220.0; + double boardY = y - 400.0; + + Path2D.Double path = new Path2D.Double(); + path.moveTo(boardX + 767.78, boardY + 0.00); + path.lineTo(boardX + 779.78, boardY + 12.00); + path.lineTo(boardX + 779.78, boardY + 102.00); + path.lineTo(boardX + 799.78, boardY + 122.00); + path.lineTo(boardX + 799.78, boardY + 380.00); + path.lineTo(boardX + 779.78, boardY + 400.00); + path.lineTo(boardX + 779.78, boardY + 412.00); + path.curveTo(boardX + 779.78, boardY + 416.44, boardX + 776.22, boardY + 419.78, boardX + 772.00, boardY + 419.78); + path.lineTo(boardX + 7.78, boardY + 419.78); + path.curveTo(boardX + 3.33, boardY + 419.78, boardX + 0.00, boardY + 416.22, boardX + 0.00, boardY + 412.00); + path.lineTo(boardX + 0.00, boardY + 7.78); + path.curveTo(boardX + 0.00, boardY + 3.33, boardX + 3.56, boardY + 0.00, boardX + 7.78, boardY + 0.00); + path.lineTo(boardX + 767.78, boardY + 0.00); + path.closePath(); + return path; } @Override @@ -182,15 +221,14 @@ public class ArduinoMega extends AbstractMakerBoard { g2d.rotate(orientation.toRadians(), x, y); } - double boardW = BOARD_WIDTH.convertToPixels(); - double boardH = BOARD_HEIGHT.convertToPixels(); - double boardX = x - 220; - double boardY = y - 400; + double boardX = x - 220.0; + double boardY = y - 400.0; Shape boardShape = getBodyShape(); Composite oldComposite = applyAlpha(g2d, componentState); + // Draw PCB body drawingObserver.startTracking(); g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor); g2d.fill(boardShape); @@ -201,41 +239,48 @@ public class ArduinoMega extends AbstractMakerBoard { g2d.draw(boardShape); if (!outlineMode) { - // Mounting holes - drawMountingHole(g2d, boardX + 110, boardY + 30, 24); - drawMountingHole(g2d, boardX + 120, boardY + 390, 24); - drawMountingHole(g2d, boardX + boardW - 30, boardY + 30, 24); - drawMountingHole(g2d, boardX + boardW - 30, boardY + boardH - 30, 24); + // Mounting holes (6 mounting holes from technical design) + drawMountingHole(g2d, boardX + 110, boardY + 400, 24); // (550, 100 mils) + drawMountingHole(g2d, boardX + 120, boardY + 20, 24); // (600, 2000 mils) + drawMountingHole(g2d, boardX + 520, boardY + 140, 24); // (2600, 1400 mils) + drawMountingHole(g2d, boardX + 520, boardY + 360, 24); // (2600, 300 mils) + drawMountingHole(g2d, boardX + 710, boardY + 20, 24); // (3550, 2000 mils) + drawMountingHole(g2d, boardX + 760, boardY + 400, 24); // (3800, 100 mils) // USB Type-B Jack & DC Power Jack - drawMetalConnector(g2d, boardX - 10, boardY + 40, 95, 80, "USB"); - drawChip(g2d, boardX - 10, boardY + boardH - 115, 105, 75, "DC IN"); + drawMetalConnector(g2d, boardX - 28, boardY + 75, 103, 90, "USB"); + drawChip(g2d, boardX - 14, boardY + 325, 104, 70, "DC IN"); // ATmega2560 square QFP chip - drawChip(g2d, boardX + 370, boardY + 165, 90, 90, "m2560"); + drawChip(g2d, boardX + 365, boardY + 165, 80, 80, "ATmega2560"); // Reset Button near USB g2d.setColor(Color.decode("#CC3333")); - g2d.fill(new Ellipse2D.Double(boardX + 45, boardY + 135, 18, 18)); + g2d.fill(new Ellipse2D.Double(boardX + 38, boardY + 20, 18, 18)); g2d.setColor(Color.WHITE); g2d.setFont(SILK_FONT_SMALL); - StringUtils.drawCenteredText(g2d, "RST", boardX + 45, boardY + 160, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "RST", boardX + 47, boardY + 48, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + + // Arduino Infinity Logo + drawArduinoLogo(g2d, boardX + 240, boardY + 80); // Silkscreen text & branding g2d.setColor(Color.WHITE); g2d.setFont(SILK_FONT_LARGE); - StringUtils.drawCenteredText(g2d, "ARDUINO MEGA 2560", boardX + 380, boardY + 100, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "ARDUINO", boardX + 288, boardY + 145, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "MEGA 2560", boardX + 288, boardY + 165, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); // Header silkscreen labels g2d.setFont(SILK_FONT_SMALL); - StringUtils.drawCenteredText(g2d, "POWER", boardX + 290, boardY + 375, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - StringUtils.drawCenteredText(g2d, "ANALOG IN", boardX + 540, boardY + 375, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - StringUtils.drawCenteredText(g2d, "DIGITAL", boardX + 350, boardY + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - StringUtils.drawCenteredText(g2d, "COMMUNICATION", boardX + 620, boardY + 45, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "POWER", boardX + 290, boardY + 370, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "ANALOG IN", boardX + 630, boardY + 370, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "DIGITAL (PWM ~)", boardX + 370, boardY + 55, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "COMMUNICATION", boardX + 610, boardY + 55, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); } g2d.setTransform(oldTx); + // Draw header pins with continuity tracking drawPins(g2d, 0, controlPoints.length, true, outlineMode, drawingObserver); g2d.setComposite(oldComposite); @@ -243,19 +288,56 @@ public class ArduinoMega extends AbstractMakerBoard { @Override public void drawIcon(Graphics2D g2d, int width, int height) { + double boardX = 5; + double boardY = 3; + double boardW = width - boardX - 2; + double boardH = height - 6; + + // USB Type-B Jack (metallic silver, top left protruding) + double usbW = 7; + double usbH = 8; + double usbX = 1; + double usbY = boardY + 2; + + // DC Power Jack (dark body, bottom left protruding) + double dcW = 7; + double dcH = 6; + double dcX = 1; + double dcY = boardY + boardH - dcH - 3; + + // Board PCB g2d.setColor(ARDUINO_BLUE); - g2d.fill(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 4, 4)); + g2d.fill(new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4)); g2d.setColor(ARDUINO_BLUE.darker()); - g2d.draw(new RoundRectangle2D.Double(2, 6, width - 4, height - 12, 4, 4)); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + g2d.draw(new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4)); + // Draw USB Jack g2d.setColor(USB_METAL_COLOR); - g2d.fillRect(3, 8, 5, 5); + g2d.fill(new RoundRectangle2D.Double(usbX, usbY, usbW, usbH, 2, 2)); + g2d.setColor(METAL_SHIELD_BORDER); + g2d.draw(new RoundRectangle2D.Double(usbX, usbY, usbW, usbH, 2, 2)); + // Draw DC Jack g2d.setColor(IC_BODY_COLOR); - g2d.fillRect(16, 11, 8, 8); + g2d.fill(new RoundRectangle2D.Double(dcX, dcY, dcW, dcH, 2, 2)); + g2d.setColor(Color.BLACK); + g2d.draw(new RoundRectangle2D.Double(dcX, dcY, dcW, dcH, 2, 2)); + // Arduino Infinity logo + double scale = 14.0 / 95.56; + double logoW = 95.56 * scale; + double logoH = 45.33 * scale; + double logoX = boardX + (boardW - logoW) / 2.0 + 1.0; + double logoY = boardY + 3.5; + drawArduinoLogo(g2d, logoX, logoY, scale); + + // MEGA text below logo g2d.setColor(Color.WHITE); - g2d.setFont(new Font("SansSerif", Font.BOLD, 6)); - StringUtils.drawCenteredText(g2d, "MEGA", width / 2 + 3, height / 2 + 1, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + int fontSize = Math.max(6, (int) Math.round(boardH * 0.26)); + g2d.setFont(new Font("SansSerif", Font.BOLD, fontSize)); + double textY = logoY + logoH + (boardY + boardH - (logoY + logoH)) / 2.0; + double textX = boardX + (boardW / 2.0) + 0.5; + StringUtils.drawCenteredText(g2d, "MEGA", textX, textY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); } } diff --git a/diylc/diylc-library/src/test/java/org/diylc/components/micro/ArduinoMegaTest.java b/diylc/diylc-library/src/test/java/org/diylc/components/micro/ArduinoMegaTest.java new file mode 100644 index 00000000..a97701e4 --- /dev/null +++ b/diylc/diylc-library/src/test/java/org/diylc/components/micro/ArduinoMegaTest.java @@ -0,0 +1,152 @@ +package org.diylc.components.micro; + +import java.awt.Shape; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.junit.Assert; +import org.junit.Test; + +public class ArduinoMegaTest { + + @Test + public void testControlPointCountAndNames() { + ArduinoMega mega = new ArduinoMega(); + Assert.assertEquals(98, mega.getControlPointCount()); + + for (int i = 0; i < mega.getControlPointCount(); i++) { + String name = mega.getControlPointNodeName(i); + Assert.assertNotNull("Pin " + i + " name should not be null", name); + Assert.assertFalse("Pin " + i + " name should not be empty", name.trim().isEmpty()); + } + + // Power header (0..7) + Assert.assertEquals("NC", mega.getControlPointNodeName(0)); + Assert.assertEquals("IOREF", mega.getControlPointNodeName(1)); + Assert.assertEquals("RESET", mega.getControlPointNodeName(2)); + Assert.assertEquals("3.3V", mega.getControlPointNodeName(3)); + Assert.assertEquals("5V", mega.getControlPointNodeName(4)); + Assert.assertEquals("GND1", mega.getControlPointNodeName(5)); + Assert.assertEquals("GND2", mega.getControlPointNodeName(6)); + Assert.assertEquals("VIN", mega.getControlPointNodeName(7)); + + // Analog Low header (8..15) + Assert.assertEquals("A0", mega.getControlPointNodeName(8)); + Assert.assertEquals("A7", mega.getControlPointNodeName(15)); + + // Analog High header (16..23) + Assert.assertEquals("A8", mega.getControlPointNodeName(16)); + Assert.assertEquals("A15", mega.getControlPointNodeName(23)); + + // Digital low header (24..31) + Assert.assertEquals("D0 (RX0)", mega.getControlPointNodeName(24)); + Assert.assertEquals("D7 (~)", mega.getControlPointNodeName(31)); + + // Digital high header (32..41) + Assert.assertEquals("D8 (~)", mega.getControlPointNodeName(32)); + Assert.assertEquals("SCL", mega.getControlPointNodeName(41)); + + // Communication header (42..49) + Assert.assertEquals("D14 (TX3)", mega.getControlPointNodeName(42)); + Assert.assertEquals("D21 (SCL)", mega.getControlPointNodeName(49)); + + // Double Digital 2x18 Header (50..85) + Assert.assertEquals("D22", mega.getControlPointNodeName(50)); + Assert.assertEquals("D23", mega.getControlPointNodeName(51)); + Assert.assertEquals("D52 (SCK)", mega.getControlPointNodeName(80)); + Assert.assertEquals("D53 (SS)", mega.getControlPointNodeName(81)); + Assert.assertEquals("GND_EXT1", mega.getControlPointNodeName(82)); + Assert.assertEquals("GND_EXT2", mega.getControlPointNodeName(83)); + Assert.assertEquals("5V_EXT1", mega.getControlPointNodeName(84)); + Assert.assertEquals("5V_EXT2", mega.getControlPointNodeName(85)); + + // Main ICSP header (86..91, ATmega2560) + Assert.assertEquals("MISO", mega.getControlPointNodeName(86)); + Assert.assertEquals("5V_ICSP", mega.getControlPointNodeName(87)); + Assert.assertEquals("SCK", mega.getControlPointNodeName(88)); + Assert.assertEquals("MOSI", mega.getControlPointNodeName(89)); + Assert.assertEquals("RST_ICSP", mega.getControlPointNodeName(90)); + Assert.assertEquals("GND_ICSP", mega.getControlPointNodeName(91)); + + // Top-left ICSP header (92..97, ATmega16U2) + Assert.assertEquals("MISO_16U2", mega.getControlPointNodeName(92)); + Assert.assertEquals("5V_16U2", mega.getControlPointNodeName(93)); + Assert.assertEquals("SCK_16U2", mega.getControlPointNodeName(94)); + Assert.assertEquals("MOSI_16U2", mega.getControlPointNodeName(95)); + Assert.assertEquals("RST_16U2", mega.getControlPointNodeName(96)); + Assert.assertEquals("GND_16U2", mega.getControlPointNodeName(97)); + } + + @Test + public void testPinGeometryAndGaps() { + ArduinoMega mega = new ArduinoMega(); + + // Power header spacing (20px per pin) + for (int i = 0; i < 7; i++) { + Point2D p1 = mega.getControlPoint(i); + Point2D p2 = mega.getControlPoint(i + 1); + Assert.assertEquals(20.0, p1.distance(p2), 0.01); + Assert.assertEquals(p1.getY(), p2.getY(), 0.01); + } + + // Gap between VIN (Pin 7) and A0 (Pin 8) is 0.2" = 40px + Point2D pVin = mega.getControlPoint(7); + Point2D pA0 = mega.getControlPoint(8); + Assert.assertEquals(40.0, pVin.distance(pA0), 0.01); + + // Gap between A7 (Pin 15) and A8 (Pin 16) is 0.2" = 40px + Point2D pA7 = mega.getControlPoint(15); + Point2D pA8 = mega.getControlPoint(16); + Assert.assertEquals(40.0, pA7.distance(pA8), 0.01); + + // D0 aligns with A5 (dx = 280 from NC) + Point2D pD0 = mega.getControlPoint(24); + Point2D pA5 = mega.getControlPoint(13); + Assert.assertEquals(pA5.getX(), pD0.getX(), 0.01); + + // D7 aligns with VIN (dx = 140 from NC) + Point2D pD7 = mega.getControlPoint(31); + Assert.assertEquals(pVin.getX(), pD7.getX(), 0.01); + + // Gap between D7 (Pin 31) and D8 (Pin 32) is 0.16" = 32px + Point2D pD8 = mega.getControlPoint(32); + Assert.assertEquals(32.0, pD7.distance(pD8), 0.01); + + // D14 aligns with A7 (dx = 320 from NC) + Point2D pD14 = mega.getControlPoint(42); + Assert.assertEquals(pA7.getX(), pD14.getX(), 0.01); + + // Gap between D0 (Pin 24) and D14 (Pin 42) is 0.2" = 40px + Assert.assertEquals(40.0, pD0.distance(pD14), 0.01); + + // Double Digital 2x18 Header (Pin 50 inner, Pin 51 outer) + Point2D pNc = mega.getControlPoint(0); + Point2D pD22 = mega.getControlPoint(50); + Point2D pD23 = mega.getControlPoint(51); + Assert.assertEquals(20.0, pD22.distance(pD23), 0.01); + Assert.assertEquals(pD22.getY(), pD23.getY(), 0.01); + Assert.assertEquals(500.0, pD22.getX() - pNc.getX(), 0.01); + Assert.assertEquals(520.0, pD23.getX() - pNc.getX(), 0.01); + + // Main ICSP header (Pin 86 MISO, Pin 87 5V_ICSP) + Point2D pMiso = mega.getControlPoint(86); + Point2D p5vIcsp = mega.getControlPoint(87); + Assert.assertEquals(20.0, pMiso.distance(p5vIcsp), 0.01); + + // Top-left ICSP header (Pin 92 MISO_16U2, Pin 93 5V_16U2) + Point2D pMiso16 = mega.getControlPoint(92); + Point2D p5v16 = mega.getControlPoint(93); + Assert.assertEquals(20.0, pMiso16.distance(p5v16), 0.01); + } + + @Test + public void testBodyShape() { + ArduinoMega mega = new ArduinoMega(); + + Shape body = mega.getBodyShape(); + Assert.assertNotNull(body); + Rectangle2D bounds = body.getBounds2D(); + Assert.assertEquals(799.78, bounds.getWidth(), 1.0); + Assert.assertEquals(419.78, bounds.getHeight(), 1.0); + } +}