Added 'Auto-Unit Conversion' configuration which automatically converts between metric and imperial units based on the ruler unit preference

This commit is contained in:
Bane Stojkovic
2026-02-17 20:44:50 +01:00
parent c4efe7d20f
commit 508d1db513
19 changed files with 376 additions and 46 deletions
@@ -88,6 +88,8 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String CACHING_ENABLED_KEY = "cachingEnabled";
public static final String FONT_SCALE_FACTOR_KEY = "fontScaleFactor";
public static final String SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY = "showResizeDimensionsTooltip";
public static final String AUTO_UNIT_CONVERSION_KEY = "autoUnitConversion";
public static final String RULER_IN_SUBDIVISION_2 = "base of 2";
public static final String RULER_IN_SUBDIVISION_10 = "base of 10";
@@ -24,8 +24,11 @@ package org.diylc.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.IPropertyValidator;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
/**
* Entity class for editable properties extracted from component objects. Represents a single
@@ -65,7 +68,29 @@ public class PropertyWrapper implements Cloneable {
public void readFrom(Object object) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException, SecurityException, NoSuchMethodException {
this.ownerObject = object;
this.value = getGetter().invoke(object);
Object value = getGetter().invoke(object);
value = convertUnitsIfNeeded(value);
this.value = value;
}
private static Object convertUnitsIfNeeded(Object value) {
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.AUTO_UNIT_CONVERSION_KEY, true)
&& value instanceof Size sizeValue) {
SizeUnit preferredUnit;
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.METRIC_KEY, true)) {
preferredUnit = SizeUnit.mm;
}
else {
preferredUnit = SizeUnit.in;
}
if (preferredUnit.getType() != sizeValue.getUnit().getType()) {
SizeUnit targetUnit = SizeUnit.ConversionMapping.get(sizeValue.getUnit());
if (targetUnit != null) {
value = sizeValue.convertToUnits(targetUnit);
}
}
}
return value;
}
// public void readUniqueFrom(IDIYComponent component)
@@ -46,11 +46,21 @@ public class Size extends AbstractMeasure<SizeUnit> implements Comparable<Size>
return getValue() * getUnit().getFactor() / SizeUnit.in.getFactor() * Constants.PIXELS_PER_INCH;
}
public double convertToUnits(SizeUnit unit) {
/**
* Converts this size to the given unit. Returns a new Size with the same
* physical length expressed in the target unit.
*/
public Size convertToUnits(SizeUnit unit) {
if (unit == getUnit()) {
return this.getValue();
return new Size(roundTo4Decimals(getValue()), unit);
}
return convertToPixels() * SizeUnit.px.getFactor();
double lengthInBase = getValue() * getUnit().getFactor();
double valueInTargetUnit = lengthInBase / unit.getFactor();
return new Size(roundTo4Decimals(valueInTargetUnit), unit);
}
private static double roundTo4Decimals(double value) {
return Math.round(value * 10000) / 10000.0;
}
@Override
@@ -23,18 +23,35 @@ package org.diylc.core.measures;
import org.diylc.utils.Constants;
import java.util.Map;
public enum SizeUnit implements Unit {
px(25.4d / Constants.PIXELS_PER_INCH), mm(1d), cm(10d), m(1e3d), in(25.4d), ft(25.4d * 12), yd(9144d);
px(25.4d / Constants.PIXELS_PER_INCH, SizeUnitType.Digital),
mm(1d, SizeUnitType.Metric),
cm(10d, SizeUnitType.Metric),
m(1e3d, SizeUnitType.Metric),
in(25.4d, SizeUnitType.Imperial),
ft(25.4d * 12, SizeUnitType.Imperial),
yd(9144d, SizeUnitType.Imperial);
double factor;
SizeUnitType type;
private SizeUnit(double factor) {
private SizeUnit(double factor, SizeUnitType type) {
this.factor = factor;
this.type = type;
}
@Override
public double getFactor() {
return factor;
}
public SizeUnitType getType() {
return type;
}
public static final Map<SizeUnit, SizeUnit> ConversionMapping =
Map.of(mm, in, cm, in, m, yd, in, mm, ft, cm, yd, m);
}
@@ -0,0 +1,5 @@
package org.diylc.core.measures;
public enum SizeUnitType {
Metric, Imperial, Digital
}
+16 -40
View File
@@ -1,45 +1,5 @@
<java.util.Arrays_-ArrayList>
<a class="org.diylc.appframework.update.Version-array">
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>4</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>'AI Analyzer' feature added which uses AI to scan the circuit diagram and report issues and suggestions</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Significantly improve rendering performance of most components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'Open Jack 1/4"' and 'Potentiometer Panel' now tolerate connections anywhere on the lugs instead of dead center only</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased maximum number of switching combinations to 256 for circuit analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Pilot Lamp Holder' component can sometimes hang the application</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with embedding image files into DIY projects</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with some components (e.g. DIP IC) jumping when showing underside of a board</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
@@ -388,5 +348,21 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>12</minor>
<build>0</build>
</versionNumber>
<releaseDate>2026-02-15 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Added 'Auto-Unit Conversion' configuration which automatically converts between metric and imperial units based on the ruler unit preference</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
</a>
</java.util.Arrays_-ArrayList>
@@ -5017,5 +5017,45 @@
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>5</major>
<minor>4</minor>
<build>0</build>
</versionNumber>
<releaseDate>2025-05-13 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>'AI Analyzer' feature added which uses AI to scan the circuit diagram and report issues and suggestions</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Significantly improve rendering performance of most components</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'Open Jack 1/4"' and 'Potentiometer Panel' now tolerate connections anywhere on the lugs instead of dead center only</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Increased maximum number of switching combinations to 256 for circuit analysis</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>'Pilot Lamp Holder' component can sometimes hang the application</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with embedding image files into DIY projects</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Fixed the issue with some components (e.g. DIP IC) jumping when showing underside of a board</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
</a>
</java.util.Arrays_-ArrayList>
@@ -0,0 +1,242 @@
/*
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.core.measures;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class SizeTest {
private static final double DELTA = 1e-9;
// --- convertToUnits: same unit ---
@Test
public void convertToUnits_sameUnit_returnsNewSizeWithValueRoundedTo4Decimals() {
Size size = new Size(1.23456789, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.mm);
assertNotSame(size, result);
assertEquals(SizeUnit.mm, result.getUnit());
assertEquals(1.2346, result.getValue(), DELTA);
}
@Test
public void convertToUnits_sameUnit_roundsTo4Decimals() {
assertEquals(2.3457, new Size(2.3456789, SizeUnit.cm).convertToUnits(SizeUnit.cm).getValue(), DELTA);
assertEquals(0.5, new Size(0.5, SizeUnit.in).convertToUnits(SizeUnit.in).getValue(), DELTA);
}
// --- convertToUnits: metric ↔ metric ---
@Test
public void convertToUnits_mmToCm() {
Size size = new Size(10.0, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.cm);
assertEquals(SizeUnit.cm, result.getUnit());
assertEquals(1.0, result.getValue(), DELTA);
}
@Test
public void convertToUnits_cmToMm() {
Size size = new Size(2.5, SizeUnit.cm);
Size result = size.convertToUnits(SizeUnit.mm);
assertEquals(SizeUnit.mm, result.getUnit());
assertEquals(25.0, result.getValue(), DELTA);
}
@Test
public void convertToUnits_mmToM() {
Size size = new Size(1500.0, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.m);
assertEquals(SizeUnit.m, result.getUnit());
assertEquals(1.5, result.getValue(), DELTA);
}
@Test
public void convertToUnits_mToMm() {
Size size = new Size(0.5, SizeUnit.m);
Size result = size.convertToUnits(SizeUnit.mm);
assertEquals(SizeUnit.mm, result.getUnit());
assertEquals(500.0, result.getValue(), DELTA);
}
// --- convertToUnits: imperial ↔ imperial ---
@Test
public void convertToUnits_inToFt() {
Size size = new Size(24.0, SizeUnit.in);
Size result = size.convertToUnits(SizeUnit.ft);
assertEquals(SizeUnit.ft, result.getUnit());
assertEquals(2.0, result.getValue(), DELTA);
}
@Test
public void convertToUnits_ftToIn() {
Size size = new Size(1.0, SizeUnit.ft);
Size result = size.convertToUnits(SizeUnit.in);
assertEquals(SizeUnit.in, result.getUnit());
assertEquals(12.0, result.getValue(), DELTA);
}
// --- convertToUnits: metric ↔ imperial ---
@Test
public void convertToUnits_mmToIn() {
Size size = new Size(25.4, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.in);
assertEquals(SizeUnit.in, result.getUnit());
assertEquals(1.0, result.getValue(), DELTA);
}
@Test
public void convertToUnits_inToMm() {
Size size = new Size(1.0, SizeUnit.in);
Size result = size.convertToUnits(SizeUnit.mm);
assertEquals(SizeUnit.mm, result.getUnit());
assertEquals(25.4, result.getValue(), DELTA);
}
@Test
public void convertToUnits_cmToIn() {
Size size = new Size(2.54, SizeUnit.cm);
Size result = size.convertToUnits(SizeUnit.in);
assertEquals(SizeUnit.in, result.getUnit());
assertEquals(1.0, result.getValue(), DELTA);
}
// --- convertToUnits: rounding to 4 decimal places ---
@Test
public void convertToUnits_resultIsRoundedTo4Decimals() {
Size size = new Size(1.0, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.in);
// 1 mm = 1/25.4 in ≈ 0.03937007874015748...
assertEquals(0.0394, result.getValue(), DELTA);
}
@Test
public void convertToUnits_roundingDown() {
Size size = new Size(1.23441, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.mm);
assertEquals(1.2344, result.getValue(), DELTA);
}
@Test
public void convertToUnits_roundingUp() {
Size size = new Size(1.23446, SizeUnit.mm);
Size result = size.convertToUnits(SizeUnit.mm);
assertEquals(1.2345, result.getValue(), DELTA);
}
// --- parseSize ---
@Test
public void parseSize_mm() {
Size size = Size.parseSize("10.5mm");
assertEquals(10.5, size.getValue(), DELTA);
assertEquals(SizeUnit.mm, size.getUnit());
}
@Test
public void parseSize_cm() {
Size size = Size.parseSize("2.54cm");
assertEquals(2.54, size.getValue(), DELTA);
assertEquals(SizeUnit.cm, size.getUnit());
}
@Test
public void parseSize_in() {
Size size = Size.parseSize("1in");
assertEquals(1.0, size.getValue(), DELTA);
assertEquals(SizeUnit.in, size.getUnit());
}
@Test
public void parseSize_caseInsensitive() {
Size size = Size.parseSize("5 MM");
assertEquals(5.0, size.getValue(), DELTA);
assertEquals(SizeUnit.mm, size.getUnit());
}
@Test(expected = IllegalArgumentException.class)
public void parseSize_unknownUnit_throws() {
Size.parseSize("10 xyz");
}
// --- fromPixels ---
@Test
public void fromPixels_roundTripWithConvertToPixels() {
Size original = new Size(1.0, SizeUnit.in);
double pixels = original.convertToPixels();
Size fromPixels = Size.fromPixels(pixels, SizeUnit.in);
assertEquals(original.getValue(), fromPixels.getValue(), 0.01);
assertEquals(original.getUnit(), fromPixels.getUnit());
}
// --- scale ---
@Test
public void scale() {
Size size = new Size(10.0, SizeUnit.mm);
Size scaled = size.scale(2.0);
assertEquals(20.0, scaled.getValue(), DELTA);
assertEquals(SizeUnit.mm, scaled.getUnit());
}
// --- compareTo ---
@Test
public void compareTo_samePhysicalSize_returnsZero() {
Size a = new Size(25.4, SizeUnit.mm);
Size b = new Size(1.0, SizeUnit.in);
assertEquals(0, a.compareTo(b));
}
@Test
public void compareTo_smaller_returnsNegative() {
Size a = new Size(1.0, SizeUnit.mm);
Size b = new Size(1.0, SizeUnit.cm);
assertTrue(a.compareTo(b) < 0);
}
@Test
public void compareTo_larger_returnsPositive() {
Size a = new Size(10.0, SizeUnit.mm);
Size b = new Size(1.0, SizeUnit.mm);
assertTrue(a.compareTo(b) > 0);
}
// --- clone ---
@Test
public void clone_returnsEqualSize() throws CloneNotSupportedException {
Size size = new Size(3.14, SizeUnit.cm);
Size cloned = size.clone();
assertNotSame(size, cloned);
assertEquals(size.getValue(), cloned.getValue(), DELTA);
assertEquals(size.getUnit(), cloned.getUnit());
}
}
@@ -84,6 +84,9 @@ public class ConfigPlugin implements IPlugIn {
swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Auto-Create Pads",
SolderPadAutoCreator.AUTO_PADS_KEY, false), CONFIG_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Auto-Convert Units",
IPlugInPort.AUTO_UNIT_CONVERSION_KEY, true), CONFIG_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Auto-Edit Mode", IPlugInPort.AUTO_EDIT_KEY, true),
CONFIG_MENU);
@@ -24,6 +24,7 @@ Anti-Aliasing|Anti-Aliasing
Arrow Size|Pfeilgröße
Author|Verfasser
Auto-Create Pads|Automatische Erstellung von Lötpads
Auto-Convert Units|Automatische Umrechnung der Einheiten
Auto-Edit Mode|Automatischer Bearbeitungsmodus
Base|Basis
Below|Unter
@@ -22,6 +22,7 @@ Anti-Aliasing
Arrow Size
Author
Auto-Create Pads
Auto-Convert Units
Auto-Edit Mode
Base
Below
@@ -22,6 +22,7 @@ Anti-Aliasing|Suavizado
Arrow Size|Tamaño de Flecha
Author|Autor
Auto-Create Pads|Creación Automática de Pads
Auto-Convert Units|Conversión automática de unidades
Auto-Edit Mode|Modo de Edición Automática
Base|Base
Below|Debajo
@@ -22,6 +22,7 @@ Anti-Aliasing|Anti-aliasing
Arrow Size|Taille de la flèche
Author|Auteur
Auto-Create Pads|Création automatique des pads
Auto-Convert Units|Conversion automatique des unités
Auto-Edit Mode|Mode d'édition automatique
Base|Base
Below|Ci-dessous
@@ -24,6 +24,7 @@ Apply Variant|Applica variante
Arrow Size|Dimensione Freccia
Author|Autore
Auto-Create Pads|Creazione Automatica dei Pad
Auto-Convert Units|Conversione automatica delle unità
Auto-Edit Mode|Modalità di Editaggio Automatica
Base|Base
Below|Sotto
@@ -24,6 +24,7 @@ Anti-Aliasing|Anti-aliasing
Arrow Size|Pijlgrootte
Author|Ontwerper
Auto-Create Pads|Automatisch aanmaken van soldeereilandjes
Auto-Convert Units|Automatische eenheidsconversie
Auto-Edit Mode|Automatisch bewerken modus
Base|Basis
Below|Onder
@@ -22,6 +22,7 @@ Anti-Aliasing|Suavizado
Arrow Size|Tamanho da Flecha
Author|Autor
Auto-Create Pads|Criação Automática de Pads
Auto-Convert Units|Conversão automática de unidades
Auto-Edit Mode|Modo de Edição Automática
Base|Base
Below|Abaixo
@@ -22,6 +22,7 @@ Anti-Aliasing
Arrow Size|Veličina Strelice
Author|Autor
Auto-Create Pads|Auto-Kreiranje Pad
Auto-Convert Units|Automatska konverzija jedinica
Auto-Edit Mode|Automatsko Editovanje
Base|Osnova
Below|Ispod
@@ -22,6 +22,7 @@ Anti-Aliasing|एंटी-अलियासिंग
Arrow Size|तीर का आकार
Author|लेखक
Auto-Create Pads|ऑटो-क्रिएट पैड
Auto-Convert Units|स्वचालित इकाई रूपांतरण
Auto-Edit Mode|ऑटो-एडिट मोड
Base|आधार
Below|नीचे
@@ -22,6 +22,7 @@ Anti-Aliasing|抗锯齿
Arrow Size|箭头大小
Author|作者
Auto-Create Pads|自动创建焊盘
Auto-Convert Units|自动转换单位
Auto-Edit Mode|自动编辑模式
Base|基座
Below|下方