mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
more components
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.displays;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "NeoPixel Ring (WS2812B)", category = "Displays & Outputs",
|
||||
author = "Branislav Stojkovic", description = "Addressable RGB WS2812B NeoPixel Ring (12/16/24 LEDs)",
|
||||
instanceNamePrefix = "LED", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class WS2812BRing extends AbstractMakerBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public enum RingSize {
|
||||
_12_LED("12 LEDs (37mm OD)", 12, 37.0, 27.0),
|
||||
_16_LED("16 LEDs (44.5mm OD)", 16, 44.5, 32.0),
|
||||
_24_LED("24 LEDs (66mm OD)", 24, 66.0, 52.0);
|
||||
|
||||
private final String label;
|
||||
private final int ledCount;
|
||||
private final double outerDiameterMm;
|
||||
private final double innerDiameterMm;
|
||||
|
||||
RingSize(String label, int ledCount, double outerDiameterMm, double innerDiameterMm) {
|
||||
this.label = label;
|
||||
this.ledCount = ledCount;
|
||||
this.outerDiameterMm = outerDiameterMm;
|
||||
this.innerDiameterMm = innerDiameterMm;
|
||||
}
|
||||
|
||||
@Override public String toString() { return label; }
|
||||
public int getLedCount() { return ledCount; }
|
||||
public double getOuterDiameterMm() { return outerDiameterMm; }
|
||||
public double getInnerDiameterMm() { return innerDiameterMm; }
|
||||
}
|
||||
|
||||
// Colors shared with WS2812BStick
|
||||
public static Color NEO_BLACK = Color.decode("#111111");
|
||||
public static Color LED_PACKAGE = Color.decode("#FDFEFE");
|
||||
public static Color LED_BORDER = Color.decode("#D0D3D4");
|
||||
public static Color LED_DIFFUSER = Color.decode("#EAECEE");
|
||||
public static Color DIFFUSER_BORDER = Color.decode("#BDC3C7");
|
||||
public static Color CHIP_DOT_COLOR = Color.decode("#333333");
|
||||
public static Color SOLDER_PAD_COLOR = Color.decode("#D4AC0D");
|
||||
public static Color SOLDER_PAD_BORDER = Color.decode("#9A7D0A");
|
||||
|
||||
private static final String[] PIN_NAMES = {"DIN", "+5V", "GND", "DOUT"};
|
||||
|
||||
private RingSize ringSize = RingSize._16_LED;
|
||||
|
||||
public WS2812BRing() {
|
||||
super();
|
||||
this.bodyColor = NEO_BLACK;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Ring Size")
|
||||
public RingSize getRingSize() {
|
||||
return ringSize;
|
||||
}
|
||||
|
||||
public void setRingSize(RingSize ringSize) {
|
||||
this.ringSize = ringSize;
|
||||
updateControlPoints();
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
if (index >= 0 && index < PIN_NAMES.length) {
|
||||
return PIN_NAMES[index];
|
||||
}
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
private double getMidRadius() {
|
||||
double outerR = new Size(ringSize.getOuterDiameterMm() / 2.0, SizeUnit.mm).convertToPixels();
|
||||
double innerR = new Size(ringSize.getInnerDiameterMm() / 2.0, SizeUnit.mm).convertToPixels();
|
||||
return (outerR + innerR) / 2.0;
|
||||
}
|
||||
|
||||
private double getPadAngle(int index) {
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double midR = getMidRadius();
|
||||
double deltaTheta = spacing / midR;
|
||||
// 4 pads placed symmetrically at the bottom of the circular ring (near angle PI / 2)
|
||||
return Math.PI / 2.0 + (index - 1.5) * deltaTheta;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
double midR = getMidRadius();
|
||||
double theta0 = getPadAngle(0);
|
||||
|
||||
double[][] relativeOffsets = new double[4][2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double theta = getPadAngle(i);
|
||||
relativeOffsets[i][0] = midR * (Math.cos(theta) - Math.cos(theta0));
|
||||
relativeOffsets[i][1] = midR * (Math.sin(theta) - Math.sin(theta0));
|
||||
}
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
private Point2D getCenter() {
|
||||
Point2D p0 = controlPoints[0];
|
||||
double midR = getMidRadius();
|
||||
double theta0 = getPadAngle(0);
|
||||
// When unrotated, center is at p0 - (midR*cos(theta0), midR*sin(theta0))
|
||||
return new Point2D.Double(p0.getX() - midR * Math.cos(theta0), p0.getY() - midR * Math.sin(theta0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getBodyShape() {
|
||||
Point2D center = getCenter();
|
||||
double outerR = new Size(ringSize.getOuterDiameterMm() / 2.0, SizeUnit.mm).convertToPixels();
|
||||
return new Ellipse2D.Double(center.getX() - outerR, center.getY() - outerR, outerR * 2, outerR * 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
Point2D center = getCenter();
|
||||
double cx = center.getX();
|
||||
double cy = center.getY();
|
||||
|
||||
double outerR = new Size(ringSize.getOuterDiameterMm() / 2.0, SizeUnit.mm).convertToPixels();
|
||||
double innerR = new Size(ringSize.getInnerDiameterMm() / 2.0, SizeUnit.mm).convertToPixels();
|
||||
double midR = (outerR + innerR) / 2.0;
|
||||
|
||||
// Annular ring shape: outer circle minus inner circle
|
||||
Area outerArea = new Area(new Ellipse2D.Double(cx - outerR, cy - outerR, outerR * 2, outerR * 2));
|
||||
Area innerArea = new Area(new Ellipse2D.Double(cx - innerR, cy - innerR, innerR * 2, innerR * 2));
|
||||
outerArea.subtract(innerArea);
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
drawingObserver.startTracking();
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
|
||||
g2d.fill(outerArea);
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
g2d.setColor(getFinalBorderColor(componentState, outlineMode));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
|
||||
g2d.draw(outerArea);
|
||||
|
||||
if (!outlineMode) {
|
||||
int ledCount = ringSize.getLedCount();
|
||||
double ledSize = Math.max(8.0, Math.min(15.0, (2 * Math.PI * midR / ledCount) * 0.60));
|
||||
double halfLed = ledSize / 2.0;
|
||||
|
||||
// Draw all LEDs around the circular ring
|
||||
for (int i = 0; i < ledCount; i++) {
|
||||
double angle = 2 * Math.PI * i / ledCount - Math.PI / 2.0;
|
||||
double lx = cx + midR * Math.cos(angle);
|
||||
double ly = cy + midR * Math.sin(angle);
|
||||
|
||||
// 5050 White Package
|
||||
g2d.setColor(LED_PACKAGE);
|
||||
g2d.fill(new RoundRectangle2D.Double(lx - halfLed, ly - halfLed, ledSize, ledSize, 2, 2));
|
||||
g2d.setColor(LED_BORDER);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(0.5f));
|
||||
g2d.draw(new RoundRectangle2D.Double(lx - halfLed, ly - halfLed, ledSize, ledSize, 2, 2));
|
||||
|
||||
// Milky phosphor lens
|
||||
double diffR = halfLed * 0.7;
|
||||
g2d.setColor(LED_DIFFUSER);
|
||||
g2d.fill(new Ellipse2D.Double(lx - diffR, ly - diffR, diffR * 2, diffR * 2));
|
||||
g2d.setColor(DIFFUSER_BORDER);
|
||||
g2d.draw(new Ellipse2D.Double(lx - diffR, ly - diffR, diffR * 2, diffR * 2));
|
||||
|
||||
// Tiny IC dot
|
||||
g2d.setColor(CHIP_DOT_COLOR);
|
||||
double dotS = Math.max(2.0, diffR * 0.4);
|
||||
g2d.fill(new Rectangle2D.Double(lx - dotS / 2.0, ly - dotS / 2.0, dotS, dotS));
|
||||
}
|
||||
|
||||
// Center silkscreen text
|
||||
g2d.setColor(Color.decode("#666666"));
|
||||
g2d.setFont(SILK_FONT_SMALL);
|
||||
StringUtils.drawCenteredText(g2d, "NeoPixel", cx, cy - 6, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, ringSize.getLedCount() + "x LED", cx, cy + 6, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// Silkscreen labels next to the 4 solder pads on the ring
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 8));
|
||||
String[] labels = new String[] {"DIN", "5V", "GND", "DOUT"};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double theta = getPadAngle(i);
|
||||
double labelR = innerR + 5;
|
||||
double px = cx + labelR * Math.cos(theta);
|
||||
double py = cy + labelR * Math.sin(theta);
|
||||
StringUtils.drawCenteredText(g2d, labels[i], px, py, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
|
||||
// Draw the 4 solder pads directly on the circular ring PCB
|
||||
drawSolderPads(g2d, 0, 4, outlineMode, drawingObserver);
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
int cx = width / 2;
|
||||
int cy = height / 2;
|
||||
int outerR = width / 2 - 2;
|
||||
int innerR = outerR / 2;
|
||||
|
||||
Area outer = new Area(new Ellipse2D.Double(cx - outerR, cy - outerR, outerR * 2, outerR * 2));
|
||||
Area inner = new Area(new Ellipse2D.Double(cx - innerR, cy - innerR, innerR * 2, innerR * 2));
|
||||
outer.subtract(inner);
|
||||
g2d.setColor(NEO_BLACK);
|
||||
g2d.fill(outer);
|
||||
g2d.setColor(Color.DARK_GRAY);
|
||||
g2d.draw(outer);
|
||||
|
||||
Color[] rainbow = new Color[] {
|
||||
Color.decode("#FF3333"),
|
||||
Color.decode("#FFD700"),
|
||||
Color.decode("#00E676"),
|
||||
Color.decode("#00E5FF"),
|
||||
Color.decode("#E040FB"),
|
||||
Color.decode("#FF6B35")
|
||||
};
|
||||
|
||||
double midR = (outerR + innerR) / 2.0;
|
||||
int dotR = Math.max(2, (int)(midR * 0.35));
|
||||
int dotCount = 6;
|
||||
|
||||
for (int i = 0; i < dotCount; i++) {
|
||||
double angle = 2 * Math.PI * i / dotCount - Math.PI / 2.0;
|
||||
double lx = cx + midR * Math.cos(angle);
|
||||
double ly = cy + midR * Math.sin(angle);
|
||||
g2d.setColor(rainbow[i % rainbow.length]);
|
||||
g2d.fill(new Ellipse2D.Double(lx - dotR, ly - dotR, dotR * 2, dotR * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.electromechanical;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "18650 Battery Holder", category = "Electro-Mechanical",
|
||||
author = "Branislav Stojkovic", description = "PCB-mount 18650 Li-ion Battery Holder (1S or 2S)",
|
||||
instanceNamePrefix = "BTR", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class BatteryHolder18650 extends AbstractTransparentComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public enum CellCount {
|
||||
_1S("1S (3.7V) — Single Cell", 1, 77.0, 21.0),
|
||||
_2S("2S (7.4V) — Two Cells", 2, 77.0, 42.0);
|
||||
|
||||
private final String label;
|
||||
private final int cells;
|
||||
private final double lengthMm;
|
||||
private final double widthMm;
|
||||
|
||||
CellCount(String label, int cells, double lengthMm, double widthMm) {
|
||||
this.label = label;
|
||||
this.cells = cells;
|
||||
this.lengthMm = lengthMm;
|
||||
this.widthMm = widthMm;
|
||||
}
|
||||
|
||||
@Override public String toString() { return label; }
|
||||
public int getCells() { return cells; }
|
||||
public double getLengthMm() { return lengthMm; }
|
||||
public double getWidthMm() { return widthMm; }
|
||||
}
|
||||
|
||||
public static Color HOLDER_BODY = Color.decode("#2C2C2C");
|
||||
public static Color HOLDER_BODY_LIGHT = Color.decode("#3C3C3C");
|
||||
public static Color SPRING_COLOR = Color.decode("#C0C0C0");
|
||||
public static Color SPRING_BORDER = Color.decode("#909090");
|
||||
public static Color PLATE_COLOR = Color.decode("#D0D0D0");
|
||||
public static Color CELL_CAVITY = Color.decode("#1A1A1A");
|
||||
|
||||
private static final Font LABEL_FONT = new Font("SansSerif", Font.BOLD, 12);
|
||||
|
||||
private String value = "";
|
||||
private CellCount cellCount = CellCount._1S;
|
||||
private Orientation orientation = Orientation.DEFAULT;
|
||||
|
||||
// Two control points: Point 0 = + (left), Point 1 = - (right)
|
||||
private Point2D[] controlPoints = new Point2D[] {
|
||||
new Point2D.Double(0, 0),
|
||||
new Point2D.Double(0, 0)
|
||||
};
|
||||
|
||||
public BatteryHolder18650() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Cell Count")
|
||||
public CellCount getCellCount() {
|
||||
return cellCount;
|
||||
}
|
||||
|
||||
public void setCellCount(CellCount cellCount) {
|
||||
this.cellCount = cellCount;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Orientation")
|
||||
public Orientation getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
this.orientation = orientation;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
private void updateControlPoints() {
|
||||
double length = new Size(cellCount.getLengthMm(), SizeUnit.mm).convertToPixels();
|
||||
Point2D p0 = controlPoints[0];
|
||||
|
||||
double theta = orientation.toRadians();
|
||||
// + at p0 (left), - at p0 + (length, 0) rotated (right)
|
||||
double dx = Math.cos(theta) * length;
|
||||
double dy = Math.sin(theta) * length;
|
||||
controlPoints[1] = new Point2D.Double(p0.getX() + dx, p0.getY() + dy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() { return 2; }
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) { return controlPoints[index]; }
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
controlPoints[index].setLocation(point);
|
||||
if (index == 0) {
|
||||
updateControlPoints();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) { return true; }
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.WHEN_SELECTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) { return false; }
|
||||
|
||||
@Override
|
||||
public boolean canControlPointOverlap(int index) { return false; }
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return index == 0 ? "+ (VCC)" : "- (GND)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() { return value; }
|
||||
|
||||
@Override
|
||||
public void setValue(String value) { this.value = value; }
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
double length = new Size(cellCount.getLengthMm(), SizeUnit.mm).convertToPixels();
|
||||
double width = new Size(cellCount.getWidthMm(), SizeUnit.mm).convertToPixels();
|
||||
double cellD = new Size(18.5d, SizeUnit.mm).convertToPixels();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
// Border color
|
||||
Color borderColor;
|
||||
if (outlineMode) {
|
||||
Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
|
||||
borderColor = (componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING)
|
||||
? SELECTION_COLOR : (theme != null ? theme.getOutlineColor() : Color.BLACK);
|
||||
} else {
|
||||
borderColor = (componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING)
|
||||
? SELECTION_COLOR : HOLDER_BODY.darker();
|
||||
}
|
||||
|
||||
double bodyY = y - width / 2.0;
|
||||
|
||||
drawingObserver.startTracking();
|
||||
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : HOLDER_BODY);
|
||||
g2d.fill(new RoundRectangle2D.Double(x, bodyY, length, width, 10, 10));
|
||||
drawingObserver.stopTracking();
|
||||
|
||||
g2d.setColor(borderColor);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
|
||||
g2d.draw(new RoundRectangle2D.Double(x, bodyY, length, width, 10, 10));
|
||||
|
||||
if (!outlineMode) {
|
||||
int cells = cellCount.getCells();
|
||||
double cellWidth = (width - 8.0) / cells;
|
||||
|
||||
for (int i = 0; i < cells; i++) {
|
||||
double cavityX = x + 6;
|
||||
double cavityY = bodyY + 4 + i * cellWidth;
|
||||
double cavityW = length - 12;
|
||||
double cavityH = cellWidth - 4;
|
||||
|
||||
// Cell cavity (dark inset)
|
||||
g2d.setColor(CELL_CAVITY);
|
||||
g2d.fill(new RoundRectangle2D.Double(cavityX, cavityY, cavityW, cavityH, 6, 6));
|
||||
g2d.setColor(HOLDER_BODY_LIGHT);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new RoundRectangle2D.Double(cavityX, cavityY, cavityW, cavityH, 6, 6));
|
||||
|
||||
// Positive flat plate button contact on left (x)
|
||||
double plateX = x + 6;
|
||||
double plateW = 6;
|
||||
double plateH = Math.min(14, cellH(cellD));
|
||||
double contactY = cavityY + cavityH / 2.0;
|
||||
g2d.setColor(PLATE_COLOR);
|
||||
g2d.fill(new Rectangle2D.Double(plateX, contactY - plateH / 2.0, plateW, plateH));
|
||||
g2d.setColor(SPRING_BORDER);
|
||||
g2d.draw(new Rectangle2D.Double(plateX, contactY - plateH / 2.0, plateW, plateH));
|
||||
|
||||
// Negative spring contact on right (x + length)
|
||||
double springX = x + length - 16;
|
||||
double springW = 10;
|
||||
double springH = Math.min(14, cellH(cellD));
|
||||
g2d.setColor(SPRING_COLOR);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
|
||||
g2d.draw(new Rectangle2D.Double(springX, contactY - springH / 2.0, springW, springH));
|
||||
g2d.draw(new Rectangle2D.Double(springX + 3, contactY - springH / 2.0 + 2, springW - 6, springH - 4));
|
||||
|
||||
// Polarity markings (+ on left, - on right)
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(LABEL_FONT);
|
||||
StringUtils.drawCenteredText(g2d, "+", x + 20, contactY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "-", x + length - 20, contactY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// 18650 text in cavity
|
||||
g2d.setColor(Color.decode("#555555"));
|
||||
StringUtils.drawCenteredText(g2d, "18650", x + length / 2.0, contactY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
// Series connector bar between cells for 2S
|
||||
if (cells > 1) {
|
||||
g2d.setColor(SPRING_COLOR);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2f));
|
||||
double midY = bodyY + width / 2.0;
|
||||
g2d.draw(new Rectangle2D.Double(x + length - 10, midY - 6, 6, 12));
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
|
||||
// Draw control point solder tabs (Point 0 = + left, Point 1 = - right)
|
||||
if (!outlineMode) {
|
||||
for (Point2D cp : controlPoints) {
|
||||
g2d.setColor(PLATE_COLOR);
|
||||
g2d.fill(new Ellipse2D.Double(cp.getX() - 5, cp.getY() - 5, 10, 10));
|
||||
g2d.setColor(SPRING_BORDER);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new Ellipse2D.Double(cp.getX() - 5, cp.getY() - 5, 10, 10));
|
||||
// Inner hole
|
||||
g2d.setColor(Constants.CANVAS_COLOR);
|
||||
g2d.fill(new Ellipse2D.Double(cp.getX() - 2, cp.getY() - 2, 4, 4));
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
private double cellH(double cellD) {
|
||||
return cellD * 0.45;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
// Dark holder body
|
||||
g2d.setColor(HOLDER_BODY);
|
||||
g2d.fill(new RoundRectangle2D.Double(2, height / 2.0 - 8, width - 4, 16, 4, 4));
|
||||
g2d.setColor(HOLDER_BODY.brighter());
|
||||
g2d.draw(new RoundRectangle2D.Double(2, height / 2.0 - 8, width - 4, 16, 4, 4));
|
||||
|
||||
// Cell cavity
|
||||
g2d.setColor(CELL_CAVITY);
|
||||
g2d.fill(new RoundRectangle2D.Double(5, height / 2.0 - 6, width - 10, 12, 3, 3));
|
||||
|
||||
// Plate on left (+), spring on right (-)
|
||||
g2d.setColor(PLATE_COLOR);
|
||||
g2d.fillRect(5, height / 2 - 3, 3, 6);
|
||||
g2d.setColor(SPRING_COLOR);
|
||||
g2d.fillRect(width - 9, height / 2 - 3, 4, 6);
|
||||
|
||||
// Polarity
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 7));
|
||||
StringUtils.drawCenteredText(g2d, "+", 12, height / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "-", width - 13, height / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
double minX = Math.min(controlPoints[0].getX(), controlPoints[1].getX());
|
||||
double minY = Math.min(controlPoints[0].getY(), controlPoints[1].getY());
|
||||
double maxX = Math.max(controlPoints[0].getX(), controlPoints[1].getX());
|
||||
double maxY = Math.max(controlPoints[0].getY(), controlPoints[1].getY());
|
||||
double width = new Size(cellCount.getWidthMm(), SizeUnit.mm).convertToPixels();
|
||||
int margin = 50;
|
||||
return new Rectangle2D.Double(minX - margin, minY - width / 2.0 - margin,
|
||||
(maxX - minX) + 2 * margin, (maxY - minY) + width + 2 * margin);
|
||||
}
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.modules;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Active Buzzer Module (KY-012)", category = "Modules & Breakouts",
|
||||
author = "Branislav Stojkovic", description = "KY-012 Active Buzzer Module — produces 2.5kHz continuous tone on HIGH signal",
|
||||
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class ActiveBuzzerModule extends AbstractMakerBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color PCB_GREEN = Color.decode("#1A6B1A");
|
||||
public static Color BUZZER_BLACK = Color.decode("#1A1A1A");
|
||||
public static Color BUZZER_VENT_COLOR = Color.decode("#333333");
|
||||
|
||||
public static Size BOARD_WIDTH = new Size(18.5d, SizeUnit.mm);
|
||||
public static Size BOARD_HEIGHT = new Size(15.0d, SizeUnit.mm);
|
||||
|
||||
// Standard Keyes KY-012 3-pin layout from left to right: - (GND), + (VCC), S (Signal)
|
||||
private static final String[] PIN_NAMES = {"- (GND)", "+ (VCC)", "S (Signal)"};
|
||||
|
||||
public ActiveBuzzerModule() {
|
||||
super();
|
||||
this.bodyColor = PCB_GREEN;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
if (index >= 0 && index < PIN_NAMES.length) {
|
||||
return PIN_NAMES[index];
|
||||
}
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
|
||||
// 3 pins along the bottom edge: - (GND), + (VCC), S (Signal)
|
||||
double[][] relativeOffsets = new double[][] {
|
||||
{ 0, 0 },
|
||||
{ spacing, 0 },
|
||||
{ spacing * 2, 0 }
|
||||
};
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getBodyShape() {
|
||||
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 - (boardW - 2 * PIN_SPACING.convertToPixels()) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double boardX = x - (boardW - 2 * PIN_SPACING.convertToPixels()) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
|
||||
Shape boardShape = getBodyShape();
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
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) {
|
||||
// Buzzer cylinder — circular element on the board
|
||||
double buzzerD = new Size(11.0d, SizeUnit.mm).convertToPixels();
|
||||
double buzzerX = boardX + (boardW - buzzerD) / 2.0;
|
||||
double buzzerY = boardY + 4;
|
||||
|
||||
// Outer black cylinder
|
||||
g2d.setColor(BUZZER_BLACK);
|
||||
g2d.fill(new Ellipse2D.Double(buzzerX, buzzerY, buzzerD, buzzerD));
|
||||
g2d.setColor(BUZZER_BLACK.brighter());
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
|
||||
g2d.draw(new Ellipse2D.Double(buzzerX, buzzerY, buzzerD, buzzerD));
|
||||
|
||||
// Vent hole ring (inner concentric)
|
||||
double ventD = buzzerD * 0.55;
|
||||
double ventX = buzzerX + (buzzerD - ventD) / 2.0;
|
||||
double ventY = buzzerY + (buzzerD - ventD) / 2.0;
|
||||
g2d.setColor(BUZZER_VENT_COLOR);
|
||||
g2d.draw(new Ellipse2D.Double(ventX, ventY, ventD, ventD));
|
||||
|
||||
// Small central sound vent hole
|
||||
double holeD = buzzerD * 0.15;
|
||||
double holeX = buzzerX + (buzzerD - holeD) / 2.0;
|
||||
double holeY = buzzerY + (buzzerD - holeD) / 2.0;
|
||||
g2d.setColor(Color.decode("#111111"));
|
||||
g2d.fill(new Ellipse2D.Double(holeX, holeY, holeD, holeD));
|
||||
|
||||
// Polarity "+" marking on buzzer casing
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 10));
|
||||
StringUtils.drawCenteredText(g2d, "+", buzzerX + 6, buzzerY + 7, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// "KY-012" model text in top-left silkscreen corner
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 8));
|
||||
StringUtils.drawCenteredText(g2d, "KY-012", boardX + 14, boardY + 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// Silkscreen pin labels (-, +, S) directly above the 3 header pins
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 9));
|
||||
double labelY = y - 9;
|
||||
StringUtils.drawCenteredText(g2d, "-", x, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "+", x + spacing, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "S", x + spacing * 2, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
// Green PCB
|
||||
g2d.setColor(PCB_GREEN);
|
||||
g2d.fill(new RoundRectangle2D.Double(3, 5, width - 6, height - 10, 3, 3));
|
||||
g2d.setColor(PCB_GREEN.darker());
|
||||
g2d.draw(new RoundRectangle2D.Double(3, 5, width - 6, height - 10, 3, 3));
|
||||
|
||||
// Black buzzer cylinder
|
||||
int bD = width - 10;
|
||||
int bX = (width - bD) / 2;
|
||||
int bY = 5;
|
||||
g2d.setColor(BUZZER_BLACK);
|
||||
g2d.fillOval(bX, bY, bD, bD);
|
||||
g2d.setColor(Color.DARK_GRAY);
|
||||
g2d.drawOval(bX, bY, bD, bD);
|
||||
|
||||
// Vent circle
|
||||
int vD = bD / 2;
|
||||
g2d.setColor(BUZZER_VENT_COLOR);
|
||||
g2d.drawOval(bX + (bD - vD) / 2, bY + (bD - vD) / 2, vD, vD);
|
||||
|
||||
// 3 pin dots
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
g2d.fillRect(6 + i * 4, height - 5, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.modules;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "GPS Module (NEO-6M)", category = "Modules & Breakouts",
|
||||
author = "Branislav Stojkovic", description = "u-blox NEO-6M GPS Module with ceramic patch antenna",
|
||||
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class GPSModuleNEO6M extends AbstractMakerBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color PCB_GREEN = Color.decode("#1A6B1A");
|
||||
public static Color ANTENNA_BEIGE = Color.decode("#D5C4A1");
|
||||
public static Color ANTENNA_BORDER = Color.decode("#928374");
|
||||
public static Color SHIELD_COLOR = Color.decode("#B8B8B8");
|
||||
public static Color SHIELD_BORDER = Color.decode("#888888");
|
||||
public static Color BATTERY_COLOR = Color.decode("#D0D3D4");
|
||||
|
||||
public static Size BOARD_WIDTH = new Size(35.5d, SizeUnit.mm);
|
||||
public static Size BOARD_HEIGHT = new Size(25.5d, SizeUnit.mm);
|
||||
|
||||
// Standard GY-GPS6MV2 pinout from pin 1 to 4: VCC, RX, TX, GND
|
||||
private static final String[] PIN_NAMES = {"VCC", "RX", "TX", "GND"};
|
||||
|
||||
public GPSModuleNEO6M() {
|
||||
super();
|
||||
this.bodyColor = PCB_GREEN;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
if (index >= 0 && index < PIN_NAMES.length) {
|
||||
return PIN_NAMES[index];
|
||||
}
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
|
||||
// 4 pins along the left edge: VCC, RX, TX, GND
|
||||
double[][] relativeOffsets = new double[][] {
|
||||
{ 0, 0 },
|
||||
{ 0, spacing },
|
||||
{ 0, spacing * 2 },
|
||||
{ 0, spacing * 3 }
|
||||
};
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getBodyShape() {
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double boardX = x - 14.0;
|
||||
double boardY = y - (boardH - 3 * spacing) / 2.0;
|
||||
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double boardX = x - 14.0;
|
||||
double boardY = y - (boardH - 3 * spacing) / 2.0;
|
||||
|
||||
Shape boardShape = getBodyShape();
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
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) {
|
||||
// 4 Small Mounting holes in the corners
|
||||
double holeMargin = 7.0;
|
||||
drawMountingHole(g2d, boardX + holeMargin, boardY + holeMargin, 8.0);
|
||||
drawMountingHole(g2d, boardX + holeMargin, boardY + boardH - holeMargin, 8.0);
|
||||
drawMountingHole(g2d, boardX + boardW - holeMargin, boardY + holeMargin, 8.0);
|
||||
drawMountingHole(g2d, boardX + boardW - holeMargin, boardY + boardH - holeMargin, 8.0);
|
||||
|
||||
// Silkscreen pin labels (VCC, RX, TX, GND) placed neatly next to left header
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 8));
|
||||
String[] pinLabels = new String[] {"VCC", "RX", "TX", "GND"};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
StringUtils.drawCenteredText(g2d, pinLabels[i], x + 9, y + i * spacing, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
// NEO-6M metal shield can (center of board, strictly to the right of pin labels)
|
||||
double icShieldX = boardX + 46.0;
|
||||
double icShieldW = 42.0;
|
||||
double icShieldH = 36.0;
|
||||
double icShieldY = boardY + (boardH - icShieldH) / 2.0 - 8.0;
|
||||
|
||||
g2d.setColor(SHIELD_COLOR);
|
||||
g2d.fill(new RoundRectangle2D.Double(icShieldX, icShieldY, icShieldW, icShieldH, 4, 4));
|
||||
g2d.setColor(SHIELD_BORDER);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new RoundRectangle2D.Double(icShieldX, icShieldY, icShieldW, icShieldH, 4, 4));
|
||||
|
||||
g2d.setColor(Color.decode("#333333"));
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 8));
|
||||
StringUtils.drawCenteredText(g2d, "NEO-6M", icShieldX + icShieldW / 2.0, icShieldY + icShieldH / 2.0 - 4,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
g2d.setFont(new Font("SansSerif", Font.PLAIN, 6));
|
||||
StringUtils.drawCenteredText(g2d, "GPS", icShieldX + icShieldW / 2.0, icShieldY + icShieldH / 2.0 + 5,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// Rechargeable backup coin cell battery (CR1220 / MS621) below shield
|
||||
double battD = 18.0;
|
||||
double battX = boardX + 58.0;
|
||||
double battY = boardY + boardH - battD - 7.0;
|
||||
g2d.setColor(BATTERY_COLOR);
|
||||
g2d.fill(new Ellipse2D.Double(battX, battY, battD, battD));
|
||||
g2d.setColor(BATTERY_COLOR.darker());
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new Ellipse2D.Double(battX, battY, battD, battD));
|
||||
|
||||
// Ceramic patch antenna on right side (20x20mm)
|
||||
double antSize = new Size(18.0d, SizeUnit.mm).convertToPixels();
|
||||
double antX = boardX + boardW - antSize - 8.0;
|
||||
double antY = boardY + (boardH - antSize) / 2.0;
|
||||
|
||||
g2d.setColor(ANTENNA_BEIGE);
|
||||
g2d.fill(new RoundRectangle2D.Double(antX, antY, antSize, antSize, 2, 2));
|
||||
g2d.setColor(ANTENNA_BORDER);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.2f));
|
||||
g2d.draw(new RoundRectangle2D.Double(antX, antY, antSize, antSize, 2, 2));
|
||||
|
||||
// Center silver electrode dot on antenna
|
||||
double antCX = antX + antSize / 2.0;
|
||||
double antCY = antY + antSize / 2.0;
|
||||
g2d.setColor(Color.decode("#7F8C8D"));
|
||||
g2d.fill(new Ellipse2D.Double(antCX - 2, antCY - 2, 4, 4));
|
||||
|
||||
// Status LED (top left area)
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.fill(new Ellipse2D.Double(boardX + 24.0, boardY + 8.0, 5, 5));
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
// Green PCB
|
||||
g2d.setColor(PCB_GREEN);
|
||||
g2d.fill(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
|
||||
g2d.setColor(PCB_GREEN.darker());
|
||||
g2d.draw(new RoundRectangle2D.Double(2, 4, width - 4, height - 8, 3, 3));
|
||||
|
||||
// Beige ceramic patch antenna (right half)
|
||||
int antS = height - 12;
|
||||
g2d.setColor(ANTENNA_BEIGE);
|
||||
g2d.fillRect(width - antS - 3, (height - antS) / 2, antS, antS);
|
||||
g2d.setColor(ANTENNA_BORDER);
|
||||
g2d.drawRect(width - antS - 3, (height - antS) / 2, antS, antS);
|
||||
|
||||
// Shield
|
||||
g2d.setColor(SHIELD_COLOR);
|
||||
g2d.fillRoundRect(4, (height - 10) / 2, 10, 10, 2, 2);
|
||||
}
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.modules;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "Current/Power Sensor (INA219)", category = "Modules & Breakouts",
|
||||
author = "Branislav Stojkovic", description = "INA219 I2C High-Side Current and Power Sensor Breakout",
|
||||
instanceNamePrefix = "MOD", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class INA219CurrentSensor extends AbstractMakerBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color PCB_GREEN = Color.decode("#115E1B");
|
||||
public static Color TERMINAL_BLUE = Color.decode("#1565C0");
|
||||
public static Color TERMINAL_BLUE_BORDER = Color.decode("#0D47A1");
|
||||
public static Color SHUNT_BODY = Color.decode("#7D6608");
|
||||
public static Color SHUNT_METAL = Color.decode("#D4AC0D");
|
||||
public static Color SHUNT_CORE = Color.decode("#2C3E50");
|
||||
public static Color SILK_YELLOW = Color.decode("#F4D03F");
|
||||
|
||||
public static Size BOARD_WIDTH = new Size(25.0d, SizeUnit.mm);
|
||||
public static Size BOARD_HEIGHT = new Size(25.0d, SizeUnit.mm);
|
||||
|
||||
// 6 header pins at bottom (0-5) + 2 screw terminal pins at top (6-7)
|
||||
private static final String[] PIN_NAMES = {
|
||||
"VCC", "GND", "SCL", "SDA", "VIN-", "VIN+",
|
||||
"VIN- (Term)", "VIN+ (Term)"
|
||||
};
|
||||
|
||||
public INA219CurrentSensor() {
|
||||
super();
|
||||
this.bodyColor = PCB_GREEN;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
if (index >= 0 && index < PIN_NAMES.length) {
|
||||
return PIN_NAMES[index];
|
||||
}
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
|
||||
// 8 control points total:
|
||||
// Pins 0-5: Bottom horizontal 6-pin header (VCC, GND, SCL, SDA, VIN-, VIN+)
|
||||
// Pins 6-7: Top 2-pin screw terminal block (VIN-, VIN+)
|
||||
double termSpacing = new Size(5.08d, SizeUnit.mm).convertToPixels();
|
||||
double termCenterX = 2.5 * spacing;
|
||||
double termY = -boardH + 24.0;
|
||||
|
||||
double[][] relativeOffsets = new double[][] {
|
||||
{ 0, 0 },
|
||||
{ spacing, 0 },
|
||||
{ spacing * 2, 0 },
|
||||
{ spacing * 3, 0 },
|
||||
{ spacing * 4, 0 },
|
||||
{ spacing * 5, 0 },
|
||||
{ termCenterX - termSpacing / 2.0, termY },
|
||||
{ termCenterX + termSpacing / 2.0, termY }
|
||||
};
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getBodyShape() {
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double boardX = x - (boardW - 5 * spacing) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 6, 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
double boardX = x - (boardW - 5 * spacing) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
|
||||
Shape boardShape = getBodyShape();
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
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) {
|
||||
// 4 Large Gold Ring Mounting Holes in corners (20mm x 20mm pitch)
|
||||
double holeMargin = 10.0;
|
||||
drawMountingHole(g2d, boardX + holeMargin, boardY + holeMargin, 13.0);
|
||||
drawMountingHole(g2d, boardX + boardW - holeMargin, boardY + holeMargin, 13.0);
|
||||
drawMountingHole(g2d, boardX + holeMargin, boardY + boardH - holeMargin, 13.0);
|
||||
drawMountingHole(g2d, boardX + boardW - holeMargin, boardY + boardH - holeMargin, 13.0);
|
||||
|
||||
// Top Blue Screw Terminal Block (5.08mm 2-pin block)
|
||||
double termW = 44.0;
|
||||
double termH = 30.0;
|
||||
double termX = boardX + (boardW - termW) / 2.0;
|
||||
double termY = boardY + 2.0;
|
||||
|
||||
// Blue outer block body
|
||||
g2d.setColor(TERMINAL_BLUE);
|
||||
g2d.fill(new RoundRectangle2D.Double(termX, termY, termW, termH, 3, 3));
|
||||
g2d.setColor(TERMINAL_BLUE_BORDER);
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new RoundRectangle2D.Double(termX, termY, termW, termH, 3, 3));
|
||||
|
||||
// Terminal block wire entry slots on top
|
||||
g2d.setColor(Color.decode("#0D47A1"));
|
||||
g2d.fillRect((int) (termX + 4), (int) (termY + 1), (int) (termW - 8), 4);
|
||||
|
||||
// Screw contacts
|
||||
double termSpacing = new Size(5.08d, SizeUnit.mm).convertToPixels();
|
||||
double screw1X = termX + termW / 2.0 - termSpacing / 2.0;
|
||||
double screw2X = termX + termW / 2.0 + termSpacing / 2.0;
|
||||
double screwY = termY + termH / 2.0 + 2.0;
|
||||
double screwD = 14.0;
|
||||
|
||||
g2d.setColor(SCREW_CIRCLE_COLOR);
|
||||
g2d.fill(new Ellipse2D.Double(screw1X - screwD / 2.0, screwY - screwD / 2.0, screwD, screwD));
|
||||
g2d.fill(new Ellipse2D.Double(screw2X - screwD / 2.0, screwY - screwD / 2.0, screwD, screwD));
|
||||
g2d.setColor(SCREW_CIRCLE_COLOR.darker());
|
||||
g2d.draw(new Ellipse2D.Double(screw1X - screwD / 2.0, screwY - screwD / 2.0, screwD, screwD));
|
||||
g2d.draw(new Ellipse2D.Double(screw2X - screwD / 2.0, screwY - screwD / 2.0, screwD, screwD));
|
||||
// Screw diagonal slots
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1.5f));
|
||||
g2d.drawLine((int) (screw1X - 4), (int) (screwY - 4), (int) (screw1X + 4), (int) (screwY + 4));
|
||||
g2d.drawLine((int) (screw2X - 4), (int) (screwY - 4), (int) (screw2X + 4), (int) (screwY + 4));
|
||||
|
||||
// Shunt Resistor (Center-Upper, directly below terminal block)
|
||||
double shuntW = 32.0;
|
||||
double shuntH = 14.0;
|
||||
double shuntX = boardX + (boardW - shuntW) / 2.0;
|
||||
double shuntY = boardY + termH + 6.0;
|
||||
|
||||
// Copper outer tabs
|
||||
g2d.setColor(Color.decode("#C0392B"));
|
||||
g2d.fill(new RoundRectangle2D.Double(shuntX, shuntY, 7, shuntH, 2, 2));
|
||||
g2d.fill(new RoundRectangle2D.Double(shuntX + shuntW - 7, shuntY, 7, shuntH, 2, 2));
|
||||
|
||||
// Metal sense element (arched center)
|
||||
g2d.setColor(Color.decode("#BDC3C7"));
|
||||
g2d.fill(new Rectangle2D.Double(shuntX + 6, shuntY + 1, shuntW - 12, shuntH - 2));
|
||||
g2d.setColor(Color.decode("#7F8C8D"));
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new Rectangle2D.Double(shuntX + 6, shuntY + 1, shuntW - 12, shuntH - 2));
|
||||
|
||||
// "R100" label on shunt
|
||||
g2d.setColor(Color.decode("#2C3E50"));
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 7));
|
||||
StringUtils.drawCenteredText(g2d, "R100", shuntX + shuntW / 2.0, shuntY + shuntH / 2.0,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// INA219 SOIC-8 IC (Center-Lower, directly below shunt)
|
||||
double icW = 20.0;
|
||||
double icH = 14.0;
|
||||
double icX = boardX + (boardW - icW) / 2.0;
|
||||
double icY = shuntY + shuntH + 4.0;
|
||||
|
||||
// IC body
|
||||
g2d.setColor(Color.decode("#1C2833"));
|
||||
g2d.fill(new RoundRectangle2D.Double(icX, icY, icW, icH, 2, 2));
|
||||
g2d.setColor(Color.decode("#111111"));
|
||||
g2d.draw(new RoundRectangle2D.Double(icX, icY, icW, icH, 2, 2));
|
||||
|
||||
// 8 silver IC pins (4 top, 4 bottom)
|
||||
g2d.setColor(Color.decode("#BDC3C7"));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double px = icX + 2.5 + i * 4.5;
|
||||
g2d.fillRect((int) px, (int) (icY - 2), 2, 2);
|
||||
g2d.fillRect((int) px, (int) (icY + icH), 2, 2);
|
||||
}
|
||||
// Pin 1 dot
|
||||
g2d.setColor(Color.decode("#F4D03F"));
|
||||
g2d.fill(new Ellipse2D.Double(icX + 2.5, icY + 2.5, 2.5, 2.5));
|
||||
|
||||
// Vertical silkscreen text on left edge
|
||||
AffineTransform preTextTx = g2d.getTransform();
|
||||
g2d.translate(boardX + 14, boardY + boardH / 2.0);
|
||||
g2d.rotate(-Math.PI / 2.0);
|
||||
g2d.setColor(SILK_YELLOW);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 7));
|
||||
StringUtils.drawCenteredText(g2d, "INA219 SENSOR", 0, 0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
g2d.setTransform(preTextTx);
|
||||
|
||||
// Vertical silkscreen labels above bottom 6 header pins (VCC, GND, SCL, SDA, VIN-, VIN+)
|
||||
String[] headerLabels = new String[] {"VCC", "GND", "SCL", "SDA", "VIN-", "VIN+"};
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 7));
|
||||
g2d.setColor(SILK_YELLOW);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
double pinX = x + i * spacing;
|
||||
AffineTransform pTx = g2d.getTransform();
|
||||
g2d.translate(pinX, y - 11);
|
||||
g2d.rotate(-Math.PI / 2.0);
|
||||
StringUtils.drawCenteredText(g2d, headerLabels[i], 0, 0, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
|
||||
g2d.setTransform(pTx);
|
||||
}
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
|
||||
// Draw bottom 6 header pins
|
||||
drawPins(g2d, 0, 6, false, outlineMode, drawingObserver);
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
// Green square PCB
|
||||
g2d.setColor(PCB_GREEN);
|
||||
g2d.fill(new RoundRectangle2D.Double(2, 2, width - 4, height - 4, 3, 3));
|
||||
g2d.setColor(PCB_GREEN.darker());
|
||||
g2d.draw(new RoundRectangle2D.Double(2, 2, width - 4, height - 4, 3, 3));
|
||||
|
||||
// Blue terminal block at top
|
||||
g2d.setColor(TERMINAL_BLUE);
|
||||
g2d.fillRoundRect(width / 2 - 8, 3, 16, 8, 2, 2);
|
||||
|
||||
// Shunt in middle
|
||||
g2d.setColor(Color.decode("#C0392B"));
|
||||
g2d.fillRect(width / 2 - 6, 13, 12, 4);
|
||||
g2d.setColor(Color.decode("#BDC3C7"));
|
||||
g2d.fillRect(width / 2 - 3, 13, 6, 4);
|
||||
|
||||
// Small IC below shunt
|
||||
g2d.setColor(Color.decode("#1C2833"));
|
||||
g2d.fillRect(width / 2 - 4, 19, 8, 5);
|
||||
|
||||
// 6 pin dots at bottom
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
g2d.fillRect(4 + i * 4, height - 5, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
*
|
||||
* 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.components.sensors;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@ComponentDescriptor(name = "IR Receiver Module (KY-022)", category = "Sensors",
|
||||
author = "Branislav Stojkovic", description = "38kHz Infrared Receiver Module (VS1838B) for remote control",
|
||||
instanceNamePrefix = "SEN", zOrder = IDIYComponent.COMPONENT,
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
|
||||
enableCache = true)
|
||||
public class IRReceiverModule extends AbstractMakerBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Color PCB_GREEN = Color.decode("#1A6B1A");
|
||||
public static Color SENSOR_METAL = Color.decode("#85929E");
|
||||
public static Color SENSOR_DOME = Color.decode("#1C2833");
|
||||
|
||||
public static Size BOARD_WIDTH = new Size(18.5d, SizeUnit.mm);
|
||||
public static Size BOARD_HEIGHT = new Size(15.0d, SizeUnit.mm);
|
||||
|
||||
// Standard Keyes KY-022 3-pin layout from left to right: - (GND), + (VCC), S (Signal)
|
||||
private static final String[] PIN_NAMES = {"- (GND)", "+ (VCC)", "S (Signal)"};
|
||||
|
||||
public IRReceiverModule() {
|
||||
super();
|
||||
this.bodyColor = PCB_GREEN;
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
if (index >= 0 && index < PIN_NAMES.length) {
|
||||
return PIN_NAMES[index];
|
||||
}
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateControlPoints() {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
|
||||
// 3 pins along the bottom edge: - (GND), + (VCC), S (Signal)
|
||||
double[][] relativeOffsets = new double[][] {
|
||||
{ 0, 0 },
|
||||
{ spacing, 0 },
|
||||
{ spacing * 2, 0 }
|
||||
};
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getBodyShape() {
|
||||
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 - (boardW - 2 * PIN_SPACING.convertToPixels()) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
return new RoundRectangle2D.Double(boardX, boardY, boardW, boardH, 4, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
if (checkPointsClipped(g2d.getClip())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point2D p0 = controlPoints[0];
|
||||
double x = p0.getX();
|
||||
double y = p0.getY();
|
||||
|
||||
AffineTransform oldTx = g2d.getTransform();
|
||||
if (orientation != Orientation.DEFAULT) {
|
||||
g2d.rotate(orientation.toRadians(), x, y);
|
||||
}
|
||||
|
||||
double boardW = BOARD_WIDTH.convertToPixels();
|
||||
double boardH = BOARD_HEIGHT.convertToPixels();
|
||||
double boardX = x - (boardW - 2 * PIN_SPACING.convertToPixels()) / 2.0;
|
||||
double boardY = y - boardH + 12;
|
||||
double spacing = PIN_SPACING.convertToPixels();
|
||||
|
||||
Shape boardShape = getBodyShape();
|
||||
|
||||
Composite oldComposite = applyAlpha(g2d, componentState);
|
||||
|
||||
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) {
|
||||
// VS1838B sensor element — metal-shielded rectangular body with central black IR dome
|
||||
double sensorW = new Size(7.5d, SizeUnit.mm).convertToPixels();
|
||||
double sensorH = new Size(6.0d, SizeUnit.mm).convertToPixels();
|
||||
double sensorX = boardX + (boardW - sensorW) / 2.0;
|
||||
double sensorY = boardY + 6;
|
||||
|
||||
// Metal shielded box
|
||||
g2d.setColor(SENSOR_METAL);
|
||||
g2d.fill(new RoundRectangle2D.Double(sensorX, sensorY, sensorW, sensorH, 3, 3));
|
||||
g2d.setColor(SENSOR_METAL.darker());
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
g2d.draw(new RoundRectangle2D.Double(sensorX, sensorY, sensorW, sensorH, 3, 3));
|
||||
|
||||
// Central dark IR lens dome
|
||||
double domeD = sensorH * 0.75;
|
||||
g2d.setColor(SENSOR_DOME);
|
||||
g2d.fill(new Ellipse2D.Double(sensorX + (sensorW - domeD) / 2.0, sensorY + (sensorH - domeD) / 2.0, domeD, domeD));
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.draw(new Ellipse2D.Double(sensorX + (sensorW - domeD) / 2.0, sensorY + (sensorH - domeD) / 2.0, domeD, domeD));
|
||||
|
||||
// Red status LED (top right)
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.fill(new Ellipse2D.Double(boardX + boardW - 12, boardY + 6, 6, 6));
|
||||
|
||||
// "KY-022" silkscreen text in the open space between the sensor and pin labels
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 9));
|
||||
StringUtils.drawCenteredText(g2d, "KY-022", boardX + boardW / 2.0, boardY + 36, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// Silkscreen pin labels (-, +, S) directly above the 3 header pins
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(new Font("SansSerif", Font.BOLD, 9));
|
||||
double labelY = y - 9;
|
||||
StringUtils.drawCenteredText(g2d, "-", x, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "+", x + spacing, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
StringUtils.drawCenteredText(g2d, "S", x + spacing * 2, labelY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
drawPins(g2d, 0, controlPoints.length, false, outlineMode, drawingObserver);
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
// Green PCB
|
||||
g2d.setColor(PCB_GREEN);
|
||||
g2d.fill(new RoundRectangle2D.Double(3, 5, width - 6, height - 10, 3, 3));
|
||||
g2d.setColor(PCB_GREEN.darker());
|
||||
g2d.draw(new RoundRectangle2D.Double(3, 5, width - 6, height - 10, 3, 3));
|
||||
|
||||
// Metal shield + dome
|
||||
g2d.setColor(SENSOR_METAL);
|
||||
g2d.fillRoundRect(width / 2 - 5, 6, 10, 8, 2, 2);
|
||||
g2d.setColor(SENSOR_DOME);
|
||||
g2d.fillOval(width / 2 - 3, 7, 6, 6);
|
||||
|
||||
// Red LED
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.fillOval(width - 7, 7, 4, 4);
|
||||
|
||||
// 3 pin dots
|
||||
g2d.setColor(PIN_COLOR);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
g2d.fillRect(6 + i * 4, height - 5, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.DefaultComponentTransformer;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.displays.*;
|
||||
import org.diylc.components.electromechanical.*;
|
||||
import org.diylc.components.micro.*;
|
||||
import org.diylc.components.modules.*;
|
||||
import org.diylc.components.robotics.*;
|
||||
@@ -78,6 +79,7 @@ public class MakerComponentsTest {
|
||||
BME280Sensor.class,
|
||||
SoilMoistureSensor.class,
|
||||
TCRT5000Sensor.class,
|
||||
IRReceiverModule.class,
|
||||
|
||||
// Displays & Outputs
|
||||
CharacterLCD.class,
|
||||
@@ -86,6 +88,7 @@ public class MakerComponentsTest {
|
||||
LEDMatrix8x8.class,
|
||||
TFTDisplay.class,
|
||||
WS2812BStick.class,
|
||||
WS2812BRing.class,
|
||||
|
||||
// Modules & Breakouts
|
||||
LogicLevelConverter.class,
|
||||
@@ -106,6 +109,12 @@ public class MakerComponentsTest {
|
||||
ULN2003Driver.class,
|
||||
PCA9685ServoDriver.class,
|
||||
MOSFETSwitchModule.class,
|
||||
ActiveBuzzerModule.class,
|
||||
GPSModuleNEO6M.class,
|
||||
INA219CurrentSensor.class,
|
||||
|
||||
// Electro-Mechanical
|
||||
BatteryHolder18650.class,
|
||||
|
||||
// Robotics
|
||||
MicroServoSG90.class,
|
||||
|
||||
Reference in New Issue
Block a user